ctf-agent 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/AGENTS.md +131 -0
  2. package/Dockerfile +192 -0
  3. package/README.md +522 -0
  4. package/agents/ctf-analyzer.md +58 -0
  5. package/agents/ctf-controller.md +151 -0
  6. package/agents/ctf-speedrun.md +74 -0
  7. package/bin/ctf-agent.js +119 -0
  8. package/docker-compose.yml +13 -0
  9. package/mcp_config.json +19 -0
  10. package/package.json +44 -0
  11. package/references/ctf-triage-ladder.md +68 -0
  12. package/references/exploit-databases.md +575 -0
  13. package/references/llm-safety-and-policy-compliance.md +173 -0
  14. package/references/multi-agent-orchestration-and-policy-routing.md +278 -0
  15. package/references/security-events-and-intelligence.md +566 -0
  16. package/references/version-matrix.md +59 -0
  17. package/rules/ctf-execution-rules.md +44 -0
  18. package/rules/ctf-safety-framing-rules.md +140 -0
  19. package/rules/flag-validation-rules.md +25 -0
  20. package/scripts/__init__.py +1 -0
  21. package/scripts/ctf-tools.lock +205 -0
  22. package/scripts/ctf_init.py +1026 -0
  23. package/scripts/ctf_update.py +583 -0
  24. package/scripts/ctfd_client.py +81 -0
  25. package/scripts/cve_lookup.py +453 -0
  26. package/scripts/extract_flags.py +155 -0
  27. package/scripts/install_as_agent.py +278 -0
  28. package/scripts/install_ctf_tools.sh +603 -0
  29. package/scripts/parallel_triage.py +395 -0
  30. package/scripts/prompt_policy_sanitizer.py +283 -0
  31. package/scripts/scope_guard.py +444 -0
  32. package/scripts/skill_validator.py +504 -0
  33. package/scripts/workspace_cleaner.py +141 -0
  34. package/skills/ctf-ai-ml/SKILL.md +120 -0
  35. package/skills/ctf-ai-ml/adversarial-ml.md +623 -0
  36. package/skills/ctf-ai-ml/llm-attacks.md +487 -0
  37. package/skills/ctf-ai-ml/model-attacks.md +422 -0
  38. package/skills/ctf-crypto/SKILL.md +320 -0
  39. package/skills/ctf-crypto/advanced-math.md +798 -0
  40. package/skills/ctf-crypto/classic-ciphers.md +651 -0
  41. package/skills/ctf-crypto/ecc-attacks.md +347 -0
  42. package/skills/ctf-crypto/exotic-crypto-2.md +380 -0
  43. package/skills/ctf-crypto/exotic-crypto.md +528 -0
  44. package/skills/ctf-crypto/historical.md +113 -0
  45. package/skills/ctf-crypto/lattice-and-lwe.md +524 -0
  46. package/skills/ctf-crypto/modern-ciphers-2.md +563 -0
  47. package/skills/ctf-crypto/modern-ciphers-3.md +453 -0
  48. package/skills/ctf-crypto/modern-ciphers.md +649 -0
  49. package/skills/ctf-crypto/prng-attacks.md +257 -0
  50. package/skills/ctf-crypto/prng.md +664 -0
  51. package/skills/ctf-crypto/rsa-attacks-2.md +792 -0
  52. package/skills/ctf-crypto/rsa-attacks.md +487 -0
  53. package/skills/ctf-crypto/stream-ciphers.md +390 -0
  54. package/skills/ctf-crypto/zkp-and-advanced.md +456 -0
  55. package/skills/ctf-forensics/3d-printing.md +121 -0
  56. package/skills/ctf-forensics/SKILL.md +379 -0
  57. package/skills/ctf-forensics/disk-advanced.md +497 -0
  58. package/skills/ctf-forensics/disk-and-memory.md +491 -0
  59. package/skills/ctf-forensics/disk-recovery.md +699 -0
  60. package/skills/ctf-forensics/linux-forensics.md +511 -0
  61. package/skills/ctf-forensics/network-advanced.md +583 -0
  62. package/skills/ctf-forensics/network.md +645 -0
  63. package/skills/ctf-forensics/peripheral-capture.md +287 -0
  64. package/skills/ctf-forensics/signals-and-hardware.md +713 -0
  65. package/skills/ctf-forensics/steganography.md +694 -0
  66. package/skills/ctf-forensics/stego-advanced-2.md +475 -0
  67. package/skills/ctf-forensics/stego-advanced.md +481 -0
  68. package/skills/ctf-forensics/stego-image.md +691 -0
  69. package/skills/ctf-forensics/windows.md +625 -0
  70. package/skills/ctf-malware/SKILL.md +181 -0
  71. package/skills/ctf-malware/c2-and-protocols.md +274 -0
  72. package/skills/ctf-malware/pe-and-dotnet.md +108 -0
  73. package/skills/ctf-malware/scripts-and-obfuscation.md +449 -0
  74. package/skills/ctf-misc/SKILL.md +498 -0
  75. package/skills/ctf-misc/bashjails.md +323 -0
  76. package/skills/ctf-misc/ctfd-navigation.md +465 -0
  77. package/skills/ctf-misc/dns.md +255 -0
  78. package/skills/ctf-misc/encodings-advanced.md +504 -0
  79. package/skills/ctf-misc/encodings.md +431 -0
  80. package/skills/ctf-misc/games-and-vms-2.md +254 -0
  81. package/skills/ctf-misc/games-and-vms-3.md +690 -0
  82. package/skills/ctf-misc/games-and-vms-4.md +229 -0
  83. package/skills/ctf-misc/games-and-vms.md +529 -0
  84. package/skills/ctf-misc/linux-privesc.md +333 -0
  85. package/skills/ctf-misc/pyjails.md +671 -0
  86. package/skills/ctf-misc/rf-sdr.md +91 -0
  87. package/skills/ctf-osint/SKILL.md +198 -0
  88. package/skills/ctf-osint/geolocation-and-media.md +464 -0
  89. package/skills/ctf-osint/social-media.md +312 -0
  90. package/skills/ctf-osint/web-and-dns.md +341 -0
  91. package/skills/ctf-pwn/SKILL.md +214 -0
  92. package/skills/ctf-pwn/advanced-exploits-2.md +579 -0
  93. package/skills/ctf-pwn/advanced-exploits-3.md +598 -0
  94. package/skills/ctf-pwn/advanced-exploits-4.md +590 -0
  95. package/skills/ctf-pwn/advanced-exploits-5.md +119 -0
  96. package/skills/ctf-pwn/advanced-exploits.md +773 -0
  97. package/skills/ctf-pwn/advanced.md +326 -0
  98. package/skills/ctf-pwn/field-notes.md +245 -0
  99. package/skills/ctf-pwn/format-string.md +694 -0
  100. package/skills/ctf-pwn/heap-fsop.md +285 -0
  101. package/skills/ctf-pwn/heap-techniques-2.md +333 -0
  102. package/skills/ctf-pwn/heap-techniques.md +513 -0
  103. package/skills/ctf-pwn/kernel-bypass.md +421 -0
  104. package/skills/ctf-pwn/kernel-techniques.md +366 -0
  105. package/skills/ctf-pwn/kernel.md +636 -0
  106. package/skills/ctf-pwn/overflow-basics.md +611 -0
  107. package/skills/ctf-pwn/rop-advanced.md +725 -0
  108. package/skills/ctf-pwn/rop-and-shellcode.md +659 -0
  109. package/skills/ctf-pwn/sandbox-escape.md +313 -0
  110. package/skills/ctf-reverse/SKILL.md +163 -0
  111. package/skills/ctf-reverse/anti-analysis-ctf.md +204 -0
  112. package/skills/ctf-reverse/anti-analysis.md +693 -0
  113. package/skills/ctf-reverse/field-notes.md +376 -0
  114. package/skills/ctf-reverse/languages-compiled.md +666 -0
  115. package/skills/ctf-reverse/languages-platforms.md +592 -0
  116. package/skills/ctf-reverse/languages.md +553 -0
  117. package/skills/ctf-reverse/patterns-ctf-2.md +397 -0
  118. package/skills/ctf-reverse/patterns-ctf-3.md +797 -0
  119. package/skills/ctf-reverse/patterns-ctf.md +670 -0
  120. package/skills/ctf-reverse/patterns-runtime.md +274 -0
  121. package/skills/ctf-reverse/patterns.md +572 -0
  122. package/skills/ctf-reverse/platforms-hardware.md +387 -0
  123. package/skills/ctf-reverse/platforms.md +664 -0
  124. package/skills/ctf-reverse/tools-advanced-2.md +421 -0
  125. package/skills/ctf-reverse/tools-advanced.md +407 -0
  126. package/skills/ctf-reverse/tools-dynamic.md +679 -0
  127. package/skills/ctf-reverse/tools-emulation.md +319 -0
  128. package/skills/ctf-reverse/tools.md +573 -0
  129. package/skills/ctf-web/SKILL.md +153 -0
  130. package/skills/ctf-web/auth-and-access-2.md +82 -0
  131. package/skills/ctf-web/auth-and-access.md +783 -0
  132. package/skills/ctf-web/auth-infra.md +321 -0
  133. package/skills/ctf-web/auth-jwt.md +186 -0
  134. package/skills/ctf-web/client-side-advanced.md +739 -0
  135. package/skills/ctf-web/client-side.md +529 -0
  136. package/skills/ctf-web/cves.md +373 -0
  137. package/skills/ctf-web/field-notes.md +482 -0
  138. package/skills/ctf-web/node-and-prototype.md +200 -0
  139. package/skills/ctf-web/server-side-2.md +337 -0
  140. package/skills/ctf-web/server-side-advanced-2.md +559 -0
  141. package/skills/ctf-web/server-side-advanced-3.md +125 -0
  142. package/skills/ctf-web/server-side-advanced-4.md +480 -0
  143. package/skills/ctf-web/server-side-advanced.md +378 -0
  144. package/skills/ctf-web/server-side-deser.md +443 -0
  145. package/skills/ctf-web/server-side-exec-2.md +799 -0
  146. package/skills/ctf-web/server-side-exec.md +457 -0
  147. package/skills/ctf-web/server-side.md +629 -0
  148. package/skills/ctf-web/sql-injection.md +790 -0
  149. package/skills/ctf-web/web3.md +374 -0
  150. package/skills/ctf-writeup/SKILL.md +90 -0
  151. package/skills/solve-challenge/SKILL.md +269 -0
  152. package/skills.json +16 -0
