Forks And Knives
Forks And Knives is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Forks And Knives attack path
Forks And Knives 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
Binary triage
Memory primitive
Control-flow hijack
Proof captured
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/Forks-and-Knives/writeup.md
- htb-challenge/Pwn/Forks-and-Knives/notes.md
- htb-challenge/Pwn/Forks-and-Knives/memory-summary.md
- htb-challenge/Pwn/Forks-and-Knives/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: Forks-and-Knives
- Category: Pwn
- Difficulty: Medium
- Mode: hybrid
Summary
Forks-and-Knives is a fork-per-client menu service with three bugs that chain together:
- A 16-byte name input clears the adjacent manager flag with the automatic NUL terminator.
- The reservation path passes a short user-controlled string directly to
fprintf, creating a file-backed format-string leak that can be viewed through the manager interface. - The order "add more" flow performs a second read at
buffer + first_read_count; with a first read of0x100, the second read crosses the stack canary and saved return address.
The final exploit leaks libc, brute-forces the fork-stable stack canary, then uses ret2libc to attach a shell to the socket and read the flag.
Artifact Inventory
files/extracted/pwn_forks_and_knives/challenge/server: stripped PIE x86-64 service.files/extracted/pwn_forks_and_knives/challenge/libc.so.6: provided libc used for offsets.files/extracted/pwn_forks_and_knives/Dockerfile: Ubuntu 22.04 service image exposing the binary on TCP 1337.
Analysis
Relevant evidence:
analysis/local-disassembly-notes.mdrecords the manager flag, format string, and overflow locations from disassembly.analysis/remote-readonly-leak-table-v2.txtrecords compact remote leaks.analysis/remote-libc-offset-byte-match.txtandanalysis/remote-libc-releak-after-canary.txtshow why the libc leak offset is0x11491b.analysis/remote-canary-oracle-sanity-v3.txtvalidates the canary crash oracle.analysis/remote-rop-write-probe-fd4.txtvalidates libc base, socket fd 4, and ROP delivery before the final shell stage.
The first libc-base attempt used a page-aligned but incorrect offset. Dereferencing %2$s showed the leaked address starts with bytes 48 3d 00, which uniquely matched offset 0x11491b in the provided libc among offsets ending in 0x91b.
Solve
Run:
python3 Pwn/Forks-and-Knives/solve/solve.py --host <TARGET> --port 32185 --output Pwn/Forks-and-Knives/analysis/flag-candidate.txtThe script:
- leaks libc with the reservation format string;
- brute-forces the fork-stable canary;
- sends a final ROP chain using
dup2andexecve; - extracts an HTB-format flag candidate into
analysis/flag-candidate.txt.
Use the harness to store the flag:
python3 scripts/challenge_harness.py capture-flag Pwn/Forks-and-Knives --from analysis/flag-candidate.txtFlag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- Page alignment alone is not enough to identify a libc leak offset; validate with bytes or another independent leak.
- Forking services make stack canary brute force practical when child crashes do not restart the parent.
- For socket shells,
execve("/bin/sh", NULL, NULL)afterdup2was more reliable thansystem("/bin/sh").
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: Forks-and-Knives
- Category: Pwn
- Difficulty: Medium
- Mode: hybrid
- Remote instance: <TARGET>:32185
- Start time: 2026-06-12T09:15:59Z
- 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/a12c7337-67cf-4d8f-9f3a-ccb241eacf33.zip | 954177 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 6 shown in artifact inventory JSON |
files/extracted/pwn_forks_and_knives/Dockerfile | 330 | <hash redacted> | ASCII text | |
files/extracted/pwn_forks_and_knives/build-docker.sh | 132 | <hash redacted> | POSIX shell script text executable, ASCII text | |
files/extracted/pwn_forks_and_knives/challenge/libc.so.6 | 2220400 | <hash redacted> | ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=<hash redacted>, for GNU/Linux 3.2.0, stripped | |
files/extracted/pwn_forks_and_knives/challenge/server | 14384 | <hash redacted> | ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=<hash redacted>, for GNU/Linux 3.2.0, stripped |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-12T09:15:59Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-12T09:18:00Z | artifact inventory | analysis/artifact-inventory.json | 5 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-12T09:18:33Z | hypothesis recorded | hypothesis-board.md | Menu-driven reservation/order binary likely exposes memory corruption or format-string primitive reachable over TCP; exploit should be built locally against bundled server/libc, then replayed remotely. | Medium | Run the server locally in Docker, map menu I/O, then fuzz/send edge-case inputs while monitoring crashes and outputs. |
| 2026-06-12T09:18:53Z | research skip | analysis/research/research-skip.md | Research intentionally skipped with recorded reason | Medium | Gate before exploit |
| 2026-06-12T09:18:53Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-12T09:19:22Z | instrumentation plan | analysis/instrumentation-plan.md | Identify and validate the local memory-corruption or file/logic primitive in the stripped restaurant server before remote exploitation. | High | Stop after two failed remote attempts without a new local fact, or if local instrumentation disproves the current primitive. |
| 2026-06-12T09:20:04Z | evaluator | analysis/evaluator-20260612T092004904167Z-656f65d4.md | Validate first | High | Run local Docker probes for manager unlock, format leak positions, and order overflow layout. |
| 2026-06-12T09:37:35Z | local memory search | analysis/research/local-memory-search-20260612T093735456559Z-b6ba5c3c.md | Found 8 safe prior-note result(s) | Medium | Record useful result or skip |
| 2026-06-12T09:37:54Z | checkpoint recorded | analysis/checkpoint-analysis-20260612T093754481742Z-bb500c63.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-12T09:38:23Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-12T09:39:13Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-12T09:39:25Z | evaluator | analysis/evaluator-20260612T093925092974Z-538728e4.md | Proceed | High | Create solve.py that leaks bases, brute-forces the canary with the crash oracle, sends dup2/system ret2libc chain, and captures HTB flag. |
| 2026-06-12T10:08:14Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-12T10:09:15Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-12T10:23:55Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-12T10:23:55Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
serveris a stripped PIE x86-64 menu service with stack canaries and a bundled Ubuntu/glibc-familylibc.so.6.- The initial name read accepts 16 bytes into the global name buffer and then writes a NUL terminator at
name + read_count; exactly 16 bytes clears the adjacent manager-gate flag and unlocks manager-only reservation viewing. - The reservation path writes a user-controlled short format token through
fprintf(file, format), then manager view readsreservations.txtback over the socket. This gives compact libc/stack/heap leaks. - Remote
%2$pleaked a libc pointer.%2$sbyte matching corrected the offset to0x11491b; the earlier0x6691bpage-aligned guess was wrong and caused the first ROP crash. - The order add-more path reads
0x100bytes into the order buffer, then reads another0x100bytes atbuffer + first_read_count. With a first read of0x100, the second read starts eight bytes before the canary and can overwrite canary, saved RBP, and RIP. - The target is fork-per-client, so the canary is stable across child crashes. The solver brute-forces the canary with a crash/no-crash oracle.
- Final payload uses libc ROP:
dup2(client_fd, 0/1/2)followed byexecve("/bin/sh", NULL, NULL). A diagnosticwrite(fd, "/bin/sh", 8)probe validated the ROP base, fd, and delivery before the final shell stage. - Correction after submission failure: reading
flag*returned an additional HTB-looking thematic decoy/static value. The valid challenge submission value is the explicitflag.txtcontent, sosolve.pynow reads onlyflag.txtand stores the first match.
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: Forks-and-Knives
- 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.
1.
Reusable Lessons
-
Dead Ends
-
Tool Quirks
-
Evidence Paths
-
Ingestion Decision
- Proposed for LightRAG: yes/no
- 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 | Menu-driven reservation/order binary likely exposes memory corruption or format-string primitive reachable over TCP; exploit should be built locally against bundled server/libc, then replayed remotely. | Artifacts are a stripped PIE x86_64 server, bundled libc, and Dockerfile exposing TCP 1337 as ctf user; challenge is Medium Pwn hybrid with remote <TARGET>:32185. | Need interaction map, mitigation summary, exact primitive, leak strategy, and control-flow target. | Run the server locally in Docker, map menu I/O, then fuzz/send edge-case inputs while monitoring crashes and outputs. | 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 Forks And Knives, 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.