pwnfreehard

Arms Roped

hackthebox

Task: ARM 32-bit binary exploitation under QEMU user-mode with PIE, Canary, and NX enabled. Solution: Multi-stage ROP chain — leak canary via partial overwrite, leak PIE base from saved LR, use ARM ret2csu to leak libc via GOT, then ret2libc with system("/bin/sh").

$ ls tags/ techniques/
canary_leak_partial_overwritepie_leak_saved_lrarm_ret2csulibc_leak_via_got_putsret2libc_system_binshmulti_stage_ropbyte_by_byte_leak

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

Arms Roped — HackTheBox

Description

ARM binary exploitation challenge running under QEMU user-mode emulation with a custom ASLR patch.

ARM 32-bit ELF binary (little-endian, EABI5), dynamically linked, not stripped. Runs via qemu-arm with a custom ASLR patch. Contains a string_storer() function that reads strings in a loop, copies them to a stack buffer, and outputs them back via puts().

Remote target: 154.57.164.72:31410.

Files: arms_roped (ARM ELF binary), libc.so.6 (ARM 32-bit libc), Dockerfile (socat + qemu-arm), patch.diff (QEMU ASLR patch), build_docker.sh.

Protections

ProtectionStatus
PIEEnabled
NXEnabled
CanaryEnabled
RELROPartial

Infrastructure

socat tcp-l:1337,reuseaddr,fork EXEC:./qemu_arm -L /usr/arm-linux-gnueabihf/ ./arms_roped

QEMU user-mode typically doesn't support ASLR. The organizers added a custom patch (patch.diff) that randomizes addresses via srand(time(NULL)) — a weak entropy source, but this doesn't matter for exploitation since we obtain address leaks directly.

Analysis

Key Functions

main() (0x8dc)

Sets up stdout/stderr buffering via setvbuf, then calls string_storer().

string_storer() (0x790) — Vulnerable Function

Loop:

  1. scanf("%m[^\n]%n", &tmp, &n)%m allocates a heap buffer for input, %n writes the number of bytes read to a global variable n
  2. memcpy(stack_buf, tmp, n) — copies n bytes to a 32-byte stack buffer without bounds checking
  3. free(tmp) — frees the heap buffer
  4. memcmp(stack_buf, "quit", 4) — if it starts with "quit", exit the loop
  5. puts(stack_buf) — otherwise outputs the buffer contents
  6. Return to step 1

Stack Layout of string_storer()

fp-0x30: buffer[32]     ← memcpy destination (32 bytes)
fp-0x10: stack canary   ← 4 bytes
fp-0x0c: padding        ← 4 bytes
fp-0x08: saved r4       ← 4 bytes
fp-0x04: saved fp       ← 4 bytes
fp+0x00: saved lr       ← 4 bytes (return address)

...

$ grep --similar

Similar writeups