@@ -0,0 +1,481 @@
1
+ # CTF Forensics - Advanced Steganography
2
+
3
+ See also: [stego-advanced-2.md](stego-advanced-2.md) for video frame techniques, JPEG XL TOC permutation, Arnold's Cat Map, SSTV FM demodulation, MJPEG steganography, EXIF/Stegano pixel patterns, PDF xref covert channels, ANSI escape code stego, and ECB image recovery.
4
+
5
+ ## Table of Contents
6
+ - [FFT Frequency Domain Steganography (Pragyan 2026)](#fft-frequency-domain-steganography-pragyan-2026)
7
+ - [SSTV Red Herring + LSB Audio Stego (0xFun 2026)](#sstv-red-herring--lsb-audio-stego-0xfun-2026)
8
+ - [DotCode Barcode via SSTV (0xFun 2026)](#dotcode-barcode-via-sstv-0xfun-2026)
9
+ - [DTMF Audio Decoding](#dtmf-audio-decoding)
10
+ - [Custom Frequency DTMF / Dual-Tone Keypad Encoding (EHAX 2026)](#custom-frequency-dtmf--dual-tone-keypad-encoding-ehax-2026)
11
+ - [Multi-Track Audio Differential Subtraction (EHAX 2026)](#multi-track-audio-differential-subtraction-ehax-2026)
12
+ - [Cross-Channel Multi-Bit LSB Steganography (ApoorvCTF 2026)](#cross-channel-multi-bit-lsb-steganography-apoorvctf-2026)
13
+ - [Audio FFT Musical Note Identification (BYPASS CTF 2025)](#audio-fft-musical-note-identification-bypass-ctf-2025)
14
+ - [Audio Metadata Octal Encoding (BYPASS CTF 2025)](#audio-metadata-octal-encoding-bypass-ctf-2025)
15
+ - [Nested Tar Archive with Whitespace Encoding (UTCTF 2026)](#nested-tar-archive-with-whitespace-encoding-utctf-2026)
16
+ - [DeepSound Audio Steganography with Password Cracking (INShAck 2018)](#deepsound-audio-steganography-with-password-cracking-inshack-2018)
17
+ - [Audio Waveform Binary Encoding (BackdoorCTF 2013)](#audio-waveform-binary-encoding-backdoorctf-2013)
18
+ - [Audio Spectrogram Hidden QR Code (BaltCTF 2013)](#audio-spectrogram-hidden-qr-code-baltctf-2013)
19
+ - [Byte-Reversed .docx ZIP Bidirectional Archive (Security Fest CTF 2018)](#byte-reversed-docx-zip-bidirectional-archive-security-fest-ctf-2018)
20
+ - [MIDI Note-On/Note-Off Pitch Pair Encoding (X-MAS CTF 2018)](#midi-note-onnote-off-pitch-pair-encoding-x-mas-ctf-2018)
21
+
22
+ ---
23
+
24
+ ## FFT Frequency Domain Steganography (Pragyan 2026)
25
+
26
+ **Pattern (H@rDl4u6H):** Image encodes data in frequency domain via 2D FFT.
27
+
28
+ **Decoding workflow:**
29
+ ```python
30
+ import numpy as np
31
+ from PIL import Image
32
+
33
+ img = np.array(Image.open("image.png")).astype(float)
34
+ F = np.fft.fftshift(np.fft.fft2(img))
35
+ mag = np.log(1 + np.abs(F))
36
+
37
+ # Look for patterns: concentric rings, dots at specific positions
38
+ # Bright peak = 0 bit, Dark (no peak) = 1 bit
39
+ cy, cx = mag.shape[0]//2, mag.shape[1]//2
40
+ radii = [100 + 69*i for i in range(21)] # Example spacing
41
+ angles = [0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5]
42
+ THRESHOLD = 13.0
43
+
44
+ bits = []
45
+ for r in radii:
46
+ byte_val = 0
47
+ for a in angles:
48
+ fx = cx + r * np.cos(np.radians(a))
49
+ fy = cy - r * np.sin(np.radians(a))
50
+ bit = 0 if mag[int(round(fy)), int(round(fx))] > THRESHOLD else 1
51
+ byte_val = (byte_val << 1) | bit
52
+ bits.append(byte_val)
53
+ ```
54
+
55
+ **Identification:** Challenge mentions "transform", poem about "frequency", or image looks blank/noisy. Try FFT visualization first.
56
+
57
+ ---
58
+
59
+ ## SSTV Red Herring + LSB Audio Stego (0xFun 2026)
60
+
61
+ **Pattern (Melodie):** WAV contains SSTV signal (Scottie 1) that decodes to "SEEMS LIKE A DEADEND". Real flag in 2-bit LSB of audio samples.
62
+
63
+ ```bash
64
+ # Decode SSTV (red herring)
65
+ qsstv # Will show decoy message
66
+
67
+ # Extract real flag from LSB
68
+ pip install stego-lsb
69
+ stegolsb wavsteg -r -i audio.wav -o out.bin -n 2 -b 1000
70
+ ```
71
+
72
+ **Lesson:** Obvious signals may be decoys. Always check LSB even when another encoding is found.
73
+
74
+ ---
75
+
76
+ ## DotCode Barcode via SSTV (0xFun 2026)
77
+
78
+ **Pattern (Dots):** SSTV decoding produces dot pattern image. Not QR — it's DotCode format.
79
+
80
+ **Identification:** Dot pattern that isn't a standard QR code. DotCode is a 2D barcode optimized for high-speed printing.
81
+
82
+ **Tool:** Aspose online DotCode reader (free).
83
+
84
+ ---
85
+
86
+ ## DTMF Audio Decoding
87
+
88
+ **Pattern (Phone Home):** Audio file contains phone dialing tones encoding data.
89
+
90
+ ```bash
91
+ # Decode DTMF tones
92
+ sox phonehome.wav -t raw -r 22050 -e signed-integer -b 16 -c 1 - | \
93
+ multimon-ng -t raw -a DTMF -
94
+ ```
95
+
96
+ **Post-processing:** Phone number may contain octal-encoded ASCII after delimiter (#):
97
+ ```python
98
+ # Convert octal groups to ASCII
99
+ octal_groups = ["115", "145", "164", "141"] # M, e, t, a
100
+ flag = ''.join(chr(int(g, 8)) for g in octal_groups)
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Custom Frequency DTMF / Dual-Tone Keypad Encoding (EHAX 2026)
106
+
107
+ **Pattern (Quantum Message):** Audio with dual-tone sequences at non-standard frequencies, aligned at regular intervals (e.g., every 1 second). Hints about "harmonic oscillators" or physics point to custom frequency design.
108
+
109
+ **Identification:** Spectrogram shows two distinct frequency sets that don't match standard DTMF (697-1633 Hz). Look for evenly-spaced rows/columns of frequency tones.
110
+
111
+ **Decoding workflow:**
112
+ ```python
113
+ import numpy as np
114
+ from scipy.io import wavfile
115
+
116
+ rate, audio = wavfile.read('challenge.wav')
117
+
118
+ # 1. Generate spectrogram to identify frequency grid
119
+ # Use ffmpeg: ffmpeg -i challenge.wav -lavfi showspectrumpic=s=1920x1080 spec.png
120
+
121
+ # 2. Map frequencies to keypad (custom grid, NOT standard DTMF)
122
+ # Example: rows = [301, 902, 1503, 2104] Hz, cols = [2705, 3306, 3907] Hz
123
+ # Forms 4x3 keypad -> digits 0-9 + symbols
124
+
125
+ # 3. Extract tone pairs per time window
126
+ window_size = rate # 1 second per symbol
127
+ for i in range(0, len(audio), window_size):
128
+ segment = audio[i:i+window_size]
129
+ freqs = np.fft.rfftfreq(len(segment), 1/rate)
130
+ magnitude = np.abs(np.fft.rfft(segment))
131
+ # Find two dominant peaks -> map to row/col -> digit
132
+
133
+ # 4. Convert digit sequence to ASCII
134
+ # Split digits into variable-length groups (ASCII range 32-126)
135
+ # E.g., "72101108108111" -> [72, 101, 108, 108, 111] -> "Hello"
136
+ def digits_to_ascii(digits):
137
+ result, i = [], 0
138
+ while i < len(digits):
139
+ for length in [2, 3]: # ASCII codes are 2-3 digits
140
+ if i + length <= len(digits):
141
+ val = int(digits[i:i+length])
142
+ if 32 <= val <= 126:
143
+ result.append(chr(val))
144
+ i += length
145
+ break
146
+ else:
147
+ i += 1
148
+ return ''.join(result)
149
+ ```
150
+
151
+ **Key insight:** When tones don't match standard DTMF frequencies, generate a spectrogram first to identify the custom frequency grid. The mapping is challenge-specific.
152
+
153
+ ---
154
+
155
+ ## Multi-Track Audio Differential Subtraction (EHAX 2026)
156
+
157
+ **Pattern (Penguin):** MKV/video file with two nearly-identical audio tracks. Hidden data is embedded as a tiny difference between the tracks, invisible when listening to either individually.
158
+
159
+ **Identification:**
160
+ - `ffprobe` reveals multiple audio streams (e.g., two stereo FLAC tracks)
161
+ - Metadata may contain a decoy flag (e.g., in comments)
162
+ - Track labels may be misleading (e.g., stereo labeled as "5.1 surround")
163
+ - `sox --info` / `sox -n stat` shows nearly identical RMS, amplitude, and frequency statistics for both tracks
164
+
165
+ **Extraction workflow:**
166
+ ```bash
167
+ # 1. Extract both audio tracks
168
+ ffmpeg -i challenge.mkv -map 0:a:0 -c copy track0.flac
169
+ ffmpeg -i challenge.mkv -map 0:a:1 -c copy track1.flac
170
+
171
+ # 2. Convert to WAV for processing
172
+ ffmpeg -i track0.flac track0.wav
173
+ ffmpeg -i track1.flac track1.wav
174
+
175
+ # 3. Subtract: invert one track and mix (cancels shared content)
176
+ sox -m track0.wav "|sox track1.wav -p vol -1" diff.wav
177
+
178
+ # 4. Normalize the difference signal
179
+ sox diff.wav diff_norm.wav gain -n -3
180
+
181
+ # 5. Generate spectrogram to read the flag
182
+ sox diff_norm.wav -n spectrogram -o spectrogram.png -X 2000 -Y 1000 -z 100 -h
183
+
184
+ # 6. Optional: filter to isolate flag frequency range
185
+ sox diff_norm.wav filtered.wav sinc 5000-12000
186
+ sox filtered.wav -n spectrogram -o filtered_spec.png -X 2000 -Y 1000 -z 100 -h
187
+ ```
188
+
189
+ **Key insight:** When two audio tracks are nearly identical, subtracting one from the other (phase inversion + mix) cancels shared content and isolates hidden data. The flag is typically encoded as text in the spectrogram of the difference signal, visible in a specific frequency band (e.g., 5-12 kHz).
190
+
191
+ **Common traps:**
192
+ - Decoy flags in metadata/comments — always verify
193
+ - Mislabeled channel configurations (stereo as 5.1)
194
+ - Flag may only be visible in a narrow time window — use high-resolution spectrogram (`-X 2000+`)
195
+
196
+ ---
197
+
198
+ ## Cross-Channel Multi-Bit LSB Steganography (ApoorvCTF 2026)
199
+
200
+ **Pattern (Beneath the Armor):** Standard LSB tools (zsteg, stegsolve) fail because different bit positions are used per RGB channel: Red channel bit 0, Green channel bit 1, Blue channel bit 2.
201
+
202
+ ```python
203
+ from PIL import Image
204
+
205
+ img = Image.open("challenge.png")
206
+ pixels = img.load()
207
+ bits = []
208
+ for y in range(img.height):
209
+ for x in range(img.width):
210
+ r, g, b = pixels[x, y][:3]
211
+ bits.append((r >> 0) & 1) # Red: bit 0
212
+ bits.append((g >> 1) & 1) # Green: bit 1
213
+ bits.append((b >> 2) & 1) # Blue: bit 2
214
+
215
+ # Pack 3 bits per pixel into bytes
216
+ data = bytearray()
217
+ for i in range(0, len(bits) - 7, 8):
218
+ byte = 0
219
+ for j in range(8):
220
+ byte = (byte << 1) | bits[i + j]
221
+ data.append(byte)
222
+ print(data.decode('ascii', errors='ignore'))
223
+ ```
224
+
225
+ **Key insight:** When standard LSB tools find nothing, the data may use different bit positions per channel. The hint "cycles" or "modular" suggests cycling through bit positions (0→1→2) across channels. Always try non-standard bit combinations: R[0]G[1]B[2], R[1]G[2]B[0], R[2]G[0]B[1], etc.
226
+
227
+ **Detection:** Standard `zsteg -a` and `stegsolve` produce no results on an image that metadata hints contain hidden data.
228
+
229
+ ---
230
+
231
+ ## Audio FFT Musical Note Identification (BYPASS CTF 2025)
232
+
233
+ **Pattern (Piano):** Identify dominant frequencies via FFT (Fast Fourier Transform), map to musical notes (A-G), then read the letter names as a word.
234
+
235
+ **Technique:** Perform FFT on audio, identify dominant frequencies, map to musical notes.
236
+
237
+ ```python
238
+ import numpy as np
239
+ from scipy.io import wavfile
240
+
241
+ rate, audio = wavfile.read('challenge.wav')
242
+ if audio.ndim > 1:
243
+ audio = audio[:, 0] # mono
244
+
245
+ # FFT to find dominant frequencies
246
+ freqs = np.fft.rfftfreq(len(audio), 1/rate)
247
+ magnitude = np.abs(np.fft.rfft(audio))
248
+
249
+ # Find top peaks
250
+ peak_indices = np.argsort(magnitude)[-20:]
251
+ peak_freqs = sorted(set(round(freqs[i]) for i in peak_indices if freqs[i] > 20))
252
+
253
+ # Musical note frequency mapping (A4 = 440 Hz)
254
+ NOTE_FREQS = {
255
+ 'C4': 261.63, 'D4': 293.66, 'E4': 329.63, 'F4': 349.23,
256
+ 'G4': 392.00, 'A4': 440.00, 'B4': 493.88,
257
+ 'C5': 523.25, 'D5': 587.33, 'E5': 659.25, 'F5': 698.46,
258
+ 'G5': 783.99, 'A5': 880.00, 'B5': 987.77,
259
+ }
260
+
261
+ def freq_to_note(freq):
262
+ return min(NOTE_FREQS.items(), key=lambda x: abs(x[1] - freq))[0]
263
+
264
+ notes = [freq_to_note(f) for f in peak_freqs]
265
+ # Extract letter names: B, A, D, F, A, C, E → "BADFACE"
266
+ answer = ''.join(n[0] for n in notes)
267
+ print(f"Notes: {notes}")
268
+ print(f"Answer: {answer}")
269
+ ```
270
+
271
+ **Extract and examine audio metadata** using `exiftool audio.mp3` for encoded hints in comment fields (e.g., octal-separated values → base64 → decoded hint).
272
+
273
+ **Key insight:** Musical note names (A-G) can spell words. When a challenge involves music/piano, identify dominant frequencies via FFT and read the note letter names as text.
274
+
275
+ ---
276
+
277
+ ## Audio Metadata Octal Encoding (BYPASS CTF 2025)
278
+
279
+ **Pattern (Piano metadata):** Audio file metadata (exiftool comment field) contains underscore-separated numbers representing octal-encoded ASCII values (digits 0-7 only).
280
+
281
+ ```python
282
+ # Extract and decode octal metadata
283
+ import subprocess, base64
284
+
285
+ # Get metadata comment
286
+ comment = "103_137_63_157_144_145_144_40_162_145_154_151_143"
287
+ octal_values = comment.split('_')
288
+ decoded = ''.join(chr(int(v, 8)) for v in octal_values)
289
+
290
+ # May decode to base64, requiring another layer
291
+ result = base64.b64decode(decoded).decode()
292
+ print(result)
293
+ ```
294
+
295
+ **Key insight:** When metadata contains underscore-separated numbers, try octal (digits 0-7 only), decimal, or hex interpretation. Multi-layer encoding (octal → base64 → plaintext) is common.
296
+
297
+ ---
298
+
299
+ ## Nested Tar Archive with Whitespace Encoding (UTCTF 2026)
300
+
301
+ **Pattern (Silent Archive):** Deeply nested tar archives where data is encoded in whitespace characters (spaces, tabs, newlines) within file names or content.
302
+
303
+ **Detection:** Archive extracts to another archive (tar-in-tar chain). File content appears empty but contains invisible whitespace characters.
304
+
305
+ **Decoding workflow:**
306
+ ```python
307
+ import tarfile
308
+ import os
309
+
310
+ # 1. Recursively extract nested tar archives
311
+ def extract_all(path, depth=0):
312
+ if depth > 100: # Guard against infinite nesting
313
+ return
314
+ if tarfile.is_tarfile(path):
315
+ with tarfile.open(path) as tf:
316
+ tf.extractall(f'layer_{depth}')
317
+ for member in tf.getmembers():
318
+ extract_all(f'layer_{depth}/{member.name}', depth + 1)
319
+
320
+ # 2. Collect whitespace from file names or content
321
+ whitespace_data = []
322
+ for root, dirs, files in os.walk('layer_0'):
323
+ for f in files:
324
+ path = os.path.join(root, f)
325
+ with open(path, 'rb') as fh:
326
+ content = fh.read()
327
+ # Check for whitespace-only content
328
+ if content.strip() == b'':
329
+ for byte in content:
330
+ if byte == 0x20: # space
331
+ whitespace_data.append('0')
332
+ elif byte == 0x09: # tab
333
+ whitespace_data.append('1')
334
+
335
+ # 3. Convert binary from whitespace
336
+ bits = ''.join(whitespace_data)
337
+ message = bytes(int(bits[i:i+8], 2) for i in range(0, len(bits)-7, 8))
338
+ print(message.decode(errors='replace'))
339
+ ```
340
+
341
+ **Whitespace encoding variants:**
342
+ - Space = 0, Tab = 1 (binary encoding)
343
+ - Whitespace Steganography: trailing spaces/tabs at end of lines
344
+ - Zero-width characters (U+200B, U+200C, U+FEFF) in Unicode text
345
+ - Number of spaces between words encodes data
346
+
347
+ **Key insight:** "Silent" or "invisible" hints point to whitespace encoding. Use `xxd` or `cat -A` to reveal hidden whitespace characters. Deeply nested archives are misdirection — the data is in the whitespace, not the nesting depth.
348
+
349
+ ---
350
+
351
+ ## DeepSound Audio Steganography with Password Cracking (INShAck 2018)
352
+
353
+ **Pattern:** Two-phase audio steganography: part 1 visible in Audacity spectrogram, part 2 hidden with DeepSound tool (password-protected). Use `deepsound2john.py` to extract the hash, crack with John, then retrieve hidden files.
354
+
355
+ ```bash
356
+ # Phase 1: Check spectrogram for visible text
357
+ sox audio.wav -n spectrogram -o spec.png
358
+
359
+ # Phase 2: Extract DeepSound password hash
360
+ python3 deepsound2john.py audio.wav > hash.txt
361
+
362
+ # Crack password
363
+ john --wordlist=rockyou.txt hash.txt
364
+
365
+ # Extract hidden file with DeepSound GUI or CLI using cracked password
366
+ ```
367
+
368
+ **DeepSound detection:**
369
+ ```python
370
+ # DeepSound embeds a signature in WAV files
371
+ # Check for DeepSound header pattern in audio data
372
+ with open('audio.wav', 'rb') as f:
373
+ data = f.read()
374
+ # DeepSound uses specific byte patterns in the audio data section
375
+ # deepsound2john.py from John the Ripper's bleeding-jumbo branch
376
+ # handles detection and hash extraction automatically
377
+ ```
378
+
379
+ **Tool installation:**
380
+ ```bash
381
+ # deepsound2john.py is part of John the Ripper bleeding-jumbo
382
+ git clone https://github.com/openwall/john.git
383
+ # Script located at: john/run/deepsound2john.py
384
+
385
+ # DeepSound GUI (Windows): http://jpinsoft.net/deepsound/
386
+ # For Linux: run under Wine or use the extracted hash + john approach
387
+ ```
388
+
389
+ **Key insight:** DeepSound embeds files in WAV audio with optional AES encryption. The password hash is extractable with `deepsound2john.py` from John the Ripper's bleeding-jumbo branch. Always check both spectrogram (visual stego) and DeepSound (data stego) in audio challenges.
390
+
391
+ **Detection:** WAV file that seems normal but `deepsound2john.py` produces a hash. Challenge has two-part structure where first part is easy (spectrogram) and second part requires a tool. Challenge mentions "layers", "hidden", or "deep".
392
+
393
+ ---
394
+
395
+ ## Audio Waveform Binary Encoding (BackdoorCTF 2013)
396
+
397
+ **Pattern:** WAV file contains two distinct waveform shapes representing binary 0 and 1. Group 8 bits into bytes and decode as ASCII.
398
+
399
+ ```python
400
+ import wave, struct
401
+ wf = wave.open('audio.wav', 'rb')
402
+ frames = wf.readframes(wf.getnframes())
403
+ samples = struct.unpack(f'{len(frames)//2}h', frames)
404
+
405
+ # Identify two distinct wave patterns (e.g., positive peak vs flat)
406
+ # Segment audio into fixed-length windows, classify each as 0 or 1
407
+ bits = ''
408
+ window = len(samples) // num_bits
409
+ for i in range(num_bits):
410
+ segment = samples[i*window:(i+1)*window]
411
+ bits += '1' if max(segment) > threshold else '0'
412
+
413
+ # Decode binary to ASCII
414
+ flag = ''.join(chr(int(bits[i:i+8], 2)) for i in range(0, len(bits)-7, 8))
415
+ ```
416
+
417
+ **Key insight:** Open in Audacity and zoom in — two visually distinct wave patterns alternate. Each pattern represents one bit. Count the patterns, group into 8-bit bytes, decode as ASCII.
418
+
419
+ ---
420
+
421
+ ## Audio Spectrogram Hidden QR Code (BaltCTF 2013)
422
+
423
+ **Pattern:** Audio file contains visual data hidden in the frequency domain, visible only in a spectrogram view.
424
+
425
+ ```bash
426
+ # Generate spectrogram image
427
+ sox audio.mp3 -n spectrogram -o spec.png
428
+ # Or use Sonic Visualiser for interactive exploration
429
+
430
+ # Look for visual patterns in specific frequency bands (often 5-12 kHz)
431
+ # Extract/assemble QR code fragments from spectrogram
432
+ # Scan with: zbarimg assembled_qr.png
433
+ ```
434
+
435
+ **Key insight:** Use Sonic Visualiser (Layer → Add Spectrogram) with adjustable window size and color mapping. QR codes or text often appear in the 2-15 kHz band. Multiple spectrogram fragments may need to be stitched together in an image editor before scanning.
436
+
437
+ ---
438
+
439
+ ## Byte-Reversed .docx ZIP Bidirectional Archive (Security Fest CTF 2018)
440
+
441
+ **Pattern (Zion):** Distributed file is a valid `.docx` (ZIP archive). Extract it normally and you see only a decoy document. Reverse the entire file byte-for-byte and the result is *also* a valid ZIP archive — containing a second `word/media/*.png` whose contents are the flag.
442
+
443
+ **Extraction:**
444
+ ```bash
445
+ # Verify the forward archive
446
+ unzip -l doc.docx
447
+
448
+ # Reverse the byte stream and unpack the mirror archive
449
+ python3 -c "import sys;sys.stdout.buffer.write(open('doc.docx','rb').read()[::-1])" > mirror.zip
450
+ unzip -l mirror.zip
451
+ unzip mirror.zip 'word/media/*' -d mirror/
452
+ ```
453
+
454
+ **Why both directions succeed:** ZIP's central directory sits at the end of the archive and the local file headers are parsed only via offsets in that directory. By placing a second set of local headers at the *start* of the file, and a matching central directory at the very end after reversing, the file satisfies the ZIP specification in both reading orders. Python's `zipfile` and `unzip -l` read the central directory from the tail, so they happily open whichever end is presented first.
455
+
456
+ **Key insight:** Always test container files for byte-reversal, bit-reversal, and byte-interleaving when the forward extraction yields only a decoy. Run `binwalk` on both the forward and reversed copies to surface embedded archives hidden in either direction. The trick generalizes to any format whose parser tolerates trailing garbage (ZIP, RAR, PDF, tar).
457
+
458
+ **References:** Security Fest CTF 2018 — writeup 10204
459
+
460
+ ---
461
+
462
+ ## MIDI Note-On/Note-Off Pitch Pair Encoding (X-MAS CTF 2018)
463
+
464
+ **Pattern:** MIDI file plays nothing recognisable but has a strict alternating Note-On/Note-Off pattern. The hidden message is split one byte per *pair*: `ord(char) = note_on_pitch + note_off_pitch`. Sometimes encoded as high/low nibble: `ord(char) = (on << 4) | off`.
465
+
466
+ ```python
467
+ import mido
468
+ mid = mido.MidiFile('hidden.mid')
469
+ ons, offs = [], []
470
+ for ev in mid.tracks[0]:
471
+ if ev.type == 'note_on': ons.append(ev.note)
472
+ elif ev.type == 'note_off': offs.append(ev.note)
473
+ flag = ''.join(chr(o + f) for o, f in zip(ons, offs))
474
+ ```
475
+
476
+ **Key insight:** MIDI pitch values are 7-bit (0..127), so any byte can be split across two notes. When a MIDI sounds "atonal" but obeys strict note-pair alternation, test `on + off`, `(on<<4)|off`, `off - on`, and XOR combinations before assuming audio stego.
477
+
478
+ **References:** X-MAS CTF 2018 — A Christmas Carol, writeup 12667
479
+
480
+ ---
481
+