Challenge / Reversing

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

MediumPublished 2025-08-10Sanitized local writeup

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.

Virtually Mad sanitized attack graph

Walkthrough flow

01

Binary triage

02

Control-flow recovery

03

Key logic reconstruction

04

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.

99% 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.

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

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

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

text
a == 0x200
b == -1
c == -1
d == 0
flags == 0x10000000
opcode_count == 5

That state can be reached with this conceptual VM program:

text
add a, 0x100
add a, 0x100
sub b, 1
mov c, b
cmp d, 0

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

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

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

text
a: 0x200
b: 0xffffffff
c: 0xffffffff
d: 0x0
flags: 0x10000000

Final capture was done through the challenge harness:

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

Flag

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 main can 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

FileSizeSHA256TypeNotes
files/a12c7370-0371-4e10-9634-462e3ff95d45.zip3584<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 2 shown in artifact inventory JSON
files/extracted/rev-virtuallymad/virtually.mad14472<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

TimeActionOutput/FileFindingConfidenceNext
2026-06-12T15:45:07Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-12T15:45:19Zartifact inventoryanalysis/artifact-inventory.json2 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-12T15:49:05Zhypothesis recordedhypothesis-board.mdReconstruct 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.HighRun solve.py through challenge_exec, then capture the generated HTB-format candidate.
2026-06-12T15:49:05Zresearch recordanalysis/research/research-records.mdResearch tagged MATCHEDMediumValidate against current evidence
2026-06-12T15:49:05Zcheckpoint recordedanalysis/checkpoint-analysis-20260612T154905882586Z-6aca4153.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-12T15:49:16Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-12T15:49:33ZRAG queryanalysis/rag/rag-query-20260612T154924521986Z-a4b96531.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-12T15:49:56Zinstrumentation plananalysis/instrumentation-plan.mdGenerate the accepted VM code from decoded opcode fields and capture it as the HTB-format answer.HighStop if solver self-test fails, Docker validation does not reach success, or generated candidate fails HTB-format capture.
2026-06-12T15:49:56ZRAG recordanalysis/rag-records.mdRetrieved memory tagged MISSINGMediumValidate or reject with live evidence
2026-06-12T15:49:56Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-12T15:50:06Zevaluatoranalysis/evaluator-20260612T155006912934Z-6059cc3f.mdProceedHighRun solve.py and capture the candidate with challenge_harness.py capture-flag.
2026-06-12T15:51:38Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-12T15:52:28Zcompletion 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: 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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Reconstruct 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.HighActive

Closed Branches

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