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 * |