Challenge / Pwn

R0bob1rd

R0bob1rd is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator

EasyPublished 2025-04-12Sanitized local writeup

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.

R0bob1rd sanitized attack graph

Walkthrough flow

01

Invalid bird index -14 leaks printf@GOT because the...

02

Supplied libc offsets convert the same-process printf...

03

The description input is a format string, and...

04

The format string overwrites printf@GOT with system...

05

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.

100% coverage
Evidence verdict

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
  • fgets allows 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 archive
  • files/extracted/r0bob1rd: target ELF
  • files/extracted/glibc/ld.so.2: supplied dynamic loader
  • files/extracted/glibc/libc.so.6: supplied libc
  • analysis/elf-analysis.txt: static function disassembly and mitigation notes
  • analysis/elf-details.txt: useful symbol/libc offsets
  • analysis/source-audit.md: exploit reasoning
  • analysis/remote/solve-transcript-redacted.txt: remote solve transcript with flag redacted
  • solve/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:

text
&robobirdNames + index * 8

and 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:

text
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:

  1. use the format string to write system into printf@GOT
  2. write main into __stack_chk_fail@GOT
  3. corrupt the canary so the stack-check call re-enters main
  4. on the second run, the final printf(buf) is now system(buf)
  5. send a flag-read command as the second description

Solve

Run:

bash
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.txt

The solver is pure Python and does not require pwntools. It:

  1. connects to the service
  2. selects -14 to leak printf@GOT
  3. computes libc base and system
  4. sends the first-stage format string
  5. answers the re-entered menu
  6. sends a shell command as the second description
  7. 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@GOT is writable.
  • Re-entering main can 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

FileSizeSHA256TypeNotes
files/a12c7342-35e9-447d-aec5-e2c7b83f695a.zip1431292<hash redacted>Zip archive data, at least v2.0 to extract, compression method=deflatezip entries: 4 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-08T10:48:40Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-08T10:48:53Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-08T10:49:04Zhypothesis recordedhypothesis-board.mdLocal binary triage: inspect packaged executable, protections, protocol, and source/container files if provided; develop a reproducible exploit locally before touching remote.MediumExtract package, identify binary and run file/strings/readelf/checksec-equivalent analysis.
2026-06-08T10:49:16Zcheckpoint recordedanalysis/checkpoint-triage-20260608T104916356590Z-3965f1d4.mdCheckpoint for TRIAGEHighUse checkpoint to drive next decision
2026-06-08T10:49:16Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-08T10:51:50Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-08T10:57:38Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T10:57:46Zevaluatoranalysis/evaluator-20260608T105746514710Z-24183d1c.mdProceedHighRun the solver against the updated remote, then capture the flag through the harness if returned.
2026-06-08T11:02:00Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T11:02:25Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-08T11:03:31Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

-

RAG / Advisory Memory

RAG output is advisory only. Record evaluated retrievals with:

bash
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.

  1. Invalid bird index -14 leaks printf@GOT because the invalid branch treats &robobirdNames + index * 8 as a string pointer.
  2. Supplied libc offsets convert the same-process printf leak into libc base and system.
  3. The description input is a format string, and controlled qwords begin at argument 8.
  4. The format string overwrites printf@GOT with system and __stack_chk_fail@GOT with main.
  5. A 104-byte first-stage payload followed by newline corrupts the canary and re-enters main.
  6. On the second run, the final printf(buf) dispatches to system(buf), so the second description can run a flag-read command.

Reusable Lessons

  • If __stack_chk_fail@GOT is writable, canary failure can become a useful control-flow edge instead of only a crash.
  • When direct __stack_chk_fail -> system is unreliable because argument registers are volatile, redirecting to main can 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 -> system was tested first but did not reliably produce a shell because the buffer pointer was not preserved in rdi.

Tool Quirks

  • The host lacked gdb, pwntools, and checksec. Workspace-local Python ELF tooling was used for static analysis.
  • macOS strings requires -t x, not GNU-style -tx.
  • The initial remote endpoint changed; the solved endpoint was <TARGET>:31332.

Evidence Paths

  • analysis/source-audit.md
  • analysis/elf-analysis.txt
  • analysis/elf-details.txt
  • analysis/remote/solve-transcript-redacted.txt
  • solve/solve.py
  • loot/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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Local 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.Mediumactive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit 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.

  1. Invalid bird index -14 leaks printf@GOT because the invalid branch treats &robobirdNames + index * 8 as a string pointer.
  2. Supplied libc offsets convert the same-process printf leak into libc base and system.
  3. The description input is a format string, and controlled qwords begin at argument 8.
  4. The format string overwrites printf@GOT with system and __stack_chk_fail@GOT with main.
  5. A 104-byte first-stage payload followed by newline corrupts the canary and re-enters main.
  6. On the second run, the final printf(buf) dispatches to system(buf), so the second description can run a flag-read command.

Reusable Lessons

  • If __stack_chk_fail@GOT is writable, canary failure can become a useful control-flow edge instead of only a crash.
  • When direct __stack_chk_fail -> system is unreliable because argument registers are volatile, redirecting to main can 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 -> system was tested first but did not reliably produce a shell because the buffer pointer was not preserved in rdi.

Tool Quirks

  • The host lacked gdb, pwntools, and checksec. Workspace-local Python ELF tooling was used for static analysis.
  • macOS strings requires -t x, not GNU-style -tx.
  • The initial remote endpoint changed; the solved endpoint was <TARGET>:31332.

Evidence Paths

  • analysis/source-audit.md
  • analysis/elf-analysis.txt
  • analysis/elf-details.txt
  • analysis/remote/solve-transcript-redacted.txt
  • solve/solve.py
  • loot/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

FileSizeSHA256TypeNotes
files/a12c7342-35e9-447d-aec5-e2c7b83f695a.zip1431292<hash redacted>Zip archive data, at least v2.0 to extract, compression method=deflatezip entries: 4 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-08T10:48:40Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-08T10:48:53Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-08T10:49:04Zhypothesis recordedhypothesis-board.mdLocal binary triage: inspect packaged executable, protections, protocol, and source/container files if provided; develop a reproducible exploit locally before touching remote.MediumExtract package, identify binary and run file/strings/readelf/checksec-equivalent analysis.
2026-06-08T10:49:16Zcheckpoint recordedanalysis/checkpoint-triage-20260608T104916356590Z-3965f1d4.mdCheckpoint for TRIAGEHighUse checkpoint to drive next decision
2026-06-08T10:49:16Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-08T10:51:50Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-08T10:57:38Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T10: <REDACTED>, then capture the flag through the harness if returned.
2026-06-08T11:02:00Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T11: <REDACTED>
2026-06-08T11:03:31Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

-

RAG / Advisory Memory

RAG output is advisory only. Record evaluated retrievals with:

bash
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.