BreizhCTF 2024 - New World

May 23, 2024

This year, BreizhCTF had a nice MISC challenge that asked us to emulate various shellcodes from multiple architectures. Altough the author hinted us to use Unicorn to do so, I choosed to use miasm. This challenge was really straightforward, and minimal code is required to get the flag.

Poking around with the server reveals that 4 different architectures are used in given shellcodes :

  • x86_64
  • x86
  • arm32
  • AArch64

All we need to do is to create an emulator using miasm’s jitter, and give it the proper Machine. The jitter will be abstracting the architecture for us:

archs = {
    b'x64': 'x86_64',
    b'x86': 'x86_32',
    b'ARM32': 'arml',
    b'AArch64': 'aarch64l'
}

Machine(archs.get(arch, None))
jit = machine.jitter(LocationDB())

Two memory areas are required when using the jitter:

  • an executable page that will be holding our shellcode ;
  • a valid page that our stack based registers can point to.

This can be achieved thanks to the vm.add_memory_page call, which as far as I know is the only part of miasm written in pure C, with python bindings on top of it.

Now execute the shellcode, rinse and repeat !

Here is the full code.

from miasm.analysis.machine import Machine
from miasm.core.locationdb import LocationDB
from miasm.jitter.csts import PAGE_EXEC, PAGE_READ, PAGE_WRITE
from pwn import *
context.log_level = 'debug' 

archs = {
    b'x64': 'x86_64',
    b'x86': 'x86_32',
    b'ARM32': 'arml',
    b'AArch64': 'aarch64l'
}

conn = remote('challenge.ctf.bzh',31129)

def round(conn):
    conn.recvuntil('registres ')
    regs = conn.recvuntil('apr\xc3\xa8s ex\xc3\xa9cution du code assembleur ').split(b'apr')[0].replace(b' ',b'').split(b',')
    arch = conn.recvuntil(' ')[:-1]
    conn.recvuntil(': ')
    payload = conn.recvline(keepends=False)
    b = b''.join([bytes([ord(b)]) for b in payload.decode('unicode_escape')])
    payload = b

    Machine(archs.get(arch, None))
    jit = machine.jitter(LocationDB())
    jit.stack_size = 0x1000
    jit.set_trace_log(trace_instr=False, trace_new_blocks=False, trace_regs=False)
    jit.init_stack()
    jit.vm.add_memory_page(0x1000, PAGE_READ | PAGE_WRITE | PAGE_EXEC, b'\x00' * 0x1000)
    jit.vm.add_memory_page(0x2000, PAGE_READ | PAGE_WRITE | PAGE_EXEC, payload + b'\x00' * (0x1000 - len(payload)))
    jit.vm.add_memory_page(0x3000, PAGE_READ | PAGE_WRITE | PAGE_EXEC, b'\x00' * 0x10000)
    jit.run_until(0x2000 + len(payload), trace=True)
    jit.run(0x2000)
    v = ''
    for i, reg in enumerate(regs):
        v += f'0x{(getattr(jit.cpu, reg.decode().upper())):02x}'
        if i+1 != len(regs):
            v += ','
    print(v)
    conn.writeline(f'{v}')
    return True

while(round(conn)):
    pass