RFlag
hackthebox
We have found the garage where some cyber criminals have all their stuff. Using an SDR device, we captured the signal from the remote key that opens the garage. Can you help us to analyze it?
$ ls tags/ techniques/
RFlag — HackTheBox
Description
We have found the garage where some cyber criminals have all their stuff. Using an SDR device, we captured the signal from the remote key that opens the garage. Can you help us to analyze it?
Files provided:
signal.cf32- A radio signal capture file (Complex Float 32-bit format, 3.8MB, 476160 samples)
Analysis
File Format
The .cf32 file is a Complex Float 32-bit format commonly used by SDR (Software Defined Radio) tools like GNU Radio. Each sample consists of two 32-bit floats representing:
- I (In-phase) component
- Q (Quadrature) component
This is the standard IQ format for representing radio signals in the digital domain.
Signal Characteristics
After loading the samples:
- Total samples: 476,160 complex samples
- Magnitude range: 0.0 to 1.414
- Mean magnitude: 0.66
- Median magnitude: 0.05
The bimodal distribution (low median, higher mean) immediately suggests OOK (On-Off Keying) modulation - the signal is either "on" (high amplitude) or "off" (low amplitude).
Modulation Identification
Applying a threshold of 0.5 to the magnitude revealed a clear binary pattern:
- 185 rising edges and 185 falling edges
- Two distinct pulse widths: ~900 samples (short) and ~1800 samples (long)
- The 1:2 ratio is characteristic of Manchester encoding
Solution
Step 1: Load and Demodulate
#!/usr/bin/env python3 import numpy as np # Load complex samples samples = np.fromfile('signal.cf32', dtype=np.complex64) print(f"Loaded {len(samples)} samples") # Convert to amplitude (OOK demodulation) magnitude = np.abs(samples) # Apply threshold for binary conversion threshold = 0.5 binary_signal = (magnitude > threshold).astype(int)
Step 2: Extract Pulse Widths
# Find transitions transitions = [] for i in range(1, len(binary_signal)): if binary_signal[i] != binary_signal[i-1]: transitions.append(i) # Calculate pulse widths pulse_widths = [] for i in range(1, len(transitions)): width = transitions[i] - transitions[i-1] pulse_widths.append(width) # Analyze: short pulses ~900, long pulses ~1800 short_threshold = 1350 # midpoint between 900 and 1800
Step 3: Convert to Bit Stream
# Convert pulse widths to unit-based representation bit_stream = "" for i, width in enumerate(pulse_widths): # Determine if pulse is short (1 unit) or long (2 units) units = 1 if width < short_threshold else 2 # Get the signal level during this pulse level = binary_signal[transitions[i] + 1] # Append bits based on units and level bit_stream += str(level) * units print(f"Bit stream: {bit_stream[:100]}...")
Step 4: Manchester Decoding
def manchester_decode(bits): """ Manchester decoding: - '01' -> '0' - '10' -> '1' """ decoded = [] for i in range(0, len(bits) - 1, 2): pair = bits[i:i+2] if pair == "01": decoded.append("0") elif pair == "10": decoded.append("1") else: # Invalid Manchester pair - skip or handle error pass return "".join(decoded) decoded_bits = manchester_decode(bit_stream) print(f"Decoded: {decoded_bits[:64]}...")
Step 5: Extract Flag
# Skip preamble (synchronization pattern: 10101010...) # Find where preamble ends (first non-alternating pattern) preamble_end = 32 # Typically 32 bits of preamble data_bits = decoded_bits[preamble_end:] # Convert to bytes flag_bytes = [] for i in range(0, len(data_bits) - 7, 8): byte = int(data_bits[i:i+8], 2) if 32 <= byte <= 126: # Printable ASCII flag_bytes.append(chr(byte)) flag = "".join(flag_bytes) print(f"Flag: {flag}")
Complete Solution Script
#!/usr/bin/env python3 """ RFlag - HackTheBox Hardware/RF Challenge SDR signal analysis with OOK demodulation and Manchester decoding """ import numpy as np def solve_rflag(filename='signal.cf32'): # Load complex IQ samples samples = np.fromfile(filename, dtype=np.complex64) # OOK demodulation - convert to amplitude magnitude = np.abs(samples) # Binary threshold threshold = 0.5 binary = (magnitude > threshold).astype(int) # Find transitions transitions = [0] for i in range(1, len(binary)): if binary[i] != binary[i-1]: transitions.append(i) transitions.append(len(binary)) # Extract pulse widths and convert to bit stream bit_stream = "" short_threshold = 1350 for i in range(len(transitions) - 1): width = transitions[i+1] - transitions[i] level = binary[transitions[i]] units = 1 if width < short_threshold else 2 bit_stream += str(level) * units # Manchester decode decoded = "" for i in range(0, len(bit_stream) - 1, 2): pair = bit_stream[i:i+2] if pair == "01": decoded += "0" elif pair == "10": decoded += "1" # Skip preamble and extract ASCII data = decoded[32:] # Skip 32-bit preamble result = "" for i in range(0, len(data) - 7, 8): byte = int(data[i:i+8], 2) if 32 <= byte <= 126: result += chr(byte) return result if __name__ == "__main__": flag = solve_rflag() print(f"Flag: {flag}")
Key Indicators
Use this technique when you see:
.cf32,.iq,.rawfiles from SDR captures- Complex float samples (I/Q data)
- Bimodal amplitude distribution (suggests OOK)
- Pulse widths with 1:2 ratio (suggests Manchester encoding)
- Preamble patterns like
10101010...(synchronization)
Concepts Explained
OOK (On-Off Keying)
Simple modulation where the carrier is either present (1) or absent (0). Common in:
- Garage door remotes
- Car key fobs
- Simple wireless sensors
Manchester Encoding
Self-clocking encoding where:
0is represented as low-to-high transition (01)1is represented as high-to-low transition (10)
Advantages:
- Clock recovery from data
- No DC component
- Error detection (invalid pairs)
Complex Float 32 (CF32)
Standard SDR format:
- Each sample = 8 bytes (2 x 32-bit floats)
- First float = I (in-phase)
- Second float = Q (quadrature)
- Represents signal as complex number: I + jQ
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar
Similar writeups
- [stego][Pro]Radio Signal— hackerlab
- [hardware][Pro]FM Hunting— ASIS CTF
- [ml][free]AI SPACE— hackthebox
- [crypto][Pro]Cyber— volgactf
- [forensics][Pro]Чувак, где мой флаг? (Dude, Where's My Flag?)— hackerlab