第一步 checksec ,本题为32位
分析一下保护机制:
- Arch: i386-32-little
- 程序是为32位的i386架构(即Intel 80386及其兼容处理器)编译的,并且使用的是小端字节序(little-endian)。
- RELRO: Partial RELRO
- RELRO(Relocation Read-Only)是一种安全特性,用于减少对全局偏移表(GOT)的攻击。它分为两种:Partial RELRO和Full RELRO。
- Partial RELRO意味着程序的某些部分(如.dynamic段)是只读的,但并不是全部。意味着攻击者可能仍然能够修改GOT中的某些条目,但难度比没有RELRO时要大。
- Stack: Canary found
- 栈保护是一种防止栈溢出攻击的机制。Canary(金丝雀)是一种特殊的值,它在函数返回之前被放置在栈帧的局部变量和返回地址之间。如果发生栈溢出,Canary值会被覆盖,程序检测到Canary值改变后,会立即终止,从而防止攻击者通过覆盖返回地址来执行任意代码。
- NX: NX enabled
- NX(No eXecute)是一种硬件特性,用于将内存区域标记为不可执行。这意味着即使攻击者能够将恶意代码注入到程序的内存中,也无法执行这些代码,因为标记为NX的内存区域不允许执行指令。
- PIE: No PIE (0x8048000)
- PIE(Position Independent Executable)是一种安全特性,它使得程序的代码可以在加载时随机化其内存地址,从而增加攻击者预测内存地址的难度。
- No PIE意味着程序没有使用PIE,其代码段加载地址是固定的(0x8048000)。这增加了攻击者利用固定地址进行攻击的可能性。
第二步 进入主函数
重点分析一下16-32行代码:
fd = open("/dev/urandom", 0);
- 这行代码打开了一个特殊的设备文件/dev/urandom,该文件提供了高质量的随机数。open函数的第二个参数是模式,通常应该使用O_RDONLY来表示只读模式,但这里直接使用了数字0,这在大多数系统上等同于O_RDONLY。
read(fd, &dword_804C044, 4u);
- 从/dev/urandom读取4个字节的数据到变量dword_804C044中。这个变量似乎是一个全局变量,其地址是硬编码的(例如,在ELF文件中,地址可能是一个固定的地址)。
printf("your name:");
- 输出提示信息,询问用户的名字。
read(0, buf, 0x63u);
- 从标准输入(文件描述符0)读取最多99个字节(0x63是99的十六进制表示)到缓冲区buf中。这里没有检查read的返回值,也没有检查缓冲区溢出的可能性,这是一个潜在的安全风险。
printf("Hello,");
- 输出问候语。
printf(buf);
- 输出用户输入的名字。由于之前没有对buf进行任何处理,这里存在一个格式化字符串漏洞!!!,攻击者可以构造输入来利用这个漏洞。
printf("your passwd:");
- 输出提示信息,询问用户的密码。
read(0, nptr, 0xFu);
- 从标准输入读取最多15个字节(0xF是15的十六进制表示)到缓冲区nptr中。这里同样没有检查read的返回值,也没有检查缓冲区溢出的可能性。
if ( atoi(nptr) == dword_804C044 )
- 使用atoi函数将nptr指向的字符串转换成整数,并与之前从/dev/urandom读取的随机数进行比较。(因此我们构建攻击载荷的地址即为dword_804C044的地址=0x804C044)
{
puts("ok!!");
system("/bin/sh");
}
- 如果输入的整数与随机数相等,则输出"ok!!"并执行/bin/sh,给予攻击者一个shell。
第三步 因为该题存在格式化字符串漏洞,所以我们使用输入AAA来确定偏移量的方法(如图所示,该偏移量位10)
地址有了,偏移量有了,开始编写脚本
第四步
详细分析一下:
from pwn import*
- 这行代码导入了pwntools库中的所有模块。pwntools是一个强大的工具集,它提供了很多用于二进制分析和利用的功能。
io.recvuntil(':')
- recvuntil函数用于从远程服务器接收数据,直到指定的字符串(在这个例子中是':')被接收到为止。这通常用于同步,确保脚本在正确的时刻发送或接收数据。
payload = p32(0x804C044) + b'%10$n'
- 这里构造了一个payload。p32函数将32位整数转换为其小端序的字符串表示,这里转换的是地址0x804C044,这个地址应该是之前提到的全局变量dword_804C044的地址。
- b'%10$n'是一个格式化字符串,它告诉printf函数将第10个参数的值写入到指针指向的地址。在格式化字符串漏洞利用中,这通常用于写入任意值到任意地址。
- 在这个上下文中,%10$n表示写入的值将是printf的第10个参数,但由于p32(0x804C044)没有在printf调用中作为参数,它实际上利用了格式化字符串漏洞,将栈上的某个值写入到地址0x804C044。
io.sendline(payload)
- 将构造的payload发送到远程服务器。由于之前调用了recvuntil(':'),这个payload将作为用户的名字发送。
io.recvuntil(':')
- 再次接收数据,直到遇到':',这次是为了同步到密码提示。
io.sendline('4')
- 发送字符串'4'作为密码。这是因为在格式化字符串漏洞利用中,我们已经将dword_804C044的值覆盖为了4(或者至少我们希望是这样),所以输入'4'应该会使atoi(nptr) == dword_804C044的条件成立。
io.interactive()
- 这行代码将脚本切换到交互模式,允许用户与远程服务器进行交互。如果之前的利用成功,这将提供一个shell。
运行成功