Phase Madness
Phase Madness is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Phase Madness attack path
Phase Madness 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 Quantum evidence, validation, and reusable operator lessons.
Walkthrough flow
Circuit constraints
State manipulation
Measurement strategy
Proof captured
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.
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.
- Quantum/Phase-Madness/writeup.md
- htb-challenge/Quantum/Phase-Madness/notes.md
- htb-challenge/Quantum/Phase-Madness/memory-summary.md
- htb-challenge/Quantum/Phase-Madness/hypothesis-board.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Quantum__Phase-Madness__memory-summary.md.ccafdf13c2.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Quantum__Phase-Madness__notes.md.20a6aadfe2.md
Technical Walkthrough
Writeup
Challenge
- Name: Phase-Madness
- Category: Quantum
- Difficulty: Easy
- Mode: hybrid
Summary
This challenge is a deterministic measurement leak. The service encodes each flag byte as a single-qubit rotation angle and lets the player perform arbitrary one-qubit basis changes before a 100,000-shot measurement. Two carefully chosen measurements per qubit are enough to recover cos(theta) and sin(theta), then reconstruct the integer byte angle directly.
Artifact Inventory
Relevant artifacts:
files/extracted/quantum_phase_madness/server.pyanalysis/source-review-quick.txtanalysis/formula-validation.mdanalysis/solver-self-test.txtanalysis/phase-madness-counts.jsonsolve/solve.py
Analysis
server.py creates one qubit per byte of flag.txt, then encodes each byte as an integer-degree rotation with a repeating pattern:
i % 3 == 0:RX(byte)i % 3 == 1:RY(byte)i % 3 == 2:HthenRZ(byte)
The service then allows repeated interactive queries:
- choose a qubit index
- apply semicolon-separated one-qubit
RX/RY/RZinstructions with integer degree parameters - receive JSON measurement counts from 100,000 shots
That makes the challenge a tomography problem, not a brute-force one.
The main service quirk is important: an empty instruction string calls the measurement helper directly on the persistent base_circuit, which mutates it by appending a measurement. Because of that, the safe identity query is not an empty string; it is RZ:0,<q>.
The source-backed recovery formulas are:
| Qubit index | Query 1 | Query 2 | Recovery |
|---|---|---|---|
q % 3 == 0 | RZ:0,q gives cos(theta) | RX:90,q gives -sin(theta) | atan2(-z2, z1) |
q % 3 == 1 | RZ:0,q gives cos(theta) | RY:90,q gives -sin(theta) | atan2(-z2, z1) |
q % 3 == 2 | RY:-90,q gives cos(theta) | RX:90,q gives sin(theta) | atan2(z2, z1) |
For each query:
z = (count("0") - count("1")) / total_shotsThe recovered angle is rounded to the nearest integer byte in [0, 255] by circular distance. The self-test in analysis/solver-self-test.txt confirms that the rounding logic is exact over the full byte range.
Solve
Run:
cd <local workspace>
python3 scripts/challenge_exec.py Quantum/Phase-Madness -- \
python3 Quantum/Phase-Madness/solve/solve.pyThe solver:
- opens one TCP connection to the challenge
- queries qubits in order starting at
0 - performs the correct two non-empty measurements for each qubit class
- stops when the service reports the qubit index is out of range
- reconstructs the byte string
- writes the candidate to
loot/flag-candidate.txt - writes detailed counts and derived estimates to
analysis/phase-madness-counts.json
The wrapped solve recovered 79 bytes and produced a valid HTB{-prefixed candidate, which was then captured through the harness.
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- When a quantum challenge exposes repeated basis-change measurements on independently encoded qubits, the shortest path is usually analytic recovery, not search.
- Service quirks around “empty” operations matter. Here, using an empty instruction string would have corrupted the base circuit and invalidated later measurements.
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: Phase-Madness
- Category: Quantum
- Difficulty: Easy
- Mode: hybrid
- Remote instance: <TARGET>:32125
- Start time: 2026-06-07T20:40:37Z
- 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/a12c738d-8675-425e-af82-a6cfdfbcebd6.zip | 1517 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 2 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-07T20:40:37Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-07T20:40:42Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-07T20:41:54Z | hypothesis recorded | hypothesis-board.md | Recover each flag byte as a rotation angle using repeated single-qubit measurement/tomography queries, then reconstruct the HTB flag bytes. | Medium | Use source-derived formulas on a few locally simulated states or send one non-mutating remote measurement query to confirm prompt/count format. |
| 2026-06-07T20:41:54Z | local memory search | analysis/research/local-memory-search-20260607T204154966928Z-48abf12d.md | Found 5 safe prior-note result(s) | Medium | Record useful result or skip |
| 2026-06-07T20:41:54Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-07T20:42:04Z | checkpoint recorded | analysis/checkpoint-triage-20260607T204204687218Z-10dca62c.md | Checkpoint for TRIAGE | High | Use checkpoint to drive next decision |
| 2026-06-07T20:42:37Z | RAG query | analysis/rag/rag-query-20260607T204222376201Z-bdc74b35.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-07T20:42:46Z | research record | analysis/research/research-records.md | Research tagged GENERIC | Medium | Validate against current evidence |
| 2026-06-07T20:42:59Z | RAG record | analysis/rag-records.md | Retrieved memory tagged GENERIC | Medium | Validate or reject with live evidence |
| 2026-06-07T20:45:45Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-07T20:45:45Z | evaluator | analysis/evaluator-20260607T204545709868Z-0129454e.md | Proceed | High | Run python3 Quantum/Phase-Madness/solve/solve.py, then challenge_harness.py capture-flag Quantum/Phase-Madness --from loot/flag-candidate.txt. |
| 2026-06-07T20:51:21Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-07T20:52:08Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- Extracted source is a Qiskit service in
files/extracted/quantum_phase_madness/server.py. - The service encodes one flag byte per qubit as an integer-degree rotation:
- i % 3 == 0: RX(byte)
- i % 3 == 1: RY(byte)
- i % 3 == 2: H then RZ(byte)
- The user can repeatedly choose a qubit, apply integer-degree
RX/RY/RZgates, and receive 100000-shot measurement counts. - The source-backed solve path is single-qubit tomography, not brute force.
- Local formula self-test passed in
analysis/solver-self-test.txt. - Remote prompt check passed in
analysis/remote-prompt-check.txt. - Do not send an empty instruction string during solving; use
RZ:0,<q>as identity because the empty branch mutatesbase_circuit. - The wrapped solver recovered 79 bytes and wrote a candidate with the expected
HTB{prefix toloot/flag-candidate.txt. - Raw remote measurement evidence is preserved in
analysis/phase-madness-counts.json; the file contains per-qubit counts, derived trigonometric estimates, and reconstructed byte values without duplicating the flag into notes.
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: Quantum
- Challenge: Phase-Madness
- 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.
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 | Recover each flag byte as a rotation angle using repeated single-qubit measurement/tomography queries, then reconstruct the HTB flag bytes. | server.py encodes one flag byte per qubit using RX/RY/H+RZ, accepts arbitrary RX/RY/RZ integer rotations before measuring one selected qubit with 100000 shots. | Use source-derived formulas on a few locally simulated states or send one non-mutating remote measurement query to confirm prompt/count format. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Memory Summary
approval_required: true
Sanitized Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: Quantum
- Challenge: Phase-Madness
- 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.
Reusable Lessons
-
Dead Ends
-
Tool Quirks
-
Evidence Paths
-
Ingestion Decision
- Proposed for LightRAG: yes/no
- Requires user approval before ingestion: yes
Notes
Notes
Scope
- Challenge: Phase-Madness
- Category: Quantum
- Difficulty: Easy
- Mode: hybrid
- Remote instance: <TARGET>:32125
- Start time: 2026-06-07T20:40:37Z
- 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/a12c738d-8675-425e-af82-a6cfdfbcebd6.zip | 1517 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 2 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-07T20:40:37Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-07T20:40:42Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-07T20: <REDACTED>, then reconstruct the HTB flag bytes. | Medium | Use source-derived formulas on a few locally simulated states or send one non-mutating remote measurement query to confirm prompt/count format. | |||
| 2026-06-07T20:41:54Z | local memory search | analysis/research/local-memory-search-20260607T204154966928Z-48abf12d.md | Found 5 safe prior-note result(s) | Medium | Record useful result or skip |
| 2026-06-07T20:41:54Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-07T20:42:04Z | checkpoint recorded | analysis/checkpoint-triage-20260607T204204687218Z-10dca62c.md | Checkpoint for TRIAGE | High | Use checkpoint to drive next decision |
| 2026-06-07T20:42:37Z | RAG query | analysis/rag/rag-query-20260607T204222376201Z-bdc74b35.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-07T20:42:46Z | research record | analysis/research/research-records.md | Research tagged GENERIC | Medium | Validate against current evidence |
| 2026-06-07T20:42:59Z | RAG record | analysis/rag-records.md | Retrieved memory tagged GENERIC | Medium | Validate or reject with live evidence |
| 2026-06-07T20:45:45Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-07T20: <REDACTED>, then challenge_harness.py capture-flag Quantum/Phase-Madness --from loot/flag-candidate.txt. | |||||
| 2026-06-07T20: <REDACTED> | |||||
| 2026-06-07T20:52:08Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- Extracted source is a Qiskit service in
files/extracted/quantum_phase_madness/server.py. - The service encodes one flag byte per qubit as an integer-degree rotation:
- i % 3 == 0: RX(byte)
- i % 3 == 1: RY(byte)
- i % 3 == 2: H then RZ(byte)
- The user can repeatedly choose a qubit, apply integer-degree
RX/RY/RZgates, and receive 100000-shot measurement counts. - The source-backed solve path is single-qubit tomography, not brute force.
- Local formula self-test passed in
analysis/solver-self-test.txt. - Remote prompt check passed in
analysis/remote-prompt-check.txt. - Do not send an empty instruction string during solving; use
RZ:0,<q>as identity because the empty branch mutatesbase_circuit. - The wrapped solver recovered 79 bytes and wrote a candidate with the expected
<<secret redacted>>prefix toloot/flag-candidate.txt. - Raw remote measurement evidence is preserved in
analysis/phase-madness-counts.json; the file contains per-qubit counts, derived trigonometric estimates, and reconstructed byte values without duplicating the flag into notes.
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.
Technical analogy
How to remember this solve
Think of the quantum circuit like a fragile sequence of switches. The solve is about arranging the switches so the final measurement lands in the one state that unlocks the flag.
For Phase Madness, 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.