Arms Roped
Arms Roped is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Arms Roped attack path
Arms Roped is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Objective
Challenge walkthrough focused on Pwn evidence, validation, and reusable operator lessons.
Walkthrough flow
ARM PIE binary has a stack overflow from...
The vulnerable function echoes non-exit input with...
Non-exit iterations can be used as a byte oracle to...
PIE base comes from the saved return address. Libc...
Final payload uses an ARM/Thumb-aware libc gadget to...
Source coverage
High source coverage
Status: complete. This article is generated from 4 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.
High confidence: the page is reconstructed from a primary walkthrough plus multiple supporting notes or evidence sources. Treat the chain as source-backed, while still checking the listed source files for sensitive values.
- Pwn/Arms-Roped/writeup.md
- htb-challenge/Pwn/Arms-Roped/notes.md
- htb-challenge/Pwn/Arms-Roped/memory-summary.md
- htb-challenge/Pwn/Arms-Roped/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: Arms-Roped
- Category: Pwn
- Difficulty: Medium
- Mode: hybrid
Summary
Arms-Roped is an ARM 32-bit PIE pwn challenge served through a patched QEMU user-mode runner. The binary has a stack overflow in string_storer: it reads a newline-delimited string with scanf("%m[^\n]%n"), copies the recorded length into a 32-byte stack buffer, then echoes the buffer with puts unless the first four bytes are quit.
The key observation is that the canary check happens only on the quit exit path. Non-quit lines can corrupt the current stack frame, leak bytes through puts(buffer), and then loop back for another line. The final payload restores the leaked canary before returning into a ROP chain.
Artifact Inventory
files/extracted/pwn_arms_roped/arms_roped: ARM 32-bit PIE executable.files/extracted/pwn_arms_roped/libc.so.6: matching ARM libc used by the Docker image.files/extracted/pwn_arms_roped/Dockerfile: QEMU+socat service wrapper.files/extracted/pwn_arms_roped/patch.diff: QEMU ASLR simulation patch.- Full inventory:
analysis/artifact-inventory.json.
Analysis
The direct disassembly in analysis/string_storer.disasm.txt shows:
- local buffer at
r11 - 0x30 - canary at
r11 - 0x10 - saved return address at offset
0x30from the buffer puts(buffer)on every non-quititeration- canary validation only after the
quitcomparison succeeds
The remote baseline in analysis/remote-baseline-transcript.txt confirmed that ordinary non-quit lines are echoed and quit exits cleanly.
The exploit model is documented in analysis/exploit-analysis.md:
- Leak canary bytes by overwriting through the prior byte and reading the next byte printed by
puts. - Leak the saved return address byte-by-byte, deriving the PIE base from the return site at
PIE + 0x948. - Use the ARM CSU sequence at
PIE + 0x9ecandPIE + 0x9ccto callputs(puts@GOT + i)byte-by-byte. - Normalize the leaked libc function pointer by clearing the Thumb/interworking bit before computing libc base.
- Use a Thumb
pop {r0, r1, pc}gadget from libc to callsystem("/bin/sh").
The RAG query was recorded but treated as generic only; it did not drive the solve. See analysis/rag-records.md.
Solve
Run the solver against the remote instance:
python3 solve/solve.py <TARGET> 31484 --output loot/flag-candidate.txtThe successful harness-wrapped execution is recorded in telemetry.jsonl. The solver is bounded, retries connections when newline-delimited payload bytes would break scanf, and writes the remote command output to loot/flag-candidate.txt.
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- A stack canary does not stop exploitation if the program loops after an overflow and exposes a pre-check output primitive.
- Byte-by-byte
putsleaks are more reliable than long string leaks when null bytes can truncate output. - ARM/Thumb interworking bits matter when computing libc bases and final branch targets.
- For newline-delimited binary payloads, retry when any required packed value contains
0x0a.
Source-Backed Dossier
The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.
Notes
Scope
- Challenge: Arms-Roped
- Category: Pwn
- Difficulty: Medium
- Mode: hybrid
- Remote instance: <TARGET>:31484
- Start time: 2026-06-13T10:06:22Z
- Operator: harness
- State file:
challenge-state.json
Harness Status
- Current phase: see
challenge-state.json - Next allowed actions: see
next-action.json - Raw flags and sensitive material stay in
loot/only. Do not paste them here.
Artifact Inventory
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c736f-b2f3-452f-a3a3-c3d5ed6b5958.zip | 586016 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 6 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-13T10:06:22Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-13T10:06:22Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-13T10:06:48Z | hypothesis recorded | hypothesis-board.md | Analyze the provided ARM binary, patch, libc, and Docker setup to build a local exploit, then send the same payload to the remote service. | Medium | Inspect patch/source hints, binary architecture/protections, and run local service/Docker if available to reproduce the crash and shell path. |
| 2026-06-13T10:06:48Z | research task | analysis/research/task-20260613T100648528187Z-9fae4574.md | Research task created for advisory investigation | Medium | Record research output |
| 2026-06-13T10:08:28Z | instrumentation plan | analysis/instrumentation-plan.md | Build a reproducible exploit for the ARM stack overflow that bypasses canary/ASLR and gets a shell on the remote service. | High | Stop after two remote crash/failure attempts without new leak/layout evidence; record failure and reassess canary or ASLR assumptions before more attempts. |
| 2026-06-13T10:08:28Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-13T10:08:28Z | checkpoint recorded | analysis/checkpoint-analysis-20260613T100828500808Z-6e4c5ed7.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-13T10:11:21Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-13T10:11:39Z | RAG query | analysis/rag/rag-query-20260613T101121206417Z-1ab6a2d4.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-13T10:12:15Z | local memory search | analysis/research/local-memory-search-20260613T101215384252Z-d1ddc39f.md | Found 5 safe prior-note result(s) | Medium | Record useful result or skip |
| 2026-06-13T10:15:07Z | RAG record | analysis/rag-records.md | Retrieved memory tagged GENERIC | Medium | Validate or reject with live evidence |
| 2026-06-13T10:15:41Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-13T10:15:41Z | research record | analysis/research/research-records.md | Research tagged MATCHED | Medium | Validate against current evidence |
| 2026-06-13T10:19:14Z | evaluator | analysis/evaluator-20260613T101914838491Z-fdc49dbc.md | Proceed | High | Gate before exploit, then execute solve/solve.py with host/port and capture the flag candidate if produced. |
| 2026-06-13T10:19:14Z | research record | analysis/research/research-records.md | Research tagged MATCHED | Medium | Validate against current evidence |
| 2026-06-13T10:24:26Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-13T10:25:33Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- Remote endpoint:
<TARGET>:31484. string_storerhas a stack overflow fromscanf("%m[^\n]%n")plus uncheckedmemcpyinto a 32-byte stack buffer.- Non-
quitinput is echoed throughputs(buffer)before the canary check, enabling byte-by-byte leaks. - Final solve leaks canary, PIE, and libc, then uses ARM/Thumb-aware ret2libc to execute
system("/bin/sh"). - Raw flag is stored only at
loot/flag.txt.
RAG / Advisory Memory
RAG output is advisory only. Record evaluated retrievals with:
scripts/challenge_harness.py rag-record <workspace> --query "..." --tag MATCHED|PARTIAL|MISSING|<secret redacted>|GENERIC --validation "..."Secrets/Flags
Raw flags and sensitive material stay in loot/ only. Use scripts/challenge_harness.py capture-flag to validate and record flag capture without printing the value.
Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: Pwn
- Challenge: Arms-Roped
- Difficulty: Medium
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
- ARM PIE binary has a stack overflow from newline-delimited dynamic
scanfinput copied into a fixed stack buffer. - The vulnerable function echoes non-exit input with
puts(buffer)and only checks the canary on thequitexit path. - Non-exit iterations can be used as a byte oracle to leak the canary and saved return address, then repair the canary in a final exit payload.
- PIE base comes from the saved return address. Libc base comes from CSU-mediated byte leaks of a resolved GOT function pointer.
- Final payload uses an ARM/Thumb-aware libc gadget to call
system("/bin/sh")and read the flag.
Reusable Lessons
- In looping stack-overflow programs, check whether the canary is validated only at function exit; pre-exit output can become a byte leak.
- Use byte-wise leaks when
putstruncation on null bytes would make a single long leak unreliable. - Clear ARM/Thumb interworking bit from leaked libc function pointers before base calculation.
- Account for newline-sensitive parsers when packing binary ROP payloads.
Dead Ends
- Local QEMU/Docker reproduction was unavailable because Docker daemon/QEMU tooling was missing locally. The solve proceeded with static analysis plus bounded remote validation.
- RAG returned generic/unrelated ARM challenge material and was not treated as evidence.
Tool Quirks
- macOS host lacked
readelf, ARM-aware objdump variants, QEMU ARM, gdb, and pwntools. Install-request artifacts were recorded instead of installing silently. - The stock
objdumpmixed ARM/Thumb decoding in libc, so the final script uses known offsets plus raw-byte validation and Thumb-bit normalization.
Evidence Paths
analysis/string_storer.disasm.txtanalysis/exploit-analysis.mdanalysis/remote-baseline-transcript.txtanalysis/libc-thumb-pop-candidates.txtsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes, sanitized technique only
- Requires user approval before ingestion: yes
Hypothesis Board
Keep no more than 3 active hypotheses on Easy/Medium and 5 on Hard unless the user explicitly asks for breadth.
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | Analyze the provided ARM binary, patch, libc, and Docker setup to build a local exploit, then send the same payload to the remote service. | Archive contains arms_roped, libc.so.6, Dockerfile, and patch.diff; scenario asks for shell on a remote TCP service. | Inspect patch/source hints, binary architecture/protections, and run local service/Docker if available to reproduce the crash and shell path. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Technical analogy
How to remember this solve
Think of the challenge as a small system with one rule that matters more than the rest. The solve is finding that rule, validating it, and using it carefully enough to reach the final proof.
For Arms Roped, keep the mental model simple: identify the trusted assumption, prove it with the smallest safe test, then automate or repeat only the part that directly leads to the flag.