Virtually Mad
Virtually Mad is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Virtually Mad attack path
Virtually Mad 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/Virtually-Mad/writeup.md
- htb-challenge/Reversing/Virtually-Mad/notes.md
- htb-challenge/Reversing/Virtually-Mad/memory-summary.md
- htb-challenge/Reversing/Virtually-Mad/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: Virtually-Mad
- Category: Reversing
- Difficulty: Medium
- Mode: file
Summary
virtually.mad is a stripped Linux ELF that implements a small custom virtual
machine. The binary asks for a hex-encoded program, executes five VM opcodes,
then checks the final register and flag state before printing the HTB-format
answer.
The solve path was to reverse the opcode format, construct the shortest
program that satisfies the final checks, validate it against a local simulator
and the real ELF, then capture the generated answer through the harness.
Artifact Inventory
files/a12c7370-0371-4e10-9634-462e3ff95d45.zip: original challenge archive.files/extracted/rev-virtuallymad/virtually.mad: stripped 64-bit PIE Linux
ELF containing the custom VM.
analysis/source-audit.md: reconstructed VM layout, instruction format,
dispatch table, and final checks.
analysis/binary-validation-redacted.txt: Docker run against the real ELF
showing the generated program reaches the redacted success output.
Analysis
The disassembly shows that each input instruction is an eight-character hex
chunk parsed as a 32-bit opcode. The high byte selects the VM operation:
mov, add, sub, cmp, or exit.
For the useful arithmetic and comparison operations, the instruction format is:
bits 31..24: opcode
bits 23..20: subtype, required to be 1
bits 19..16: destination register index
bits 15..12: operand mode, 0 for immediate and 1 for register
bits 11..0 : immediate value, or source register index shifted left by 8The VM has four 32-bit registers, a, b, c, and d, plus a flags field.
The main routine requires exactly five instructions and applies per-position
shape checks. The final accepted state is:
a == 0x200
b == -1
c == -1
d == 0
flags == 0x10000000
opcode_count == 5That state can be reached with this conceptual VM program:
add a, 0x100
add a, 0x100
sub b, 1
mov c, b
cmp d, 0The RAG query did not return actionable challenge-specific memory, so it was
recorded as missing and rejected. The evidence used for the final path is the
current ELF disassembly, the local VM simulator, and Docker validation against
the binary.
Solve
solve/solve.py implements the VM encoder and simulator. It first self-tests
the constructed five-opcode program against the expected final VM state. It can
then write the resulting HTB-format answer to a file for harness capture.
Validation performed:
python3 -m py_compile Reversing/Virtually-Mad/solve/solve.py
python3 Reversing/Virtually-Mad/solve/solve.py --self-test \
--transcript Reversing/Virtually-Mad/analysis/self-test-transcript.txtThe generated code was also sent to the real Linux ELF inside Docker. The
redacted transcript in analysis/binary-validation-redacted.txt confirms the
binary reached the success path with:
a: 0x200
b: 0xffffffff
c: 0xffffffff
d: 0x0
flags: 0x10000000Final capture was done through the challenge harness:
python3 scripts/challenge_exec.py Reversing/Virtually-Mad -- \
python3 Reversing/Virtually-Mad/solve/solve.py \
--output Reversing/Virtually-Mad/analysis/flag-candidate.txt \
--transcript Reversing/Virtually-Mad/analysis/solve-transcript.txt
python3 scripts/challenge_harness.py capture-flag Reversing/Virtually-Mad \
--from analysis/flag-candidate.txtFlag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- A compact custom VM is often faster to solve by reconstructing the instruction
fields and writing a small simulator than by manually trying inputs.
- Per-index validation in
maincan be as important as the VM dispatch logic. - For file-only reversing challenges, a redacted binary validation transcript is
enough to preserve proof without leaking the raw flag outside loot/.
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: Virtually-Mad
- Category: Reversing
- Difficulty: Medium
- Mode: file
- Remote instance: none
- Start time: 2026-06-12T15:45:07Z
- 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/a12c7370-0371-4e10-9634-462e3ff95d45.zip | 3584 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 2 shown in artifact inventory JSON |
files/extracted/rev-virtuallymad/virtually.mad | 14472 | <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 4.4.0, stripped |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-12T15:45:07Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-12T15:45:19Z | artifact inventory | analysis/artifact-inventory.json | 2 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-12T15:49:05Z | hypothesis recorded | hypothesis-board.md | Reconstruct the custom VM opcode format, build a five-opcode program that satisfies the final register and flag checks, and wrap the accepted code as the challenge flag. | High | Run solve.py through challenge_exec, then capture the generated HTB-format candidate. |
| 2026-06-12T15:49:05Z | research record | analysis/research/research-records.md | Research tagged MATCHED | Medium | Validate against current evidence |
| 2026-06-12T15:49:05Z | checkpoint recorded | analysis/checkpoint-analysis-20260612T154905882586Z-6aca4153.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-12T15:49:16Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-12T15:49:33Z | RAG query | analysis/rag/rag-query-20260612T154924521986Z-a4b96531.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-12T15:49:56Z | instrumentation plan | analysis/instrumentation-plan.md | Generate the accepted VM code from decoded opcode fields and capture it as the HTB-format answer. | High | Stop if solver self-test fails, Docker validation does not reach success, or generated candidate fails HTB-format capture. |
| 2026-06-12T15:49:56Z | RAG record | analysis/rag-records.md | Retrieved memory tagged MISSING | Medium | Validate or reject with live evidence |
| 2026-06-12T15:49:56Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-12T15:50:06Z | evaluator | analysis/evaluator-20260612T155006912934Z-6059cc3f.md | Proceed | High | Run solve.py and capture the candidate with challenge_harness.py capture-flag. |
| 2026-06-12T15:51:38Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-12T15:52:28Z | 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: Virtually-Mad
- 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 | Reconstruct the custom VM opcode format, build a five-opcode program that satisfies the final register and flag checks, and wrap the accepted code as the challenge flag. | Disassembly shows high-byte dispatch for mov/add/sub/cmp/exit, fixed per-index opcode-shape checks in main, and final checks requiring a=0x200, b=-1, c=-1, d=0, flags=0x10000000 with exactly five opcodes. Docker validation against the ELF reaches the redacted success path. | Need harness-gated candidate generation and flag capture. | Run solve.py through challenge_exec, then capture the generated HTB-format candidate. | High | 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 Virtually Mad, 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.