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,397 @@
1
+ # CTF Reverse - Competition-Specific Patterns (Part 2)
2
+
3
+ ## Table of Contents
4
+ - [Multi-Layer Self-Decrypting Binary (DiceCTF 2026)](#multi-layer-self-decrypting-binary-dicectf-2026)
5
+ - [Embedded ZIP + XOR License Decryption (MetaCTF 2026)](#embedded-zip--xor-license-decryption-metactf-2026)
6
+ - [Stack String Deobfuscation from .rodata XOR Blob (Nullcon 2026)](#stack-string-deobfuscation-from-rodata-xor-blob-nullcon-2026)
7
+ - [Prefix Hash Brute-Force (Nullcon 2026)](#prefix-hash-brute-force-nullcon-2026)
8
+ - [CVP/LLL Lattice for Constrained Integer Validation (HTB ShadowLabyrinth)](#cvplll-lattice-for-constrained-integer-validation-htb-shadowlabyrinth)
9
+ - [Decision Tree Function Obfuscation (HTB WonderSMS)](#decision-tree-function-obfuscation-htb-wondersms)
10
+ - [GF(2^8) Gaussian Elimination for Flag Recovery (ApoorvCTF 2026)](#gf28-gaussian-elimination-for-flag-recovery-apoorvctf-2026)
11
+ - [ROP Chain Obfuscation in Modified Binary (PlaidCTF 2016)](#rop-chain-obfuscation-in-modified-binary-plaidctf-2016)
12
+
13
+ ---
14
+
15
+ ## Multi-Layer Self-Decrypting Binary (DiceCTF 2026)
16
+
17
+ **Pattern (another-onion):** Binary with N layers (e.g., 256), each reading 2 key bytes, deriving keystream via SHA-256 NI instructions, XOR-decrypting the next layer, then jumping to it. Must solve within a time limit (e.g., 30 minutes).
18
+
19
+ **Oracle for correct key:** Wrong key bytes produce garbage code. Correct key bytes produce code with exactly 2 `call read@plt` instructions (next layer's reads). Brute-force all 65536 candidates per layer using this oracle.
20
+
21
+ **JIT execution approach (fastest):**
22
+ ```c
23
+ // Map binary's memory at original virtual addresses into solver process
24
+ // Compile solver at non-overlapping address: -Wl,-Ttext-segment=0x10000000
25
+ void *text = mmap((void*)0x400000, text_size, PROT_RWX, MAP_FIXED|MAP_PRIVATE, fd, 0);
26
+ void *bss = mmap((void*)bss_addr, bss_size, PROT_RW, MAP_FIXED|MAP_SHARED, shm_fd, 0);
27
+
28
+ // Patch read@plt to inject candidate bytes instead of reading stdin
29
+ // Patch tail jmp/call to next layer with ret/NOP to return from layer
30
+
31
+ // Fork-per-candidate: COW gives isolated memory without memcpy
32
+ for (int candidate = 0; candidate < 65536; candidate++) {
33
+ pid_t pid = fork();
34
+ if (pid == 0) {
35
+ // Child: remap BSS as MAP_PRIVATE (COW from shared file)
36
+ mmap(bss_addr, bss_size, PROT_RW, MAP_FIXED|MAP_PRIVATE, shm_fd, 0);
37
+ inject_key(candidate >> 8, candidate & 0xff);
38
+ ((void(*)())layer_addr)(); // Execute layer as function call
39
+ // Check: does decrypted code contain exactly 2 call read@plt?
40
+ if (count_read_calls(next_layer_addr) == 2) signal_found(candidate);
41
+ _exit(0);
42
+ }
43
+ }
44
+ ```
45
+
46
+ **Performance tiers:**
47
+ | Approach | Speed | 256-layer estimate |
48
+ |----------|-------|--------------------|
49
+ | Python subprocess | ~2/s | days |
50
+ | Ptrace fork injection | ~119/s | 6+ hours |
51
+ | JIT + fork-per-candidate | ~1000/s | 140 min |
52
+ | JIT + shared BSS + 32 workers | ~3500/s | **~17 min** |
53
+
54
+ **Shared BSS optimization:** BSS (16MB+) stored in `/dev/shm` as `MAP_SHARED` in parent. Children remap as `MAP_PRIVATE` for COW. Reduces fork overhead from 16MB page-table setup to ~4KB.
55
+
56
+ **Key insight:** Multi-layer decryption challenges are fundamentally about building fast brute-force engines. JIT execution (mapping binary memory into solver, running code directly as function calls) is orders of magnitude faster than ptrace. Fork-based COW provides free memory isolation per candidate.
57
+
58
+ **Gotchas:**
59
+ - Real binary may use `call` (0xe8) instead of `jmp` (0xe9) for layer transitions — adjust tail patching
60
+ - BSS may extend beyond ELF MemSiz via kernel brk mapping — map extra space
61
+ - SHA-NI instructions work even when not advertised in `/proc/cpuinfo`
62
+
63
+ ---
64
+
65
+ ## Embedded ZIP + XOR License Decryption (MetaCTF 2026)
66
+
67
+ **Pattern (License To Rev):** Binary requires a license file as argument. Contains an embedded ZIP archive with the expected license, and an XOR-encrypted flag.
68
+
69
+ **Recognition:**
70
+ - `strings` reveals `EMBEDDED_ZIP` and `ENCRYPTED_MESSAGE` symbols
71
+ - Binary is not stripped — `nm` or `readelf -s` shows data symbols in `.rodata`
72
+ - `file` shows PIE executable, source file named `licensed.c`
73
+
74
+ **Analysis workflow:**
75
+ 1. **Find data symbols:**
76
+ ```bash
77
+ readelf -s binary | grep -E "EMBEDDED|ENCRYPTED|LICENSE"
78
+ # EMBEDDED_ZIP at offset 0x2220, 384 bytes
79
+ # ENCRYPTED_MESSAGE at offset 0x21e0, 35 bytes
80
+ ```
81
+
82
+ 2. **Extract embedded ZIP:**
83
+ ```python
84
+ import struct
85
+ with open('binary', 'rb') as f:
86
+ data = f.read()
87
+ # Find PK\x03\x04 magic in .rodata
88
+ zip_start = data.find(b'PK\x03\x04')
89
+ # Extract ZIP (size from symbol table or until next symbol)
90
+ open('embedded.zip', 'wb').write(data[zip_start:zip_start+384])
91
+ ```
92
+
93
+ 3. **Extract license from ZIP:**
94
+ ```bash
95
+ unzip embedded.zip # Contains license.txt
96
+ ```
97
+
98
+ 4. **XOR decrypt the flag:**
99
+ ```python
100
+ license = open('license.txt', 'rb').read()
101
+ enc_msg = open('encrypted_msg.bin', 'rb').read() # Extract from .rodata
102
+ flag = bytes(a ^ b for a, b in zip(enc_msg, license))
103
+ print(flag.decode())
104
+ ```
105
+
106
+ **Key insight:** No need to run the binary or bypass the expiry date check. The embedded ZIP and encrypted message are both in `.rodata` — extract and XOR offline.
107
+
108
+ **Disassembly confirms:**
109
+ - `memcmp(user_license, decompressed_embedded_zip, size)` — license validation
110
+ - Date parsing with `sscanf("%d-%d-%d")` on `EXPIRY_DATE=` field
111
+ - XOR loop: `ENCRYPTED_MESSAGE[i] ^ license[i]` → `putc()` per byte
112
+
113
+ **Lesson:** When a binary has named symbols (`EMBEDDED_*`, `ENCRYPTED_*`), extract data directly from the binary without execution. XOR with known plaintext (the license) is trivially reversible.
114
+
115
+ ---
116
+
117
+ ## Stack String Deobfuscation from .rodata XOR Blob (Nullcon 2026)
118
+
119
+ **Pattern (stack_strings_1/2):** Binary mmaps a blob from `.rodata`, XOR-deobfuscates it, then uses the blob to validate input. Flag is recovered by reimplementing the verification loop.
120
+
121
+ **Recognition:**
122
+ - `mmap()` call followed by XOR loop over `.rodata` data
123
+ - Verification loop with running state (`eax`, `ebx`, `r9`) updated with constants like `0x9E3779B9`, `0x85EBCA6B`, `0xA97288ED`
124
+ - `rol32()` operations with position-dependent shifts
125
+ - Expected bytes stored in deobfuscated buffer
126
+
127
+ **Approach:**
128
+ 1. Extract `.rodata` blob with pyelftools:
129
+ ```python
130
+ from elftools.elf.elffile import ELFFile
131
+ with open(binary, "rb") as f:
132
+ elf = ELFFile(f)
133
+ ro = elf.get_section_by_name(".rodata")
134
+ blob = ro.data()[offset:offset+size]
135
+ ```
136
+ 2. Recover embedded constants (length, magic values) by XOR with known keys from disassembly
137
+ 3. Reimplement the byte-by-byte verification loop:
138
+ - Each iteration: compute two hash-like values from running state
139
+ - XOR them together and with expected byte to recover input byte
140
+ - Update running state with constant additions
141
+
142
+ **Variant (stack_strings_2):** Adds position permutation + state dependency on previous character:
143
+ - Position permutation: byte `i` may go to position `pos[i]` in the output
144
+ - State dependency: `need = (expected - rol8(prev_char, 1)) & 0xFF`
145
+ - Must track `state` variable that updates to current character each iteration
146
+
147
+ **Key constants to look for:**
148
+ - `0x9E3779B9` (golden ratio fractional, common in hash functions)
149
+ - `0x85EBCA6B` (MurmurHash3 finalizer constant)
150
+ - `0xA97288ED` (related hash constant)
151
+ - `rol32()` with shift `i & 7`
152
+
153
+ ---
154
+
155
+ ## Prefix Hash Brute-Force (Nullcon 2026)
156
+
157
+ **Pattern (Hashinator):** Binary hashes every prefix of the input independently and outputs one digest per prefix. Given N output digests, the flag has N-1 characters.
158
+
159
+ **Attack:** Recover input one character at a time:
160
+ ```python
161
+ for pos in range(1, len(target_hashes)):
162
+ for ch in charset:
163
+ candidate = known_prefix + ch + padding
164
+ hashes = run_binary(candidate)
165
+ if hashes[pos] == target_hashes[pos]:
166
+ known_prefix += ch
167
+ break
168
+ ```
169
+
170
+ **Key insight:** If each prefix hash is independent (no chaining/HMAC), the problem decomposes into `N` x `|charset|` binary executions. This is the hash equivalent of byte-at-a-time block cipher attacks.
171
+
172
+ **Detection:** Binary outputs multiple hash lines. Changing last character only changes last hash. Different input lengths produce different numbers of output lines.
173
+
174
+ ---
175
+
176
+ ## CVP/LLL Lattice for Constrained Integer Validation (HTB ShadowLabyrinth)
177
+
178
+ **Pattern:** Binary validates flag via matrix multiplication where grouped input characters are multiplied by coefficient matrices and checked against expected 64-bit results. Standard algebra fails because solutions must be printable ASCII (32-126). Lattice-based CVP (Closest Vector Problem) with LLL reduction solves this efficiently.
179
+
180
+ **Identification:**
181
+ 1. Binary groups input characters (e.g., 4 at a time)
182
+ 2. Each group is multiplied by a coefficient matrix
183
+ 3. Results compared against hardcoded 64-bit values
184
+ 4. Need integer solutions in a constrained range (printable ASCII)
185
+
186
+ **SageMath CVP solver:**
187
+ ```python
188
+ from sage.all import *
189
+
190
+ def solve_constrained_matrix(coefficients, targets, char_range=(32, 126)):
191
+ """
192
+ coefficients: list of coefficient rows (e.g., 4 values per group)
193
+ targets: expected output values
194
+ char_range: valid character range (printable ASCII)
195
+ """
196
+ n = len(coefficients[0]) # characters per group
197
+ mid = (char_range[0] + char_range[1]) // 2
198
+
199
+ # Build lattice: [coeff_matrix | I*scale]
200
+ # The target vector includes adjusted targets
201
+ M = matrix(ZZ, n + len(targets), n + len(targets))
202
+ scale = 1000 # Weight to constrain character range
203
+
204
+ for i, row in enumerate(coefficients):
205
+ for j, c in enumerate(row):
206
+ M[j, i] = c
207
+ M[n + i, i] = 1 # padding
208
+
209
+ for j in range(n):
210
+ M[j, len(targets) + j] = scale
211
+
212
+ target_vec = vector(ZZ, [t - sum(c * mid for c in row)
213
+ for row, t in zip(coefficients, targets)]
214
+ + [0] * n)
215
+
216
+ # LLL + CVP
217
+ L = M.LLL()
218
+ closest = L * L.solve_left(target_vec) # or use Babai
219
+ solution = [closest[len(targets) + j] // scale + mid for j in range(n)]
220
+ return bytes(solution)
221
+ ```
222
+
223
+ **Two-phase validation pattern:**
224
+ 1. **Phase 1 (matrix math):** Solve via CVP/LLL → recovers first N characters
225
+ 2. First N characters become AES key → decrypt `file.bin` (XOR last 16 bytes + AES-256-CBC + zlib decompress)
226
+ 3. **Phase 2 (custom VM):** Decrypted bytecode runs in custom VM, validates remaining characters via another linear system (mod 2^32)
227
+
228
+ **Modular linear system solving (Phase 2 — VM validation):**
229
+ ```python
230
+ import numpy as np
231
+ from sympy import Matrix
232
+
233
+ # M * x = v (mod 2^32)
234
+ M_mod = Matrix(coefficients) % (2**32)
235
+ v_mod = Matrix(targets) % (2**32)
236
+ # Gaussian elimination in Z/(2^32)
237
+ solution = M_mod.solve(v_mod) # Returns flag characters
238
+ ```
239
+
240
+ **Key insight:** When a binary validates input through linear combinations with large coefficients and the solution must be in a small range (printable ASCII), this is a lattice problem in disguise. LLL reduction + CVP finds the nearest lattice point, recovering the constrained solution. Cross-reference: invoke `/ctf-crypto` for LLL/CVP fundamentals (advanced-math.md in ctf-crypto).
241
+
242
+ **Detection:** Binary performs matrix-like operations on grouped input, compares against 64-bit constants, and a brute-force search space is too large (e.g., 256^4 per group × 12 groups).
243
+
244
+ ---
245
+
246
+ ## Decision Tree Function Obfuscation (HTB WonderSMS)
247
+
248
+ **Pattern:** Binary routes input through ~200+ auto-generated functions, each computing a polynomial expression from input positions, comparing against a constant, and branching left/right. The tree makes static analysis impractical without scripted extraction.
249
+
250
+ **Identification:**
251
+ 1. Large number of similar functions with random-looking names (e.g., `f315732804`)
252
+ 2. Each function computes arithmetic on specific input positions
253
+ 3. Functions call other tree functions or a final validation function
254
+ 4. Decompiled code shows `if (expr cmp constant) call_left() else call_right()`
255
+
256
+ **Ghidra headless scripting for mass extraction:**
257
+ ```python
258
+ # Extract comparison constants from all tree functions
259
+ # Run via: analyzeHeadless project/ tmp -import binary -postScript extract_tree.py
260
+ from ghidra.program.model.listing import *
261
+ from ghidra.program.model.symbol import *
262
+
263
+ fm = currentProgram.getFunctionManager()
264
+ results = []
265
+ for func in fm.getFunctions(True):
266
+ name = func.getName()
267
+ if name.startswith('f') and name[1:].isdigit():
268
+ # Find CMP instruction and extract immediate constant
269
+ inst_iter = currentProgram.getListing().getInstructions(func.getBody(), True)
270
+ for inst in inst_iter:
271
+ if inst.getMnemonicString() == 'CMP':
272
+ operand = inst.getOpObjects(1)
273
+ if operand:
274
+ results.append((name, int(operand[0].getValue())))
275
+ ```
276
+
277
+ **Constraint propagation from known output format:**
278
+ 1. Start from known output bytes (e.g., `http://HTB{...}`) → fix several input positions
279
+ 2. Fixed positions cascade through arithmetic constraints → determine dependent positions
280
+ 3. Tree root equation pins down remaining free variables
281
+ 4. Recognize English words in partial flag to disambiguate multiple solutions
282
+
283
+ **Key insight:** Auto-generated decision trees look overwhelming but are repetitive by construction. Script the extraction (Ghidra, Binary Ninja, radare2) rather than reversing each function manually. The tree is just a dispatcher — the real logic is in the leaf function and its constraints.
284
+
285
+ **Detection:** Binary with hundreds of similarly-structured functions, 3-5 input position references per function, branching to two other functions or a common leaf.
286
+
287
+ ---
288
+
289
+ ## GF(2^8) Gaussian Elimination for Flag Recovery (ApoorvCTF 2026)
290
+
291
+ **Pattern (Forge):** Stripped binary performs Gaussian elimination over GF(2^8) (Galois Field with 256 elements, using the AES polynomial). A matrix and augmentation vector are embedded in `.rodata`. The solution vector is the flag.
292
+
293
+ **GF(2^8) arithmetic with AES polynomial (x^8+x^4+x^3+x+1 = 0x11b):**
294
+ ```python
295
+ def gf_mul(a, b):
296
+ """Multiply in GF(2^8) with AES reduction polynomial."""
297
+ p = 0
298
+ for _ in range(8):
299
+ if b & 1:
300
+ p ^= a
301
+ hi = a & 0x80
302
+ a = (a << 1) & 0xff
303
+ if hi:
304
+ a ^= 0x1b # Reduction: x^8 = x^4+x^3+x+1
305
+ b >>= 1
306
+ return p
307
+
308
+ def gf_inv(a):
309
+ """Brute-force multiplicative inverse (fine for 256 elements)."""
310
+ if a == 0: return 0
311
+ for x in range(1, 256):
312
+ if gf_mul(a, x) == 1:
313
+ return x
314
+ return 0
315
+ ```
316
+
317
+ **Solving the linear system:**
318
+ ```python
319
+ # Extract N×N matrix + N-byte augmentation from binary .rodata
320
+ N = 56 # Flag length
321
+ # Build augmented matrix: N rows × (N+1) cols
322
+
323
+ for col in range(N):
324
+ # Find non-zero pivot
325
+ pivot = next((r for r in range(col, N) if aug[r][col] != 0), -1)
326
+ if pivot != col:
327
+ aug[col], aug[pivot] = aug[pivot], aug[col]
328
+ # Scale pivot row by inverse
329
+ inv = gf_inv(aug[col][col])
330
+ aug[col] = [gf_mul(v, inv) for v in aug[col]]
331
+ # Eliminate column in all other rows
332
+ for row in range(N):
333
+ if row == col: continue
334
+ factor = aug[row][col]
335
+ if factor == 0: continue
336
+ aug[row] = [v ^ gf_mul(factor, aug[col][j]) for j, v in enumerate(aug[row])]
337
+
338
+ flag = bytes(aug[i][N] for i in range(N))
339
+ ```
340
+
341
+ **Key insight:** GF(2^8) is NOT regular integer arithmetic — addition is XOR, multiplication uses polynomial reduction. The AES polynomial (0x11b) is the most common; look for the constant `0x1b` in disassembly. The binary may encrypt the result with AES-GCM afterward, but the raw solution vector (pre-encryption) is the flag.
342
+
343
+ **Detection:** Binary with a large matrix in `.rodata` (N² bytes), XOR-based row operations, constants `0x1b` or `0x11b`, and flag length matching sqrt of matrix size.
344
+
345
+ ---
346
+
347
+ ## ROP Chain Obfuscation in Modified Binary (PlaidCTF 2016)
348
+
349
+ **Pattern (quite quixotic quest):** Modified `curl` binary with a custom `--pctfkey KEY` option. Key validation replaces `esp` with a buffer address and returns into a ~250KB ROP chain stored in a `magic_buf` symbol. The ROP chain validates the key through XOR, MD5, and constant comparisons.
350
+
351
+ **Analysis approach:**
352
+
353
+ 1. **Detect the ROP dispatch:** Look for `mov esp, eax; ret` or similar stack pivot — this redirects execution into the ROP chain
354
+ 2. **Dump the ROP chain:** Script GDB to disassemble instructions after each return address in the chain:
355
+ ```python
356
+ # GDB script to trace ROP gadgets
357
+ import gdb
358
+
359
+ magic_buf = 0x080b0000 # symbol address
360
+ buf_size = 0x40000 # quarter megabyte
361
+ offset = 0
362
+
363
+ while offset < buf_size:
364
+ addr = int.from_bytes(gdb.selected_inferior().read_memory(magic_buf + offset, 4), 'little')
365
+ gdb.execute(f'x/3i {addr}')
366
+ # Advance past the gadget (typically 4 bytes per return address)
367
+ offset += 4
368
+ ```
369
+
370
+ 3. **Identify patterns in the chain:** Look for unrolled loops (repeated gadget sequences), `pop` instructions that skip data, and `ret imm16` that skip large blocks
371
+ 4. **Reconstruct the algorithm:** The chain typically performs:
372
+ - Key length check (compare with constant)
373
+ - Character-level operations (sum ASCII values, XOR with constants)
374
+ - Hash computation (MD5 of derived value)
375
+ - Hash prefix comparison
376
+ - XOR of input with hash as keystream
377
+ - Comparison with embedded constants
378
+
379
+ 5. **Extract and solve:** Dump the embedded constants, brute-force any intermediate values (e.g., character sum → MD5 with matching prefix), then XOR to recover the key:
380
+ ```python
381
+ import hashlib
382
+
383
+ # Brute-force the sum that produces correct MD5 prefix
384
+ target_prefix = 0xc0050bdd # extracted from ROP chain
385
+ for s in range(128 * 0x35): # max sum of printable chars * key_length
386
+ h = hashlib.md5(str(s ^ xor_constant).encode()).hexdigest()
387
+ if int(h[:8], 16) == target_prefix:
388
+ md5_key = bytes.fromhex(h)
389
+ break
390
+
391
+ # XOR embedded values with MD5 keystream to get flag
392
+ flag = bytes(v ^ md5_key[i % 16] for i, v in enumerate(embedded_values))
393
+ ```
394
+
395
+ **Key insight:** ROP chain obfuscation ("ROPfuscation") hides algorithms in chains of return-oriented gadgets. The chain looks incomprehensible as raw addresses but becomes analyzable when you: (a) dump each gadget's disassembly, (b) filter repetitions and skip regions, (c) annotate register effects. The chain is functionally equivalent to normal code — it just uses `ret` instead of sequential execution. Large chains (100K+ gadgets) often contain unrolled loops that compress to ~1000 lines of pseudocode.
396
+
397
+ See also: [patterns-ctf.md](patterns-ctf.md) for Part 1 (hidden emulator opcodes, SPN static extraction, image XOR smoothness, byte-at-a-time cipher, mathematical convergence bitmap, Windows PE XOR bitmap OCR, two-stage RC4+VM loaders, GBA ROM meet-in-the-middle, Sprague-Grundy game theory, kernel module maze solving, multi-threaded VM channels). [patterns-ctf-3.md](patterns-ctf-3.md) for Part 3 (Z3 single-line Python circuit, sliding window popcount, keyboard LED Morse code, C++ destructor-hidden validation, syscall side-effect memory corruption, MFC dialog event handlers, VM sequential key-chain brute-force, Burrows-Wheeler transform inversion, OpenType font ligature exploitation, GLSL shader VM with self-modifying code, instruction counter as cryptographic state).