R0bob1rd
R0bob1rd is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
R0bob1rd attack path
R0bob1rd 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
Invalid bird index -14 leaks printf@GOT because the...
Supplied libc offsets convert the same-process printf...
The description input is a format string, and...
The format string overwrites printf@GOT with system...
A 104-byte first-stage payload followed by newline...
Source coverage
High source coverage
Status: complete. This article is generated from 6 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/r0bob1rd/writeup.md
- htb-challenge/Pwn/r0bob1rd/notes.md
- htb-challenge/Pwn/r0bob1rd/memory-summary.md
- htb-challenge/Pwn/r0bob1rd/hypothesis-board.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Pwn__r0bob1rd__memory-summary.md.683ca4c107.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Pwn__r0bob1rd__notes.md.3064dfaa12.md
Technical Walkthrough
Writeup
Challenge
- Name: r0bob1rd
- Category: Pwn
- Difficulty: Easy
- Mode: hybrid
Summary
r0bob1rd is a Linux x86-64 pwn challenge with a menu-style robotic bird game. The binary includes a matching loader and libc. The exploit combines three bugs in operation():
- invalid bird indexes leak bytes from GOT-adjacent memory
- the description is passed directly to
printf fgetsallows a one-byte canary corruption
The final solve leaks printf, computes libc, overwrites printf@GOT with system, overwrites __stack_chk_fail@GOT with main, triggers the canary failure, and then uses the re-entered program to execute a shell command through the second description prompt.
Artifact Inventory
Key artifacts:
files/a12c7342-35e9-447d-aec5-e2c7b83f695a.zip: original archivefiles/extracted/r0bob1rd: target ELFfiles/extracted/glibc/ld.so.2: supplied dynamic loaderfiles/extracted/glibc/libc.so.6: supplied libcanalysis/elf-analysis.txt: static function disassembly and mitigation notesanalysis/elf-details.txt: useful symbol/libc offsetsanalysis/source-audit.md: exploit reasoninganalysis/remote/solve-transcript-redacted.txt: remote solve transcript with flag redactedsolve/solve.py: reproducible socket solver
Analysis
The binary is non-PIE with NX, canary, and writable .got.plt. It is not stripped, so operation() is easy to identify.
The first primitive is an out-of-bounds read in the invalid bird-selection branch. The code computes:
&robobirdNames + index * 8and passes that computed address to %s. Since robobirdNames is at 0x6020a0, selecting index -14 points to printf@GOT at 0x602030. This leaks the resolved libc printf address in the same connection.
The second primitive is the description formatter:
fgets(buf, 0x6a, stdin)
printf(buf)Remote probing confirmed controlled qwords begin at format argument 8. The solver uses positional %hn writes to modify GOT entries.
The third primitive is the size mismatch. The buffer begins 104 bytes before the canary, and fgets(..., 0x6a, ...) can place data into the canary. A 104-byte payload followed by a newline corrupts the canary and triggers the stack check.
The reliable route is two-stage:
- use the format string to write
systemintoprintf@GOT - write
maininto__stack_chk_fail@GOT - corrupt the canary so the stack-check call re-enters
main - on the second run, the final
printf(buf)is nowsystem(buf) - send a flag-read command as the second description
Solve
Run:
python3 Pwn/r0bob1rd/solve/solve.py \
--host <TARGET> \
--port 31332 \
--output Pwn/r0bob1rd/loot/flag-candidate.txt \
--transcript Pwn/r0bob1rd/analysis/remote/solve-transcript-redacted.txt
python3 scripts/challenge_harness.py capture-flag Pwn/r0bob1rd --from loot/flag-candidate.txtThe solver is pure Python and does not require pwntools. It:
- connects to the service
- selects
-14to leakprintf@GOT - computes libc base and
system - sends the first-stage format string
- answers the re-entered menu
- sends a shell command as the second description
- stores the HTB-format flag candidate under
loot/
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- In pwn menus, invalid indexes can become useful info leaks even when the obvious bug is a later format string.
- A canary mismatch can be used as a controlled dispatch when
__stack_chk_fail@GOTis writable. - Re-entering
maincan be more reliable than depending on caller-saved registers after a libc call. - A pure socket solver is enough for simple pwn services when pwntools is unavailable.
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: r0bob1rd
- Category: Pwn
- Difficulty: Easy
- Mode: hybrid
- Remote instance: <TARGET>:31332
- Start time: 2026-06-08T10:48:40Z
- 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/a12c7342-35e9-447d-aec5-e2c7b83f695a.zip | 1431292 | <hash redacted> | Zip archive data, at least v2.0 to extract, compression method=deflate | zip entries: 4 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-08T10:48:40Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-08T10:48:53Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-08T10:49:04Z | hypothesis recorded | hypothesis-board.md | Local binary triage: inspect packaged executable, protections, protocol, and source/container files if provided; develop a reproducible exploit locally before touching remote. | Medium | Extract package, identify binary and run file/strings/readelf/checksec-equivalent analysis. |
| 2026-06-08T10:49:16Z | checkpoint recorded | analysis/checkpoint-triage-20260608T104916356590Z-3965f1d4.md | Checkpoint for TRIAGE | High | Use checkpoint to drive next decision |
| 2026-06-08T10:49:16Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-08T10:51:50Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-08T10:57:38Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T10:57:46Z | evaluator | analysis/evaluator-20260608T105746514710Z-24183d1c.md | Proceed | High | Run the solver against the updated remote, then capture the flag through the harness if returned. |
| 2026-06-08T11:02:00Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T11:02:25Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-08T11:03:31Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
-
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: r0bob1rd
- Difficulty: Easy
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
- Invalid bird index
-14leaksprintf@GOTbecause the invalid branch treats&robobirdNames + index * 8as a string pointer. - Supplied libc offsets convert the same-process
printfleak into libc base andsystem. - The description input is a format string, and controlled qwords begin at argument 8.
- The format string overwrites
printf@GOTwithsystemand__stack_chk_fail@GOTwithmain. - A 104-byte first-stage payload followed by newline corrupts the canary and re-enters
main. - On the second run, the final
printf(buf)dispatches tosystem(buf), so the second description can run a flag-read command.
Reusable Lessons
- If
__stack_chk_fail@GOTis writable, canary failure can become a useful control-flow edge instead of only a crash. - When direct
__stack_chk_fail -> systemis unreliable because argument registers are volatile, redirecting tomaincan create a cleaner second-stage input. - For non-PIE pwn binaries, signed index arithmetic around global arrays can leak GOT entries without needing a format-string leak first.
- Pure Python sockets can replace pwntools for straightforward menu protocols.
Dead Ends
- Direct
__stack_chk_fail@GOT -> systemwas tested first but did not reliably produce a shell because the buffer pointer was not preserved inrdi.
Tool Quirks
- The host lacked
gdb, pwntools, and checksec. Workspace-local Python ELF tooling was used for static analysis. - macOS
stringsrequires-t x, not GNU-style-tx. - The initial remote endpoint changed; the solved endpoint was
<TARGET>:31332.
Evidence Paths
analysis/source-audit.mdanalysis/elf-analysis.txtanalysis/elf-details.txtanalysis/remote/solve-transcript-redacted.txtsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes, after user approval
- 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 | Local binary triage: inspect packaged executable, protections, protocol, and source/container files if provided; develop a reproducible exploit locally before touching remote. | Pwn challenge includes downloadable archive and live TCP remote. | Exact memory corruption or logic primitive and exploit constraints. | Extract package, identify binary and run file/strings/readelf/checksec-equivalent analysis. | Medium | active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Memory Summary
approval_required: true
Sanitized Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: Pwn
- Challenge: r0bob1rd
- Difficulty: Easy
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
- Invalid bird index
-14leaksprintf@GOTbecause the invalid branch treats&robobirdNames + index * 8as a string pointer. - Supplied libc offsets convert the same-process
printfleak into libc base andsystem. - The description input is a format string, and controlled qwords begin at argument 8.
- The format string overwrites
printf@GOTwithsystemand__stack_chk_fail@GOTwithmain. - A 104-byte first-stage payload followed by newline corrupts the canary and re-enters
main. - On the second run, the final
printf(buf)dispatches tosystem(buf), so the second description can run a flag-read command.
Reusable Lessons
- If
__stack_chk_fail@GOTis writable, canary failure can become a useful control-flow edge instead of only a crash. - When direct
__stack_chk_fail -> systemis unreliable because argument registers are volatile, redirecting tomaincan create a cleaner second-stage input. - For non-PIE pwn binaries, signed index arithmetic around global arrays can leak GOT entries without needing a format-string leak first.
- Pure Python sockets can replace pwntools for straightforward menu protocols.
Dead Ends
- Direct
__stack_chk_fail@GOT -> systemwas tested first but did not reliably produce a shell because the buffer pointer was not preserved inrdi.
Tool Quirks
- The host lacked
gdb, pwntools, and checksec. Workspace-local Python ELF tooling was used for static analysis. - macOS
stringsrequires-t x, not GNU-style-tx. - The initial remote endpoint changed; the solved endpoint was
<TARGET>:31332.
Evidence Paths
analysis/source-audit.mdanalysis/elf-analysis.txtanalysis/elf-details.txtanalysis/remote/solve-transcript-redacted.txtsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes, after user approval
- Requires user approval before ingestion: yes
Notes
Notes
Scope
- Challenge: r0bob1rd
- Category: Pwn
- Difficulty: Easy
- Mode: hybrid
- Remote instance: <TARGET>:31332
- Start time: 2026-06-08T10:48:40Z
- 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/a12c7342-35e9-447d-aec5-e2c7b83f695a.zip | 1431292 | <hash redacted> | Zip archive data, at least v2.0 to extract, compression method=deflate | zip entries: 4 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-08T10:48:40Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-08T10:48:53Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-08T10:49:04Z | hypothesis recorded | hypothesis-board.md | Local binary triage: inspect packaged executable, protections, protocol, and source/container files if provided; develop a reproducible exploit locally before touching remote. | Medium | Extract package, identify binary and run file/strings/readelf/checksec-equivalent analysis. |
| 2026-06-08T10:49:16Z | checkpoint recorded | analysis/checkpoint-triage-20260608T104916356590Z-3965f1d4.md | Checkpoint for TRIAGE | High | Use checkpoint to drive next decision |
| 2026-06-08T10:49:16Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-08T10:51:50Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-08T10:57:38Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T10: <REDACTED>, then capture the flag through the harness if returned. | |||||
| 2026-06-08T11:02:00Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T11: <REDACTED> | |||||
| 2026-06-08T11:03:31Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
-
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.
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 R0bob1rd, 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.