Maze
Maze is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Maze attack path
Maze 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 Reversing evidence, validation, and reusable operator lessons.
Walkthrough flow
Binary triage
Control-flow recovery
Key logic reconstruction
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.
- Reversing/Maze/writeup.md
- htb-challenge/Reversing/Maze/notes.md
- htb-challenge/Reversing/Maze/memory-summary.md
- htb-challenge/Reversing/Maze/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: Maze
- Category: Reversing
- Difficulty: Medium
- Mode: file
Summary
maze.exe is a Windows PyInstaller bundle that hides a multi-stage reversing
chain. The visible executable asks for a route, decrypts an AES ZIP, repairs a
second-stage Linux ELF using a PRNG key derived from maze.png, and the ELF
then validates the final HTB-format answer through sliding 3-byte sums.
The solve path was:
- Extract the PyInstaller CArchive.
- Disassemble the main Python 3.8 bytecode.
- Decode the nested
obf_pathmodule to recover the PNG-derived PRNG seed. - Decrypt and repair the second-stage ELF.
- Parse the ELF’s sum table and solve the linear constraints.
Artifact Inventory
files/a12c733f-ef44-47e3-941d-19d83b135774.zip: original challenge archive.files/extracted/rev_maze/maze.exe: Windows x64 PyInstaller bundle.files/extracted/rev_maze/maze.png: image used by the obfuscated helper to
derive a PRNG seed.
files/extracted/rev_maze/enc_maze.zip: AES-encrypted ZIP containing the
second-stage file maze.
analysis/source-audit.md: local audit of the PyInstaller bundle, nested
module, seed derivation, ELF repair, and table solve.
Analysis
The PyInstaller cookie and table of contents were parsed from maze.exe. The
main script entry is named maze, and the embedded PYZ contains a module named
obf_path.
The main Python 3.8 bytecode was disassembled in Docker. It accepts only one
route string, calls obf_path.obfuscate_route(), decrypts enc_maze.zip, then
modifies every tenth byte of the extracted maze file.
The imported obf_path module contains nested marshal, LZMA, and zlib layers.
Decoding those layers without executing them shows that it reads four bytes
from maze.png at offsets 4817, 2624, 2640, and 2720; their sum is the
PRNG seed 493. The module also prints a clue to generate 300 random integers
with randint(32, 125).
Using that random stream as the XOR key after the main script’s +80 mod 256
step repairs the encrypted ZIP’s extracted maze file into a valid Linux
x86-64 ELF. The repaired ELF then checks the final answer:
- It requires the input to begin with
HTB. - It has a 36-dword table at
.rodataoffset0x2060. - Each table entry is the sum of a 3-character sliding window.
- A 36-entry table means the final answer is 38 bytes.
With the fixed HTB prefix, each next character is determined by:
next = table[i] - previous - currentRAG returned only generic reversing guidance, so it was recorded as advisory.
All decisive evidence comes from the current local artifacts.
Solve
The reproducible solver is [solve.py](<local workspace>).
It performs the complete chain:
- Reads
maze.pngand derives seed493. - Uses
7zwith the recovered AES ZIP password to extract the second-stage
maze.
- Repairs every tenth byte using the seed-derived random key.
- Reads the dword table from the repaired ELF.
- Solves the sliding-sum equations.
- Writes the generated HTB-format candidate to the requested output path.
The repaired ELF was then validated with the generated candidate. The redacted
success transcript is stored at
analysis/decoded-elf-validation-redacted.txt.
Harness capture used:
python3 scripts/challenge_exec.py Reversing/Maze -- \
python3 Reversing/Maze/solve/solve.py \
--workspace Reversing/Maze \
--output Reversing/Maze/analysis/flag-candidate.txt \
--decoded-elf analysis/decoded/solved_dec_maze \
--transcript analysis/solve-transcript.txt
python3 scripts/challenge_harness.py capture-flag Reversing/Maze \
--from analysis/flag-candidate.txtFlag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- PyInstaller bundles can be solved cleanly by parsing the CArchive and PYZ
structures instead of treating the PE as native code.
- Obfuscated helper modules may carry dynamic repair clues; nested marshal and
compressed payloads should be decoded but not blindly executed.
- A sliding-sum table becomes a deterministic linear solve when the prefix is
known.
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: Maze
- Category: Reversing
- Difficulty: Medium
- Mode: file
- Remote instance: none
- Start time: 2026-06-12T15:55:46Z
- 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/a12c733f-ef44-47e3-941d-19d83b135774.zip | 5266198 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 4 shown in artifact inventory JSON |
files/extracted/rev_maze/enc_maze.zip | 2500 | <hash redacted> | Zip archive data, at least v6.3 to extract, compression method=AES Encrypted | zip entries: 1 shown in artifact inventory JSON |
files/extracted/rev_maze/maze.exe | 4983877 | <hash redacted> | PE32+ executable (console) x86-64, for MS Windows | |
files/extracted/rev_maze/maze.png | 433995 | <hash redacted> | PNG image data, 512 x 512, 8-bit/color RGBA, non-interlaced |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-12T15:55:46Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-12T15:55:46Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-12T15:55:57Z | artifact inventory | analysis/artifact-inventory.json | 4 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-12T15:58:57Z | hypothesis recorded | hypothesis-board.md | Extract the PyInstaller Python payload, recover the accepted maze input and AES ZIP password from the Python 3.8 bytecode constants, decrypt enc_maze.zip, patch every 10th byte of the extracted maze file as the bytecode does, then inspect the decoded artifact for the flag. | Medium | Run a local solve script that extracts the archive using the recovered password, applies the byte transform, then validates the decoded file type and captures any HTB-format output. |
| 2026-06-12T16:03:32Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-12T16:03:44Z | research record | analysis/research/research-records.md | Research tagged MATCHED | Medium | Validate against current evidence |
| 2026-06-12T16:03:45Z | checkpoint recorded | analysis/checkpoint-analysis-20260612T160345037536Z-62b91e54.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-12T16:03:57Z | RAG query | analysis/rag/rag-query-20260612T160345100744Z-a0fa068c.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-12T16:04:19Z | RAG record | analysis/rag-records.md | Retrieved memory tagged GENERIC | Medium | Validate or reject with live evidence |
| 2026-06-12T16:04:19Z | instrumentation plan | analysis/instrumentation-plan.md | Generate and capture the HTB-format answer by reproducing the recovered PyInstaller, PNG-seed, AES ZIP, ELF repair, and sliding-sum solve chain. | High | Stop if 7z extraction fails, repaired ELF is not valid, sliding-sum validation fails, or the decoded ELF rejects the generated candidate. |
| 2026-06-12T16:04:19Z | evaluator | analysis/evaluator-20260612T160419585198Z-96d5b958.md | Proceed | High | Run harness-gated solve.py and capture the generated candidate. |
| 2026-06-12T16:04:40Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-12T16:05:24Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-12T16:06:10Z | 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: Reversing
- Challenge: Maze
- 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 | Extract the PyInstaller Python payload, recover the accepted maze input and AES ZIP password from the Python 3.8 bytecode constants, decrypt enc_maze.zip, patch every 10th byte of the extracted maze file as the bytecode does, then inspect the decoded artifact for the flag. | The PyInstaller TOC contains a main script named maze; Python 3.8 disassembly shows the accepted input string, the AES ZIP password string, and the post-extraction byte transformation. | Run a local solve script that extracts the archive using the recovered password, applies the byte transform, then validates the decoded file type and captures any HTB-format output. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Technical analogy
How to remember this solve
Think of it like taking apart a small appliance on a workbench. You do not need every screw at once; you trace the control path and rebuild just enough logic to make it reveal the answer.
For Maze, 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.