$ cat writeup.md…
$ cat writeup.md…
HackTheBox
"Encrypting data with discrete values is all very well and good, but there'll always be a finite number of outputs, and I hate anything you can brute force. That's why I use continuous values to store my flags! It's just... quite hard to get them back again..."
"Encrypting data with discrete values is all very well and good, but there'll always be a finite number of outputs, and I hate anything you can brute force. That's why I use continuous values to store my flags! It's just... quite hard to get them back again..."
Files: encrypt.py, encrypted.wav
The encrypt.py script encodes each flag character as a sine wave and sums them into a single WAV file:
import numpy as np from scipy.io.wavfile import write from secret import flag N = 1_000_000 T = .0001 x = np.linspace(0.0, N*T, N, endpoint=False) final_waveform = 0 count_used = dict() for i, c in enumerate(flag): count_used[c] = count_used.get(c, 0) + 1 multiplier = .1 * c * (4**(count_used[c]-1)) final_waveform += (i+1) * np.sin(2 * np.pi * x * multiplier) write("encrypted.wav", 20_000_000, final_waveform)
For each character c at position i:
| Parameter | Formula | Purpose |
|---|---|---|
| Frequency | 0.1 * ASCII(c) * 4^(occurrence - 1) | Unique frequency for each character; repeated characters get exponentially higher frequencies (x4 each time) |
| Amplitude | i + 1 | Character position in the flag (1-indexed) |
| Signal | amplitude * sin(2*pi*freq*x) | Standard sine wave |
The resulting WAV = sum of 39 sine waves (one per flag character).
This is a classic inverse Fourier transform problem: sum of sine waves -> FFT -> individual frequency components with amplitudes. FFT is the exact mathematical inverse of the encoding process.
Apply Fast Fourier Transform to the WAV file to decompose the summed signal into individual sinusoidal components:
fft_result = np.fft.rfft(data) freqs = np.fft.rfftfreq(N, d=T) magnitudes = np.abs(fft_result)
Use scipy.signal.find_peaks with a threshold of 0.1% of maximum magnitude to identify all 39 significant frequency peaks:
threshold = np.max(magnitudes) * 0.001 peak_indices, _ = find_peaks(magnitudes, height=threshold, distance=3)
For each peak, extract:
A * N / 2. Therefore position = round(magnitude * 2 / N) - 1 (0-indexed).ascii_val = freq / (0.1 * 4^(count-1)). If the result rounds to a valid printable ASCII (32-126), that's our character.#!/usr/bin/env python3 """ Noisy Solver - HackTheBox FFT decomposition of summed sine waves to recover flag characters """ import numpy as np from scipy.io.wavfile import read from scipy.signal import find_peaks rate, data = read("encrypted.wav") N = len(data) T = 0.0001 # FFT decomposition fft_result = np.fft.rfft(data) freqs = np.fft.rfftfreq(N, d=T) magnitudes = np.abs(fft_result) # Find all significant peaks threshold = np.max(magnitudes) * 0.001 peak_indices, _ = find_peaks(magnitudes, height=threshold, distance=3) # Decode each peak -> (position, character) flag_chars = {} for idx in peak_indices: freq = freqs[idx] mag = magnitudes[idx] if freq <= 0: continue # Amplitude -> position (FFT magnitude of sin with amp A over N samples = A*N/2) position = round(mag * 2 / N) - 1 # Frequency -> character (try occurrence counts 1, 2, 3, ...) for count in range(1, 20): ascii_val = freq / (0.1 * (4 ** (count - 1))) rounded_ascii = round(ascii_val) if abs(ascii_val - rounded_ascii) < 0.5 and 32 <= rounded_ascii <= 126: flag_chars[position] = chr(rounded_ascii) break # Reconstruct flag flag_len = max(flag_chars.keys()) + 1 flag = ''.join(flag_chars.get(i, '?') for i in range(flag_len)) print(f"Flag: {flag}")
pos= 0: 'H' pos= 1: 'T' pos= 2: 'B' pos= 3: '{' pos= 4: 'm'
pos= 5: 'Y' pos= 6: '_' pos= 7: 'f' pos= 8: '4' pos= 9: 'v'
pos=10: '0' pos=11: 'U' pos=12: 'R' pos=13: '1' pos=14: 't'
pos=15: '3' pos=16: '_' pos=17: 't' pos=18: 'r' pos=19: '4'
pos=20: 'n' pos=21: 'S' pos=22: 'F' pos=23: '0' pos=24: 'r'
pos=25: 'M' pos=26: '_' pos=27: '-' pos=28: '_' pos=29: 'f'
pos=30: '0' pos=31: 'u' pos=32: 'r' pos=33: 'I' pos=34: 'E'
pos=35: 'r' pos=36: '!' pos=37: '!' pos=38: '}'
Discrete Fourier Transform (DFT/FFT) decomposes a signal into a sum of sine waves with specific frequencies and amplitudes. This is the exact inverse of the encoding process:
Encoding: characters -> sine waves -> sum (WAV)
Decoding: WAV -> FFT -> frequencies + amplitudes -> characters
For a sine wave A * sin(2*pi*f*t) with N samples:
A * N / 2A = magnitude * 2 / NThe multiplier 4^(occurrence-1) ensures that each occurrence of the same character has a unique frequency:
0.1 * 114 * 1 = 11.40.1 * 114 * 4 = 45.60.1 * 114 * 16 = 182.4This prevents peak overlap in the frequency spectrum.
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar