Challenge / Hardware

RFlag

RFlag is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator

EasyPublished 2024-10-18Sanitized local writeup

Scenario

RFlag attack path

RFlag 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 Hardware evidence, validation, and reusable operator lessons.

RFlag sanitized attack graph

Walkthrough flow

01

Treat .cf32 as little-endian complex float32 IQ...

02

Compute magnitude squared to distinguish idle noise...

03

Threshold as OOK/ASK and run-length encode high/low...

04

Estimate bit-cell timing from short runs, then expand...

05

Manchester decode with phase 0 and convention 01=0,...

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.

  • Hardware/RFlag/writeup.md
  • htb-challenge/Hardware/RFlag/notes.md
  • htb-challenge/Hardware/RFlag/memory-summary.md
  • htb-challenge/Hardware/RFlag/hypothesis-board.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Hardware__RFlag__memory-summary.md.e663273744.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Hardware__RFlag__notes.md.d110ce8a5f.md

Technical Walkthrough

Writeup

Challenge

  • Name: RFlag
  • Category: Hardware
  • Difficulty: Easy
  • Mode: file

Summary

RFlag is a file-only SDR signal-analysis challenge. The provided signal.cf32 capture is an OOK/ASK signal from a garage remote. Decoding required thresholding the IQ magnitude, reducing the waveform to short/long runs, expanding those runs into bit cells, and applying Manchester decoding. The decoded byte stream contains an HTB-format flag after a short preamble.

Artifact Inventory

  • files/a12c7374-3809-4b8e-8b0a-9bdcf9be63fd.zip: original HTB archive, preserved unmodified.
  • files/extracted/hardware_rflag/signal.cf32: complex-float IQ capture, 3,809,280 bytes, 476,160 complex samples. Evidence: analysis/artifact-inventory.json, analysis/unzip.txt, analysis/sha256-extracted.txt.

Analysis

The file extension and challenge scenario indicated SDR IQ data. The first pass treated it as complex float32 samples. Magnitude statistics showed two strong populations: idle/noise samples near magnitude squared 0.001, and active carrier samples near magnitude squared 2.0. That supports OOK/ASK rather than FSK as the first decode path. Evidence: analysis/signal-basic-stats.txt.

Thresholding at magnitude squared 0.1 produced a clean pulse train. The run lengths clustered around two durations: roughly 900 samples and 1800 samples. Evidence: analysis/ook-runs.txt.

Direct high/low pair PWM was tested and rejected because all four pair classes (SS, SL, LS, LL) appeared frequently. The correct interpretation was run-length expansion: short runs are one bit cell, long runs are two bit cells. Evidence: analysis/ook-symbols.txt, analysis/runlength-decode-attempts.txt.

After run-length expansion, Manchester decoding with phase 0 and convention 01=0, 10=1 produced a byte stream with a preamble and an HTB-format flag beginning at decoded bit offset 48. Including the trailing low run was necessary to recover the final byte; trimming it produced only a partial flag. Evidence: analysis/manchester-full-decode.txt.

Solve

Run:

bash
cd <local workspace>
python3 solve/solve.py --output loot/flag-candidate.txt

The solver:

  1. Parses the cf32 file as little-endian float32 I/Q pairs.
  2. Computes magnitude squared and thresholds it as OOK.
  3. Estimates the bit-cell unit from short pulse lengths.
  4. Expands each run by round(length / unit).
  5. Manchester decodes phase 0 using 01=0, 10=1.
  6. Searches byte alignments for an HTB-format flag and writes it to loot/.

The harness captured the generated candidate with:

bash
cd <local workspace>
./scripts/challenge_harness.py capture-flag Hardware/RFlag --from loot/flag-candidate.txt

Flag

Raw flag is stored in loot/flag.txt and intentionally not reproduced here.

