ARMs Race
Just wanted to make a quick writeup for this challenge because easy challenges structured in this way are pretty fun to me. Although not too challenging, it was fun to solve. This challenge also wraps up the “Easy” challenges in the active reversing category for HTB.
The challenges name is “ARMs Race”, which gives us a strong hint that the architecture is ARM. There are no files to download, just an instance to spawn and connect to.
Connecting to the instance the end user is greeted with:
Level 1/50: 370301e3d11c07e35e1c4ce3df2d04e3922345e3000060e2291707e3f0134ae3422800e3542347e3900100e0900200e0221f0ae3491041e3912609e35c284fe3900100e0900200e08d1200e3a91c47e3182e00e3d32c47e3010000e0020000e069110ce3b21444e3c22506e34b2f4ee3010080e1020080e1b91d0de3c41540e37a2606e34b284ae3010000e0020000e0f21f0de3f8194ce329290fe3ad2a49e3010080e00200a0e0611408e3c51a4ce38c2403e3ee2d41e3010080e00200a0e01910a0e3521e43e361280ce3002a42e3010000e0020000e0431c0be3821744e3fe2205e363214fe3010020e0020020e0861d0ee3d1184ae3f2220ce3bf2d44e3010040e00200c0e0171d08e3021348e3042303e3ae2e47e3000060e2db170fe31c1b4fe3f1220be33a244fe3010040e00200c0e09c130ae3c61a4ce3292300e3a92f4ee3010000e0020000e04d1206e3301440e3582402e320234de3010040e00200c0e025110fe3601047e3472c04e3ea274ee3010080e00200a0e0a31701e35e144ce34d2209e3802b42e3010040e00200c0e0041b04e38e1142e3922e06e3c92b44e3900100e0900200e0fa120ae3941744e3312205e3932543e3010080e00200a0e04a100ee326144be3c2270de33c2944e3010040e00200c0e0
Register r0:
If you are actively working on this challenge it will likely contain different bytes for you, but this is an example.. Anyways, the program is providing us the bytes that make up some assembly that we need to execute and find the result of $r0
.
This can be solved in a few different ways, the way I chose to do it was with Unicorn ARM emulation and pwntools tubes. The solve script is quite simple:
solve.py
#!/usr/bin/env python3
from pwn import *
from unicorn import *
from unicorn.arm_const import *
context.log_level="critical"
def run_emu(CODE):
addr = 0x100000
mu = Uc(UC_ARCH_ARM, CS_MODE_ARM)
mu.mem_map(addr, 2*1024*1024)
mu.mem_write(addr, CODE)
mu.emu_start(addr, addr+len(CODE))
r0 = mu.reg_read(UC_ARM_REG_R0)
print(">>> R0: 0x%x" % r0)
return r0
p = remote(ip, port)
while True:
try:
req = p.recvuntil(b"\n").rstrip(b"\n").split(b" ")
level, code = req[1], bytes.fromhex(req[2].decode())
print(level.rstrip(b":"))
reg = run_emu(code)
p.recvuntil(b": ")
p.sendline(hex(reg).encode("utf-8"))
except Exception as e:
print(f"[!] {e}")
print(req[0].decode())
break
We use pwntools to recursively capture the code and pass it to unicorn, unicorn then emulates the code snippet and returns the resulting register value $r0. Once we complete 50 rounds of this the flag is returned.
:D