BreizhCTF 2024 - New World
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