Lessons

  • For SDR captures, inspect magnitude statistics before assuming a protocol decoder is required.
  • OOK remotes may need run-length expansion before Manchester decoding.
  • Do not trim trailing idle blindly; the final low run can carry the second half of the last Manchester bit.

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: RFlag
  • Category: Hardware
  • Difficulty: Easy
  • Mode: file
  • Remote instance: none
  • Start time: 2026-06-09T12:38:54Z
  • 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/a12c7374-3809-4b8e-8b0a-9bdcf9be63fd.zip370674<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-09T12:38:54Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-09T12:39:07Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-09T12:39:45Zhypothesis recordedhypothesis-board.mdDecode the cf32 RF capture as OOK/ASK remote-key amplitude pulsesMediumLoad complex float32 samples, inspect magnitude envelope, threshold high/low pulses, infer symbol timing, then decode bits/ASCII.
2026-06-09T12:42:34Zresearch taskanalysis/research/task-20260609T124234161757Z-c7ef9d75.mdResearch task created for advisory investigationMediumRecord research output
2026-06-09T12:42:35Zresearch recordanalysis/research/research-records.mdResearch tagged MATCHEDMediumValidate against current evidence
2026-06-09T12:43:32Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-09T12:46:02Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • The ZIP contains a single complex-float IQ capture: files/extracted/hardware_rflag/signal.cf32; see analysis/unzip.txt.
  • The signal is OOK/ASK-like: low noise has magnitude squared around 0.001, while active carrier samples cluster near 2.0; see analysis/signal-basic-stats.txt.
  • Thresholding at magnitude squared 0.1 yields short and long runs near 900 and 1800 samples; see analysis/ook-runs.txt.
  • Pair-level PWM was not the right decode because SS, SL, LS, and LL run pairs all occur; see analysis/ook-symbols.txt.
  • Expanding high/low runs at the 900-sample unit and Manchester decoding with 01=0, 10=1, phase 0 reveals an HTB-format flag at decoded bit offset 48. The trailing low run is required to recover the closing byte; see sanitized evidence in analysis/manchester-full-decode.txt.
  • solve/solve.py reproduces the full decode and writes the raw flag only to loot/.

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: Hardware
  • Challenge: RFlag
  • 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. Treat .cf32 as little-endian complex float32 IQ samples.
  2. Compute magnitude squared to distinguish idle noise from active carrier.
  3. Threshold as OOK/ASK and run-length encode high/low regions.
  4. Estimate bit-cell timing from short runs, then expand long runs into repeated bit cells.
  5. Manchester decode with phase 0 and convention 01=0, 10=1.
  6. Search decoded byte alignments for the HTB-format flag; include the trailing low run so the final byte is complete.

Reusable Lessons

  • Hardware SDR captures can often be solved with simple envelope thresholding before specialized tools like urh or rtl_433.
  • If pair-level PWM has all SS, SL, LS, and LL classes, try run-length expansion before Manchester decoding.
  • Avoid trimming all trailing idle; the last low run may contain part of the final Manchester symbol.

Dead Ends

  • Direct raw expanded ASCII was noisy and did not produce a clean flag.
  • Pair-level PWM classification was not sufficient because all four pair classes appeared.

Tool Quirks

  • numpy, scipy, matplotlib, rtl_433, and urh were unavailable locally, but standard-library Python was enough for this capture.

Evidence Paths

  • analysis/signal-basic-stats.txt
  • analysis/ook-runs.txt
  • analysis/ook-symbols.txt
  • analysis/runlength-decode-attempts.txt
  • analysis/manchester-full-decode.txt
  • solve/solve.py
  • loot/flag.txt

Ingestion Decision

  • Proposed for LightRAG: yes
  • 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
1Decode the cf32 RF capture as OOK/ASK remote-key amplitude pulsesArtifact is named signal.cf32 and the scenario says an SDR captured a garage remote key signal.Load complex float32 samples, inspect magnitude envelope, threshold high/low pulses, infer symbol timing, then decode bits/ASCII.MediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit Condition

