
0x00 前言
依然是
CTF-WIKI上关于Unlink的一道例题。题目下载链接:下载链接
参考链接:
0x01 分析
1 | Arch: amd64-64-little |
程序提供了5个选项。
New note,创建note,每个note的size和chunk都会存在bss段的对应位置。Show note能输出note的内容。Edit note提供了overwrite和append两种方式。Delete note能执行正常的free。Exit退出程序
漏洞点1
- 漏洞点1: 在
New note中,我们的size采用的是unsigned int类型,但是在我们的读入函数MyRead((__int64)ptr, size, 10);
1 | unsigned __int64 __fastcall MyRead(__int64 a1, __int64 a2, char a3) |
- 也就是在for循环的终止条件中,i 是与
a2(也就是我们的size)-1进行比较。 假设我们的size设为0,由于size是unsigned int类型,那么size-1将是4294967295这么大,我们就可以输入非常多的内容从而造成堆溢出了。 - 由于glibc的分配内存机制,当我们的
size = 0时,实际会分配给我们0x20的内存块。
漏洞点2
- 漏洞点2: 在
Edit note中,程序只free掉了v8,并没有置NULL。
思路
本文利用的是漏洞点1, 貌似也可以利用漏洞点2?还请大佬们指教。
创建三个chunk,大小分别为
0x80,0x0,0x80,在chunk0中伪造chunk,通过chunk1的漏洞,修改掉chunk2的prev_size和size里的prev_inuse标志位,free掉chunk2,触发unlink,想办法泄露出某个函数的地址,然后推算出libcbase,从而得到system地址,将atoi覆盖成system,执行system('/bin/sh)
0x02 Let’s do it!
前期模板
1 | from pwn import * |
创建note,并伪造chunk
CTF-WIKI中,伪造chunk里的内容有点绕,不太好理解。- 本文的伪造chunk和
CTF-WIKI中的不一样,相对来说更好理解一些吧,但是对大佬来说估计都一样..
1 | head = 0x602120 |
- 对于伪造chunk的
size为什么是0xa1?

当它的
size正好等于0xa1的时候,这个伪造chunk正好包含住chunk1且紧挨着chunk2。当我们
free掉chunk2,程序会根据chunk2的presize来获得指向前一块chunk的指针。因此下一步就要想办法修改
chunk2的presize和inuse标志位。
修改chunk2的presize和inuse标志位 并执行unlink
1 | delete(1) |
当我们
delete(1)的时候,由于该chunk属于fastbin,所以下次在申请的时候仍然会申请到该chunk,然后通过漏洞覆盖掉chunk2的prevsize和inuse位随后当我们
delete(2)
1 | p是指向free掉的chunk2的指针 |
- 那么伪造chunk就会执行unlink,效果就是让前面的
*(head) = head - 0x18
打印出atoi的地址,并计算libcbase
1 | atoi_got = elf.got['atoi'] |
将atoi变成system,并getshell
1 | edit(0,1,p64(system)) |
0x03 完整exp
1 | from pwn import * |

