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,274 @@
1
+ # CTF Reverse - Runtime Patching and Oracle Techniques
2
+
3
+ Malware unpacking, multi-stage shellcode, timing/signal side channels, and CTF-specific oracle attacks that rely on runtime state rather than static pattern matching.
4
+
5
+ For static reversing patterns (custom VMs, anti-debug, self-modifying code, LLVM obfuscation, S-box generation, SECCOMP/BPF, memory dumps, x86-64 gotchas, byte-wise transforms), see [patterns.md](patterns.md).
6
+
7
+ ## Table of Contents
8
+ - [Malware Anti-Analysis Bypass via Patching](#malware-anti-analysis-bypass-via-patching)
9
+ - [Multi-Stage Shellcode Loaders](#multi-stage-shellcode-loaders)
10
+ - [Timing Side-Channel Attack](#timing-side-channel-attack)
11
+ - [Multi-Thread Anti-Debug with Decoy + Signal Handler Mixed Boolean-Arithmetic (ApoorvCTF 2026)](#multi-thread-anti-debug-with-decoy--signal-handler-mixed-boolean-arithmetic-apoorvctf-2026)
12
+ - [INT3 Patch + Coredump Brute-Force Oracle (Pwn2Win 2016)](#int3-patch--coredump-brute-force-oracle-pwn2win-2016)
13
+ - [Signal Handler Chain + LD_PRELOAD Oracle (Nuit du Hack 2016)](#signal-handler-chain--ld_preload-oracle-nuit-du-hack-2016)
14
+ - [printf Format String VM Decompilation to Z3 (SECCON 2017)](#printf-format-string-vm-decompilation-to-z3-seccon-2017)
15
+ - [Quadtree Recursive Image Format Parser (Google CTF Quals 2018)](#quadtree-recursive-image-format-parser-google-ctf-quals-2018)
16
+
17
+ ---
18
+
19
+ ## Malware Anti-Analysis Bypass via Patching
20
+
21
+ **Pattern (Carrot):** Malware with multiple environment checks before executing payload.
22
+
23
+ **Common checks to patch:**
24
+ | Check | Technique | Patch |
25
+ |-------|-----------|-------|
26
+ | `ptrace(PTRACE_TRACEME)` | Anti-debug | Change `cmp -1` to `cmp 0` |
27
+ | `sleep(150)` | Anti-sandbox timing | Change sleep value to 1 |
28
+ | `/proc/cpuinfo` "hypervisor" | Anti-VM | Flip `JNZ` to `JZ` |
29
+ | "VMware"/"VirtualBox" strings | Anti-VM | Flip `JNZ` to `JZ` |
30
+ | `getpwuid` username check | Environment | Flip comparison |
31
+ | `LD_PRELOAD` check | Anti-hook | Skip check |
32
+ | Fan count / hardware check | Anti-VM | Flip `JLE` to `JGE` |
33
+ | Hostname check | Environment | Flip `JNZ` to `JZ` |
34
+
35
+ **Ghidra patching workflow:**
36
+ 1. Find check function, identify the conditional jump
37
+ 2. Click on instruction → `Ctrl+Shift+G` → modify opcode
38
+ 3. For `JNZ` (0x75) → `JZ` (0x74), or vice versa
39
+ 4. For immediate values: change operand bytes directly
40
+ 5. Export: press `O` → choose "Original File" format
41
+ 6. `chmod +x` the patched binary
42
+
43
+ **Server-side validation bypass:**
44
+ - If patched binary sends system info to remote server, patch the data too
45
+ - Modify string addresses in data-gathering functions
46
+ - Change format strings to embed correct values directly
47
+
48
+ ---
49
+
50
+ ## Multi-Stage Shellcode Loaders
51
+
52
+ **Pattern (I Heard You Liked Loaders):** Nested shellcode with XOR decode loops and anti-debug.
53
+
54
+ **Debugging workflow:**
55
+ 1. Break at `call rax` in launcher, step into shellcode
56
+ 2. Bypass ptrace anti-debug: step to syscall, `set $rax=0`
57
+ 3. Step through XOR decode loop (or break on `int3` if hidden)
58
+ 4. Repeat for each stage until final payload
59
+
60
+ **Flag extraction from `mov` instructions:**
61
+ ```python
62
+ # Final stage loads flag 4 bytes at a time via mov ebx, value
63
+ # Extract little-endian 4-byte chunks
64
+ values = [0x6174654d, 0x7b465443, ...] # From disassembly
65
+ flag = b''.join(v.to_bytes(4, 'little') for v in values)
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Timing Side-Channel Attack
71
+
72
+ **Pattern (Clock Out):** Validation time varies per correct character (longer sleep on match).
73
+
74
+ **Exploitation:**
75
+ ```python
76
+ import time
77
+ from pwn import *
78
+
79
+ flag = ""
80
+ for pos in range(flag_length):
81
+ best_char, best_time = '', 0
82
+ for c in string.printable:
83
+ io = remote(host, port)
84
+ start = time.time()
85
+ io.sendline((flag + c).ljust(total_len, 'X'))
86
+ io.recvall()
87
+ elapsed = time.time() - start
88
+ if elapsed > best_time:
89
+ best_time = elapsed
90
+ best_char = c
91
+ io.close()
92
+ flag += best_char
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Multi-Thread Anti-Debug with Decoy + Signal Handler Mixed Boolean-Arithmetic (ApoorvCTF 2026)
98
+
99
+ **Pattern (A Golden Experience Requiem):** Multi-threaded binary with layered anti-analysis: Thread 1 performs decoy operations (fake AES + deliberate crash via `ud2`), Thread 2 does the real flag computation in a SIGSEGV signal handler using Mixed Boolean Arithmetic (MBA), Thread 3 erases memory to prevent post-mortem analysis.
100
+
101
+ **Thread layout:**
102
+ | Thread | Purpose | Trap |
103
+ |--------|---------|------|
104
+ | Thread 1 | Decoy: AES-looking operations → `ud2` crash | Analysts waste time reversing fake crypto |
105
+ | Thread 2 | Real flag: SIGSEGV handler with MBA transforms | Hidden in signal handler, not main code path |
106
+ | Thread 3 | Memory eraser: zeros out flag data after computation | Prevents memory dumping |
107
+ | Main | rdtsc-based anti-debug timing check | Penalizes debugger-attached execution |
108
+
109
+ **Solving approach — pure Python emulation of MBA logic:**
110
+ ```python
111
+ # MBA helpers (extracted from assembly)
112
+ def mba_add(a, b): return (a + b) & 0xff
113
+ def mba_xor(a, b): return (a ^ b) & 0xff
114
+
115
+ def mba_transform(i):
116
+ """Position-dependent transform from signal handler."""
117
+ val = (i * 7 + 0x3f) & 0xff
118
+ rotated = ((i << 3) | (i >> 5)) & 0xff
119
+ return mba_xor(val, rotated)
120
+
121
+ # S-box (SHA-256 initial hash values repurposed)
122
+ SBOX = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
123
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]
124
+
125
+ def sbox_lookup(i):
126
+ idx = i & 7
127
+ shift = ((i >> 3) & 3) * 8
128
+ return (SBOX[idx] >> shift) & 0xff
129
+
130
+ # Two interleaved rodata arrays (even indices → array1, odd → array2)
131
+ rodata1 = bytes.fromhex("39407691b717c97879013adf3a2adea11c2b04e0")
132
+ rodata2 = bytes.fromhex("bb19b025e37eaa786c4116e7aeea00c9c623940d")
133
+
134
+ flag = []
135
+ for i in range(40): # flag length
136
+ t = mba_transform(i)
137
+ s = sbox_lookup(i)
138
+ mem = rodata1[i // 2] if i % 2 == 0 else rodata2[i // 2]
139
+ flag.append(chr(t ^ s ^ mem))
140
+
141
+ print(''.join(flag))
142
+ ```
143
+
144
+ **Key insight:** The real flag logic is in the signal handler (SIGSEGV/SIGILL), not the main thread. Thread 1's AES-like code and `ud2` crash are intentional misdirection. The `rdtsc` timing check detects debuggers and corrupts output. Bypass by extracting the MBA logic from assembly and reimplementing in Python — never run the binary under a debugger.
145
+
146
+ **Detection indicators:**
147
+ - Multiple `pthread_create` calls with different handler functions
148
+ - `signal(SIGSEGV, handler)` or `sigaction` setup
149
+ - `ud2` instruction (deliberate illegal instruction)
150
+ - `rdtsc` instructions for timing checks
151
+ - SHA-256 constants (0x6a09e667...) used as lookup tables, not for hashing
152
+
153
+ ---
154
+
155
+ ## INT3 Patch + Coredump Brute-Force Oracle (Pwn2Win 2016)
156
+
157
+ Instead of reversing complex transformation logic, patch a byte to `0xCC` (INT3) after the transform, enable core dumps, brute-force each character by running the binary and extracting the transformed result from the coredump via `strings`.
158
+
159
+ ```bash
160
+ # Patch byte at transform output point to 0xCC
161
+ printf '\xcc' | dd of=binary bs=1 seek=$((0x400ebb)) conv=notrunc
162
+ ulimit -c unlimited
163
+ # Brute-force each position:
164
+ for c in $(seq 32 126); do
165
+ echo -ne "$(printf '\\x%02x' $c)$known_suffix" | ./binary 2>/dev/null
166
+ strings core | grep -q "$expected" && echo "Found: $c"
167
+ done
168
+ ```
169
+
170
+ **Key insight:** Use INT3/SIGTRAP as a breakpoint oracle -- the coredump captures computed state at the crash point. Avoids full reverse engineering of the transformation.
171
+
172
+ ---
173
+
174
+ ## Signal Handler Chain + LD_PRELOAD Oracle (Nuit du Hack 2016)
175
+
176
+ Binary uses Unix signals for flow control: `main()` sends SIGINT to itself 1024 times, each handler checks one password character, then calls `signal()` to install the next handler. Bypass: LD_PRELOAD a custom `signal()` that logs when it's called (indicating correct character), brute-force each position.
177
+
178
+ ```c
179
+ // LD_PRELOAD library:
180
+ #include <signal.h>
181
+ sighandler_t signal(int sig, sighandler_t handler) {
182
+ write(2, "CORRECT\n", 8); // signal() called = char was correct
183
+ return SIG_DFL;
184
+ }
185
+ ```
186
+
187
+ **Key insight:** Signal-handler-chain anti-reversing can be defeated by hooking `signal()` via LD_PRELOAD. The call to `signal()` (to install the next handler) acts as a side-channel confirming the current character.
188
+
189
+ ---
190
+
191
+ ### printf Format String VM Decompilation to Z3 (SECCON 2017)
192
+
193
+ A "virtual machine" implemented entirely via `%hhn` format strings. Format string `%hhn` writes the count of printed characters (mod 256) to a pointed-to byte. A sequence of `%Nc%hhn` instructions implements arbitrary byte-to-memory writes, effectively creating a bytecode VM.
194
+
195
+ **Step 1: Identify instruction types.**
196
+ Count unique format patterns to determine the instruction set:
197
+ ```bash
198
+ # Normalize numbers and count unique patterns
199
+ sed -e 's/[[:digit:]]\+/1/g' program.fs | sort | uniq -c | sort -nr
200
+ ```
201
+
202
+ **Step 2: Write a decompiler.**
203
+ Convert format patterns to C-style pseudocode. Each `%N...%hhn` pair maps to a memory write: extract the write address (from the argument pointer) and value (from the character count).
204
+
205
+ **Step 3: Recognize the algorithm.**
206
+ The pseudocode typically reveals a linear equation system over bytes. Map memory addresses to symbolic variables.
207
+
208
+ **Step 4: Generate Z3 constraints and solve.**
209
+ ```python
210
+ from z3 import *
211
+
212
+ flag_len = 32 # adjust based on decompiled output
213
+ flag = [BitVec(f'f{i}', 8) for i in range(flag_len)]
214
+ s = Solver()
215
+
216
+ # Constrain to printable ASCII
217
+ for f in flag:
218
+ s.add(f >= 0x20, f <= 0x7e)
219
+
220
+ # Add constraints from decompiled format string operations
221
+ # e.g., flag[3] + flag[7] == 0xAB (mod 256)
222
+ # These come from the write sequences: each %hhn accumulates
223
+ # character counts and writes the result to a target byte
224
+ s.add((flag[0] + flag[1]) & 0xFF == 0x9A) # example constraint
225
+ s.add((flag[2] ^ flag[3]) & 0xFF == 0x3F) # example constraint
226
+ # ... (add all constraints from decompilation)
227
+
228
+ if s.check() == sat:
229
+ m = s.model()
230
+ print(bytes([m[f].as_long() for f in flag]))
231
+ ```
232
+
233
+ **Decompilation approach in detail:**
234
+ 1. Extract the write address and value from each `%N...%hhn` pair
235
+ 2. Map memory addresses to symbolic variables (flag bytes)
236
+ 3. Build an equation system from the write sequences
237
+ 4. Solve with Z3
238
+
239
+ **Key insight:** Format string `%hhn` writes the count of printed characters (mod 256) to a pointed-to byte. A sequence of `%Nc%hhn` instructions implements arbitrary byte-to-memory writes, effectively creating a bytecode VM. Decompile by: (1) extract the write address and value from each `%N...%hhn` pair, (2) map memory addresses to symbolic variables, (3) build an equation system from the write sequences, (4) solve with Z3.
240
+
241
+ **References:** SECCON 2017
242
+
243
+ ---
244
+
245
+ ## Quadtree Recursive Image Format Parser (Google CTF Quals 2018)
246
+
247
+ **Pattern:** Challenge ships a proprietary image format. Reverse engineering shows it is a quadtree: the canvas is split into the largest enclosing power-of-two square, that square is recursively split into four quadrants, and a 1-byte command tells which of the four to subdivide further. Quadrants marked as "leaf" are followed by three bytes of RGB color; the rest recurse.
248
+
249
+ ```python
250
+ # Command byte: bits 3..0 = {top-left, top-right, bottom-left, bottom-right}
251
+ # Bit set ⇒ subdivide; bit clear ⇒ leaf (next 3 bytes = RGB)
252
+
253
+ def parse(stream, x, y, size):
254
+ cmd = stream.read(1)[0]
255
+ half = size // 2
256
+ children = [
257
+ (x, y ),
258
+ (x + half, y ),
259
+ (x, y + half),
260
+ (x + half, y + half),
261
+ ]
262
+ for i, (cx, cy) in enumerate(children):
263
+ if cmd & (1 << (3 - i)):
264
+ parse(stream, cx, cy, half)
265
+ else:
266
+ rgb = stream.read(3)
267
+ fill_rect(cx, cy, half, half, rgb)
268
+ ```
269
+
270
+ Walk the recursion until `half == 1` (or until a "leaf" bit is seen) and paint the canvas as the format pushes bytes. The flag image renders correctly once the quadrant bit order is matched.
271
+
272
+ **Key insight:** Proprietary image/compression formats in CTF challenges are almost always quadtrees, LZ77 variants, or Huffman streams. Look for recursive structures with a short command byte followed by either more commands or fixed-width leaf data. Prototype the parser by printing the recursion depth and offset for each call — mismatched depth is the first signal that the bit order or leaf size is wrong.
273
+
274
+ **References:** Google CTF Quals 2018 — writeup 10335