Memory Summary

approval_required: true

Sanitized Memory Summary

Metadata

  • Platform: HackTheBox Challenges
  • Category: Hardware
  • Challenge: RFlag
  • 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. Treat .cf32 as little-endian complex float32 IQ samples.
  2. Compute magnitude squared to distinguish idle noise from active carrier.
  3. Threshold as OOK/ASK and run-length encode high/low regions.
  4. Estimate bit-cell timing from short runs, then expand long runs into repeated bit cells.
  5. Manchester decode with phase 0 and convention 01=0, 10=1.
  6. Search decoded byte alignments for the HTB-format flag; include the trailing low run so the final byte is complete.

Reusable Lessons

  • Hardware SDR captures can often be solved with simple envelope thresholding before specialized tools like urh or rtl_433.
  • If pair-level PWM has all SS, SL, LS, and LL classes, try run-length expansion before Manchester decoding.
  • Avoid trimming all trailing idle; the last low run may contain part of the final Manchester symbol.

Dead Ends

  • Direct raw expanded ASCII was noisy and did not produce a clean flag.
  • Pair-level PWM classification was not sufficient because all four pair classes appeared.

Tool Quirks

  • numpy, scipy, matplotlib, rtl_433, and urh were unavailable locally, but standard-library Python was enough for this capture.

Evidence Paths

  • analysis/signal-basic-stats.txt
  • analysis/ook-runs.txt
  • analysis/ook-symbols.txt
  • analysis/runlength-decode-attempts.txt
  • analysis/manchester-full-decode.txt
  • solve/solve.py
  • loot/flag.txt

Ingestion Decision

  • Proposed for LightRAG: yes
  • Requires user approval before ingestion: yes

Notes

Notes

Scope

  • Challenge: RFlag
  • Category: Hardware
  • Difficulty: Easy
  • Mode: file
  • Remote instance: none
  • Start time: 2026-06-09T12:38:54Z
  • 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/a12c7374-3809-4b8e-8b0a-9bdcf9be63fd.zip370674<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-09T12:38:54Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-09T12:39:07Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-09T12:39:45Zhypothesis recordedhypothesis-board.mdDecode the cf32 RF capture as OOK/ASK remote-key amplitude pulsesMediumLoad complex float32 samples, inspect magnitude envelope, threshold high/low pulses, infer symbol timing, then decode bits/ASCII.
2026-06-09T12:42:34Zresearch taskanalysis/research/task-20260609T124234161757Z-c7ef9d75.mdResearch task created for advisory investigationMediumRecord research output
2026-06-09T12:42:35Zresearch recordanalysis/research/research-records.mdResearch tagged MATCHEDMediumValidate against current evidence
2026-06-09T12: <REDACTED>
2026-06-09T12:46:02Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • The ZIP contains a single complex-float IQ capture: files/extracted/hardware_rflag/signal.cf32; see analysis/unzip.txt.
  • The signal is OOK/ASK-like: low noise has magnitude squared around 0.001, while active carrier samples cluster near 2.0; see analysis/signal-basic-stats.txt.
  • Thresholding at magnitude squared 0.1 yields short and long runs near 900 and 1800 samples; see analysis/ook-runs.txt.
  • Pair-level PWM was not the right decode because SS, SL, LS, and LL run pairs all occur; see analysis/ook-symbols.txt.
  • Expanding high/low runs at the 900-sample unit and Manchester decoding with 01=0, 10=1, phase 0 reveals an HTB-format flag at decoded bit offset 48. The trailing low run is required to recover the closing byte; see sanitized evidence in analysis/manchester-full-decode.txt.
  • solve/solve.py reproduces the full decode and writes the raw flag only to loot/.

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 hardware challenge like following copper tracks on a circuit board. The useful clue is usually where signals enter, where they are transformed, and which debug or storage path exposes hidden state.

For RFlag, 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.