Short Term Fuel Trim
Short Term Fuel Trim
Platform: tamuctf | Category: Steganography | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-03-21 | Status: Solved Techniques: audio_reconstruction, inverse_stft, speech_to_text
Summary
Task: Text file with complex numbers representing STFT coefficients (Short-Time Fourier Transform). Solution: Inverse STFT to reconstruct audio, then speech recognition with Whisper to transcribe the spoken flag.
Short Term Fuel Trim — TAMUctf
Description
Some stupid numbers or something. Not really sure what they're for, maybe you can figure it out?
NOTE: The flag is fully lowercase!
Files: short-term-fuel-trim.tar.gz containing numbers.txt
The archive contains a 9.7MB text file with 178,021 lines of complex numbers.
Analysis
Initial Recon
Extracting the archive reveals numbers.txt — a large file with complex numbers:
# STFT shape: complex64 (129, 1380)
(0.0017089844+0j)
(-0.0010986328-0.00048828125j)
(0.00048828125+0.00024414062j)
...
Key observations:
- First line is a comment revealing the data structure:
STFT shape: complex64 (129, 1380) - STFT = Short-Time Fourier Transform — the task name "Short Term Fuel Trim" is a clever wordplay!
- Shape (129, 1380) means 129 frequency bins and 1380 time frames
- 129 bins suggests
nperseg=256(since 256/2+1 = 129) - 178,020 complex numbers = 129 * 1380 = 177,720 (matches!)
Understanding STFT
The Short-Time Fourier Transform converts a time-domain signal into frequency-domain representation over time. Given STFT coefficients, we can reconstruct the original audio using the inverse STFT (iSTFT).
Parameters deduced:
nperseg = 256(window size, giving 129 frequency bins)fs = 22050Hz (common sample rate for speech)- Duration: ~8 seconds
Solution
Step 1: Parse Complex Numbers
python read-only • copy allowed#!/usr/bin/env python3 """ Inverse STFT to reconstruct audio from frequency-domain data """ import numpy as np from scipy.signal import istft import scipy.io.wavfile as wavfile # Parse complex numbers from file with open('numbers.txt', 'r') as f: lines = f.readlines() # Skip comment line, parse complex numbers complex_nums = [] for line in lines[1:]: # skip "# STFT shape: complex64 (129, 1380)" line = line.strip().lstrip('(').rstrip(')') if line: complex_nums.append(complex(line.replace(' ', ''))) print(f"Parsed {len(complex_nums)} complex numbers") # Output: Parsed 177720 complex numbers
Step 2: Reconstruct Audio with Inverse STFT
python read-only • copy allowed# Reshape to STFT matrix (129 freq bins x 1380 time frames) stft_matrix = np.array(complex_nums, dtype=np.complex64).reshape(129, 1380) # Perform inverse STFT # fs=22050 is standard for speech, nperseg=256 gives 129 bins t, x = istft(stft_matrix, fs=22050, nperseg=256) print(f"Reconstructed audio: {len(x)} samples, {len(x)/22050:.2f} seconds") # Output: Reconstructed audio: 176896 samples, 8.02 seconds # Normalize and save as WAV x_normalized = x / np.max(np.abs(x)) * 0.9 x_int16 = (x_normalized * 32767).astype(np.int16) wavfile.write('output.wav', 22050, x_int16)
Step 3: Spectrogram Analysis
python read-only • copy allowedimport matplotlib.pyplot as plt # Generate spectrogram to visualize plt.figure(figsize=(14, 6)) plt.specgram(x, Fs=22050, NFFT=1024, noverlap=512, cmap='viridis') plt.xlabel('Time (s)') plt.ylabel('Frequency (Hz)') plt.title('Reconstructed Audio Spectrogram') plt.colorbar(label='Intensity (dB)') plt.savefig('spectrogram.png', dpi=150) plt.show()
The spectrogram shows typical speech formant patterns — this is spoken audio, not visual steganography.
Step 4: Speech Recognition with Whisper
python read-only • copy allowedimport whisper # Load Whisper model model = whisper.load_model("medium") # Transcribe result = model.transcribe("output.wav") print(result["text"])
Whisper output:
G I G E M left curly bracket FFT underscore IS underscore five zero underscore zero P right curly bracket
Decoding the Flag
The spoken text spells out the flag character by character:
- "G I G E M" →
gigem - "left curly bracket" →
{ - "FFT underscore IS underscore" →
fft_is_ - "five zero underscore zero P" →
50_0p - "right curly bracket" →
}
Leet speak interpretation: 50_0p = "so op" (5=s, 0=o), meaning "FFT is so OP" (overpowered)!
Complete Solution Script
python read-only • copy allowed#!/usr/bin/env python3 """ TAMUctf 2026 - Short Term Fuel Trim Inverse STFT audio reconstruction + speech recognition """ import numpy as np from scipy.signal import istft import scipy.io.wavfile as wavfile # Parse complex numbers with open('numbers.txt', 'r') as f: lines = f.readlines() complex_nums = [] for line in lines[1:]: line = line.strip().lstrip('(').rstrip(')') if line: complex_nums.append(complex(line.replace(' ', ''))) # Reshape and inverse STFT stft_matrix = np.array(complex_nums, dtype=np.complex64).reshape(129, 1380) t, x = istft(stft_matrix, fs=22050, nperseg=256) # Save WAV x_normalized = x / np.max(np.abs(x)) * 0.9 x_int16 = (x_normalized * 32767).astype(np.int16) wavfile.write('output.wav', 22050, x_int16) print("Audio saved to output.wav") print("Use Whisper or any speech recognition to transcribe:") print(" whisper output.wav --model medium") # Alternative: use whisper directly try: import whisper model = whisper.load_model("base") result = model.transcribe("output.wav") print(f"\nTranscription: {result['text']}") except ImportError: print("\nInstall whisper: pip install openai-whisper")
Theory
Short-Time Fourier Transform (STFT)
STFT divides a signal into short overlapping segments and computes the FFT of each segment, producing a 2D time-frequency representation:
- Frequency bins:
nperseg/2 + 1(for real input) - Time frames: depends on signal length and hop size
- Output: complex numbers representing magnitude and phase
Inverse STFT (iSTFT)
Given STFT coefficients, the original signal can be reconstructed using overlap-add synthesis. The scipy.signal.istft() function handles this automatically.
Common Parameters
| Parameter | Typical Values | Notes |
|---|---|---|
| fs | 22050, 44100 | Sample rate (Hz) |
| nperseg | 256, 512, 1024 | Window size |
| noverlap | nperseg/2 | 50% overlap is common |
References
- scipy.signal.istft documentation
- Short-Time Fourier Transform
- OpenAI Whisper