Challenge / Quantum

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

EasyPublished 2025-05-19Sanitized local writeup

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.

Phase Madness sanitized attack graph

Walkthrough flow

01

Circuit constraints

02

State manipulation

03

Measurement strategy

04

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.

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

  • 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.py
  • analysis/source-review-quick.txt
  • analysis/formula-validation.md
  • analysis/solver-self-test.txt
  • analysis/phase-madness-counts.json
  • solve/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: H then RZ(byte)

The service then allows repeated interactive queries:

  1. choose a qubit index
  2. apply semicolon-separated one-qubit RX / RY / RZ instructions with integer degree parameters
  3. 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 indexQuery 1Query 2Recovery
q % 3 == 0RZ:0,q gives cos(theta)RX:90,q gives -sin(theta)atan2(-z2, z1)
q % 3 == 1RZ:0,q gives cos(theta)RY:90,q gives -sin(theta)atan2(-z2, z1)
q % 3 == 2RY:-90,q gives cos(theta)RX:90,q gives sin(theta)atan2(z2, z1)

For each query:

text
z = (count("0") - count("1")) / total_shots

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

bash
cd <local workspace>
python3 scripts/challenge_exec.py Quantum/Phase-Madness -- \
  python3 Quantum/Phase-Madness/solve/solve.py

The solver:

  1. opens one TCP connection to the challenge
  2. queries qubits in order starting at 0
  3. performs the correct two non-empty measurements for each qubit class
  4. stops when the service reports the qubit index is out of range
  5. reconstructs the byte string
  6. writes the candidate to loot/flag-candidate.txt
  7. 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

FileSizeSHA256TypeNotes
files/a12c738d-8675-425e-af82-a6cfdfbcebd6.zip1517<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 2 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-07T20:40:37Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-07T20:40:42Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-07T20:41:54Zhypothesis recordedhypothesis-board.mdRecover each flag byte as a rotation angle using repeated single-qubit measurement/tomography queries, then reconstruct the HTB flag bytes.MediumUse 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:54Zlocal memory searchanalysis/research/local-memory-search-20260607T204154966928Z-48abf12d.mdFound 5 safe prior-note result(s)MediumRecord useful result or skip
2026-06-07T20:41:54Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-07T20:42:04Zcheckpoint recordedanalysis/checkpoint-triage-20260607T204204687218Z-10dca62c.mdCheckpoint for TRIAGEHighUse checkpoint to drive next decision
2026-06-07T20:42:37ZRAG queryanalysis/rag/rag-query-20260607T204222376201Z-bdc74b35.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-07T20:42:46Zresearch recordanalysis/research/research-records.mdResearch tagged GENERICMediumValidate against current evidence
2026-06-07T20:42:59ZRAG recordanalysis/rag-records.mdRetrieved memory tagged GENERICMediumValidate or reject with live evidence
2026-06-07T20:45:45Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-07T20:45:45Zevaluatoranalysis/evaluator-20260607T204545709868Z-0129454e.mdProceedHighRun 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:21Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-07T20:52:08Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional 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/RZ gates, 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 mutates base_circuit.
  • The wrapped solver recovered 79 bytes and wrote a candidate with the expected HTB{ prefix to loot/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:

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

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Recover 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.MediumActive

Closed Branches

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

FileSizeSHA256TypeNotes
files/a12c738d-8675-425e-af82-a6cfdfbcebd6.zip1517<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 2 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-07T20:40:37Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-07T20:40:42Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-07T20: <REDACTED>, then reconstruct the HTB flag bytes.MediumUse 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:54Zlocal memory searchanalysis/research/local-memory-search-20260607T204154966928Z-48abf12d.mdFound 5 safe prior-note result(s)MediumRecord useful result or skip
2026-06-07T20:41:54Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-07T20:42:04Zcheckpoint recordedanalysis/checkpoint-triage-20260607T204204687218Z-10dca62c.mdCheckpoint for TRIAGEHighUse checkpoint to drive next decision
2026-06-07T20:42:37ZRAG queryanalysis/rag/rag-query-20260607T204222376201Z-bdc74b35.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-07T20:42:46Zresearch recordanalysis/research/research-records.mdResearch tagged GENERICMediumValidate against current evidence
2026-06-07T20:42:59ZRAG recordanalysis/rag-records.mdRetrieved memory tagged GENERICMediumValidate or reject with live evidence
2026-06-07T20:45:45Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate 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:08Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional 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/RZ gates, 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 mutates base_circuit.
  • The wrapped solver recovered 79 bytes and wrote a candidate with the expected <<secret redacted>> prefix to loot/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:

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.

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.