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,456 @@
1
+ # CTF Crypto - ZKP, Solvers & Advanced Techniques
2
+
3
+ ## Table of Contents
4
+ - [ZKP Attacks](#zkp-attacks)
5
+ - [Graph 3-Coloring](#graph-3-coloring)
6
+ - [Z3 SMT Solver Guide](#z3-smt-solver-guide)
7
+ - [Garbled Circuits: Free XOR Delta Recovery (LACTF 2026)](#garbled-circuits-free-xor-delta-recovery-lactf-2026)
8
+ - [Bigram/Trigram Substitution -> Constraint Solving (LACTF 2026)](#bigramtrigram-substitution---constraint-solving-lactf-2026)
9
+ - [Shamir Secret Sharing with Deterministic Coefficients (LACTF 2026)](#shamir-secret-sharing-with-deterministic-coefficients-lactf-2026)
10
+ - [Race Condition in Crypto-Protected Endpoints (LACTF 2026)](#race-condition-in-crypto-protected-endpoints-lactf-2026)
11
+ - [Garbled Circuits: AES Key Recovery via Metadata Leakage (srdnlenCTF 2026)](#garbled-circuits-aes-key-recovery-via-metadata-leakage-srdnlenctf-2026)
12
+ - [Post-Quantum Signature Fault Injection: MAYO (srdnlenCTF 2026)](#post-quantum-signature-fault-injection-mayo-srdnlenctf-2026)
13
+ - [Lattice-Based Threshold Signature Attack: FROST (srdnlenCTF 2026)](#lattice-based-threshold-signature-attack-frost-srdnlenctf-2026)
14
+ - [Groth16 Broken Trusted Setup — delta == gamma (DiceCTF 2026)](#groth16-broken-trusted-setup--delta--gamma-dicectf-2026)
15
+ - [Groth16 Proof Replay — Unconstrained Nullifier (DiceCTF 2026)](#groth16-proof-replay--unconstrained-nullifier-dicectf-2026)
16
+ - [DV-SNARG Forgery via Verifier Oracle (DiceCTF 2026)](#dv-snarg-forgery-via-verifier-oracle-dicectf-2026)
17
+ - [KZG Pairing Oracle for Permutation Recovery (UNbreakable 2026)](#kzg-pairing-oracle-for-permutation-recovery-unbreakable-2026)
18
+ - [Shamir Secret Sharing with Reused Polynomial Coefficients (PoliCTF 2017)](#shamir-secret-sharing-with-reused-polynomial-coefficients-polictf-2017)
19
+
20
+ ---
21
+
22
+ ## ZKP Attacks
23
+
24
+ - Look for information leakage in proofs
25
+ - If proving IMPOSSIBLE problem (e.g., 3-coloring K4), you must cheat
26
+ - Find hash collisions to commit to one value but reveal another
27
+ - PRNG state recovery: salts generated from seeded PRNG can be predicted
28
+ - Small domain brute force: if you know `commit(i) = sha256(salt(i), color(i))` and have salt, brute all colors
29
+
30
+ ---
31
+
32
+ ## Graph 3-Coloring
33
+
34
+ ```python
35
+ import networkx as nx
36
+ nx.coloring.greedy_color(G, strategy='saturation_largest_first')
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Z3 SMT Solver Guide
42
+
43
+ Z3 solves constraint satisfaction - useful when crypto reduces to finding values satisfying conditions.
44
+
45
+ **Basic usage:**
46
+ ```python
47
+ from z3 import *
48
+
49
+ # Boolean variables (for bit-level problems)
50
+ bits = [Bool(f'b{i}') for i in range(64)]
51
+
52
+ # Integer/bitvector variables
53
+ x = BitVec('x', 32) # 32-bit bitvector
54
+ y = Int('y') # arbitrary precision int
55
+
56
+ solver = Solver()
57
+ solver.add(x ^ 0xdeadbeef == 0x12345678)
58
+ solver.add(y > 100, y < 200)
59
+
60
+ if solver.check() == sat:
61
+ model = solver.model()
62
+ print(model.eval(x))
63
+ ```
64
+
65
+ **BPF/SECCOMP filter solving:**
66
+
67
+ When challenges use BPF bytecode for flag validation (e.g., custom syscall handlers):
68
+
69
+ ```python
70
+ from z3 import *
71
+
72
+ # Model flag as array of 4-byte chunks (how BPF sees it)
73
+ flag = [BitVec(f'f{i}', 32) for i in range(14)]
74
+ s = Solver()
75
+
76
+ # Constraint: printable ASCII
77
+ for f in flag:
78
+ for byte in range(4):
79
+ b = (f >> (byte * 8)) & 0xff
80
+ s.add(b >= 0x20, b < 0x7f)
81
+
82
+ # Extract constraints from BPF dump (seccomp-tools dump ./binary)
83
+ mem = [BitVec(f'm{i}', 32) for i in range(16)]
84
+
85
+ # Example BPF constraint reconstruction
86
+ s.add(mem[0] == flag[0])
87
+ s.add(mem[1] == mem[0] ^ flag[1])
88
+ s.add(mem[4] == mem[0] + mem[1] + mem[2] + mem[3])
89
+ s.add(mem[8] == 4127179254) # From BPF if statement
90
+
91
+ if s.check() == sat:
92
+ m = s.model()
93
+ flag_bytes = b''
94
+ for f in flag:
95
+ val = m[f].as_long()
96
+ flag_bytes += val.to_bytes(4, 'little')
97
+ print(flag_bytes.decode())
98
+ ```
99
+
100
+ **Converting bits to flag:**
101
+ ```python
102
+ from Crypto.Util.number import long_to_bytes
103
+
104
+ if solver.check() == sat:
105
+ model = solver.model()
106
+ flag_bits = ''.join('1' if model.eval(b) else '0' for b in bits)
107
+ print(long_to_bytes(int(flag_bits, 2)))
108
+ ```
109
+
110
+ **When to use Z3:**
111
+ - Type system constraints (OCaml GADTs, Haskell types)
112
+ - Custom hash/cipher with algebraic structure
113
+ - Equation systems over finite fields
114
+ - Boolean satisfiability encoded in challenge
115
+ - Constraint propagation puzzles
116
+
117
+ ---
118
+
119
+ ## Garbled Circuits: Free XOR Delta Recovery (LACTF 2026)
120
+
121
+ **Pattern (sisyphus):** Yao's garbled circuit with free XOR optimization. Circuit designed so normal evaluation only reaches one wire label, but the other is needed.
122
+
123
+ **Free XOR property:** Wire labels satisfy `W_0 XOR W_1 = delta` for global secret delta.
124
+
125
+ **Attack:** XOR three of four encrypted truth table entries to cancel AES terms:
126
+ ```python
127
+ # Encrypted rows: E_i = AES(key_a_i XOR key_b_i, G_out_f(a,b))
128
+ # XOR of three rows where AES inputs differ by delta causes cancellation
129
+ # Reveals delta directly, then compute: W_1 = W_0 XOR delta
130
+ ```
131
+
132
+ **General lesson:** In garbled circuits, if you can obtain any two labels for the same wire, you recover delta and can compute all labels.
133
+
134
+ ---
135
+
136
+ ## Bigram/Trigram Substitution -> Constraint Solving (LACTF 2026)
137
+
138
+ **Pattern (lazy-bigrams):** Bigram substitution cipher where plaintext has known structure (NATO phonetic alphabet).
139
+
140
+ **OR-Tools CP-SAT approach:**
141
+ 1. Model substitution as injective mapping (IntVar per bigram)
142
+ 2. Add crib constraints from known flag prefix
143
+ 3. Add **regular language constraint** (automaton) for valid NATO word sequences
144
+ 4. Solver finds unique solution
145
+
146
+ **Pattern (not-so-lazy-trigrams):** "Trigram substitution" that decomposes into three independent monoalphabetic ciphers on positions mod 3.
147
+
148
+ **Decomposition insight:** If cipher uses `shuffle[pos % n][char]`, each residue class `pos = k (mod n)` is an independent monoalphabetic substitution. Solve each separately with frequency analysis or known-plaintext.
149
+
150
+ ---
151
+
152
+ ## Shamir Secret Sharing with Deterministic Coefficients (LACTF 2026)
153
+
154
+ **Pattern (spreading-secrets):** Coefficients `a_1...a_9` are deterministic functions of secret s (via RNG seeded with s). One share (x_0, y_0) is revealed.
155
+
156
+ **Vulnerability:** Given one share, the equation `y_0 = s + g(s)*x_0 + g^2(s)*x_0^2 + ... + g^9(s)*x_0^9` is **univariate** in s.
157
+
158
+ **Root-finding via Frobenius:**
159
+ ```python
160
+ # In GF(p), find roots of h(s) via gcd with x^p - x
161
+ # h(s) = s + g(s)*x_0 + ... + g^9(s)*x_0^9 - y_0
162
+ # Compute x^p mod h(x) via binary exponentiation with polynomial reduction
163
+ # gcd(x^p - x, h(x)) = product of (x - root_i) for all roots
164
+ R.<x> = PolynomialRing(GF(p))
165
+ h = construct_polynomial(x0, y0)
166
+ xp = pow(x, p, h) # Fast modular exponentiation
167
+ g = gcd(xp - x, h) # Extract linear factors
168
+ roots = [-g[0]/g[1]] if g.degree() == 1 else g.roots()
169
+ ```
170
+
171
+ **General lesson:** If ALL Shamir coefficients are derived from the secret, a single share creates a univariate equation. This completely breaks the (k,n) threshold scheme.
172
+
173
+ ---
174
+
175
+ ## Race Condition in Crypto-Protected Endpoints (LACTF 2026)
176
+
177
+ **Pattern (misdirection):** Endpoint has TOCTOU vulnerability: `if counter < 4` check happens before increment, allowing concurrent requests to all pass the check.
178
+
179
+ **Exploitation:**
180
+ 1. **Cache-bust signatures:** Modify each request slightly (e.g., prepend zeros to nonce) so server can't use cached verification results
181
+ 2. **Synchronize requests:** Use multiprocessing with barrier to send ~80 simultaneous requests
182
+ 3. All pass `counter < 4` check before any increments -> counter jumps past limit
183
+
184
+ ```python
185
+ from multiprocessing import Process, Barrier
186
+ barrier = Barrier(80)
187
+
188
+ def make_request(barrier, modified_sig):
189
+ barrier.wait() # Synchronize all processes
190
+ requests.post(url, json={"sig": modified_sig})
191
+
192
+ # Launch 80 processes with unique signature modifications
193
+ processes = [Process(target=make_request, args=(barrier, modify_sig(i))) for i in range(80)]
194
+ ```
195
+
196
+ **Key insight:** TOCTOU in `check-then-act` patterns. Look for read-modify-write without atomicity/locking.
197
+
198
+ ---
199
+
200
+ ## Garbled Circuits: AES Key Recovery via Metadata Leakage (srdnlenCTF 2026)
201
+
202
+ **Pattern (FHAES):** Service evaluates AES via garbled circuits with a fixed per-connection key. Exploit garbling metadata rather than AES cryptanalysis.
203
+
204
+ **Attack:**
205
+ 1. Construct a custom circuit with one attacker-controlled AND gate that leaks the global Free-XOR offset delta
206
+ 2. Use delta to locally evaluate the key-schedule section (first 1360 AND gates) as the evaluator
207
+ 3. For each of the first 16 key-schedule S-box calls, brute-force the input byte by re-garbling the S-box chunk and comparing observed AND tables
208
+ 4. Reconstruct key words from S-box outputs and recover the full 128-bit key through algebraic manipulation of the AES-128 schedule recurrence
209
+
210
+ ```python
211
+ def garble_and(A, B, D, and_idx):
212
+ """Reproduce garbling with proper parity handling."""
213
+ r = B & 1
214
+ alpha = A & 1
215
+ beta = B & 1
216
+ # Computes gate0, gate1, z output via hash-based approach
217
+ return gate0, gate1, z
218
+
219
+ def evaluator_and(A, B, gate0, gate1, and_idx):
220
+ """Evaluate AND gate using hash-based approach."""
221
+ hashA = h_wire(A, and_idx)
222
+ hashB = h_wire(B, and_idx)
223
+ L = hashA if (A & 1) == 0 else (hashA ^ gate0)
224
+ R = hashB if (B & 1) == 0 else (hashB ^ gate1)
225
+ return L ^ R ^ (A * (B & 1))
226
+ ```
227
+
228
+ **Key insight:** Garbled circuits that use free XOR optimization with fixed keys across sessions leak key material through the AND gate truth tables. Each S-box has a small enough input space (256 values) to brute-force when you know delta. This extends the LACTF technique from "recovering delta" to "recovering the entire AES key."
229
+
230
+ ---
231
+
232
+ ## Post-Quantum Signature Fault Injection: MAYO (srdnlenCTF 2026)
233
+
234
+ **Pattern (Faulty Mayo):** One-byte fault injection window in `mayo_sign_signature` before final `s = v + O*x` construction. Controlled bit flips across 64 signature queries recover the secret matrix O row by row.
235
+
236
+ **Attack:**
237
+ 1. Reverse binary to map fault offsets to `mayo_sign_signature` instructions
238
+ 2. For each of 64 rows of secret matrix O, use faulted signatures to extract linear equations over GF(16)
239
+ 3. Solve 17-variable linear systems over GF(16) for each row using Gaussian elimination
240
+ 4. Rebuild equivalent signer using recovered O and public seed from compressed public key
241
+ 5. Forge valid signature for challenge message
242
+
243
+ **GF(16) Gaussian elimination:**
244
+ ```python
245
+ # Precompute multiplication and inverse tables for GF(16)
246
+ # GF(16) = GF(2)[x] / (x^4 + x + 1), elements 0-15
247
+ INV = [0] * 16 # multiplicative inverses
248
+ MUL = [[0]*16 for _ in range(16)] # multiplication table
249
+
250
+ def solve_linear_gf16(equations, nvars=17):
251
+ """Gaussian elimination over GF(16)."""
252
+ A = [x[:] + [y] for x, y in equations]
253
+ m, row = len(A), 0
254
+ for col in range(nvars):
255
+ piv = next((r for r in range(row, m) if A[r][col] != 0), None)
256
+ if piv is None: continue
257
+ A[row], A[piv] = A[piv], A[row]
258
+ invp = INV[A[row][col]]
259
+ A[row] = [MUL[invp][v] for v in A[row]]
260
+ for r in range(m):
261
+ if r != row and A[r][col] != 0:
262
+ f = A[r][col]
263
+ A[r] = [A[r][c] ^ MUL[f][A[row][c]] for c in range(nvars + 1)]
264
+ row += 1
265
+ return [A[i][nvars] for i in range(nvars)]
266
+ ```
267
+
268
+ **Key insight:** Post-quantum signature schemes like MAYO can be broken with fault injection if you can cause controlled bit flips during signing. Each fault creates a linear equation over GF(16), and 17+ equations per row suffice to recover the secret. This is analogous to DFA on classical schemes but over extension fields.
269
+
270
+ ---
271
+
272
+ ## Lattice-Based Threshold Signature Attack: FROST (srdnlenCTF 2026)
273
+
274
+ **Pattern (Threshold):** Preprocessing queue capacity allows collecting many signatures. Fixed challenge construction enables solving 1D noisy linear equations per coefficient.
275
+
276
+ **Attack:**
277
+ 1. Exploit queue-depth cap (≤8 active) rather than total-usage cap by alternating menu options
278
+ 2. Force fixed challenge `c` by choosing commitment `w₀` each query to zero aggregate commitment before high-bit extraction
279
+ 3. With fixed `c`, each coefficient becomes: `z = λ·u + noise (mod q)`
280
+ 4. Select multiple signer subsets to obtain different Lagrange coefficient scales (small/mid/huge) for each target signer
281
+ 5. Solve via interval intersection and maximum-likelihood selection
282
+ 6. Recover 7 signer shares; combine with own share; reconstruct master secret via Lagrange interpolation
283
+
284
+ **Interval intersection algorithm:**
285
+ ```python
286
+ from math import ceil, floor
287
+
288
+ def intersect_intervals(intervals, lam, z, q, B):
289
+ """Refine candidate intervals using one (λ, z) observation with noise bound B."""
290
+ out = []
291
+ for lo, hi in intervals:
292
+ if lam > 0:
293
+ kmin = ceil((lam * lo - z - B) / q)
294
+ kmax = floor((lam * hi - z + B) / q)
295
+ for k in range(kmin, kmax + 1):
296
+ a = (z + q * k - B) / lam
297
+ b = (z + q * k + B) / lam
298
+ lo2, hi2 = max(lo, a), min(hi, b)
299
+ if lo2 <= hi2:
300
+ out.append((lo2, hi2))
301
+ # Merge overlapping intervals
302
+ out.sort()
303
+ merged = [out[0]] if out else []
304
+ for lo, hi in out[1:]:
305
+ if lo <= merged[-1][1]:
306
+ merged[-1] = (merged[-1][0], max(merged[-1][1], hi))
307
+ else:
308
+ merged.append((lo, hi))
309
+ return merged
310
+ ```
311
+
312
+ **Key insight:** Threshold signature schemes can leak individual shares when the challenge value is controlled. By querying with different signer subsets, you get different Lagrange coefficient scales for the same unknown share, allowing iterative interval refinement. With enough observations, the interval converges to a unique value.
313
+
314
+ ---
315
+
316
+ ## Groth16 Broken Trusted Setup — delta == gamma (DiceCTF 2026)
317
+
318
+ **Pattern (Housing Crisis):** Groth16 verifier has `vk_delta_2 == vk_gamma_2`, which breaks soundness entirely. Proofs are trivially forgeable.
319
+
320
+ **Forgery:**
321
+ ```python
322
+ from py_ecc.bn128 import G1, G2, multiply, add, neg, pairing
323
+ from py_ecc.bn128 import curve_order as q
324
+
325
+ # When delta == gamma, the pairing equation simplifies:
326
+ # e(A, B) = e(alpha, beta) * e(vk_x + C, gamma)
327
+ # Set A = vk_alpha1, B = vk_beta2, then:
328
+ # e(alpha, beta) * e(vk_x + C, gamma) = e(alpha, beta)
329
+ # → e(vk_x + C, gamma) = 1 → C = -vk_x (point negation)
330
+
331
+ forged_A = vk_alpha1 # alpha point from verification key
332
+ forged_B = vk_beta2 # beta point from verification key
333
+ forged_C = neg(vk_x) # negate the public input accumulator
334
+
335
+ # This proof verifies for ANY public inputs
336
+ ```
337
+
338
+ **Detection:** Compare `vk_delta_2` and `vk_gamma_2` in the verifier contract. If equal, the entire Groth16 scheme collapses — any statement can be "proven."
339
+
340
+ **When to check:** Always inspect Groth16 verification key constants before attempting complex attacks. A broken trusted setup makes everything else unnecessary.
341
+
342
+ ---
343
+
344
+ ## Groth16 Proof Replay — Unconstrained Nullifier (DiceCTF 2026)
345
+
346
+ **Pattern (Housing Crisis):** DAO governance never tracks used `proposalNullifierHash` values, and the circuit leaves the nullifier unconstrained. A valid proof from the setup transaction can be replayed infinitely.
347
+
348
+ **Attack:**
349
+ 1. Find the DAO contract's deployment/setup transaction
350
+ 2. Extract constructor arguments containing valid Groth16 proof
351
+ 3. Replay the same proof for every proposal — it always verifies
352
+ 4. Use proposals to control DAO actions (betting, market creation, resolution)
353
+
354
+ **Key insight:** ZK circuits that leave inputs unconstrained and systems that don't track nullifiers are vulnerable to replay. Always check: does the verifier contract track proof nullifiers? Does the circuit actually constrain all declared public inputs?
355
+
356
+ ---
357
+
358
+ ## DV-SNARG Forgery via Verifier Oracle (DiceCTF 2026)
359
+
360
+ **Pattern (Dot):** DV-SNARG (Designated Verifier Succinct Non-interactive ARGument) for an adder circuit. Must produce 20 valid proofs for **wrong** answers.
361
+
362
+ **Key insight:** DV-SNARGs explicitly lose soundness when the prover has oracle access to the verifier (ePrint 2024/1138). The verifier's secret randomness can be extracted through query patterns.
363
+
364
+ **DPP (Dot Product Proof) structure:**
365
+ ```text
366
+ q[i] = v[i] + b*(tensor[i] - constraint[i])
367
+ where b = fixed constant (e.g., 162817)
368
+ v[i] = random in [-256, 256]
369
+ constraint weights r = random in [-2^40, 2^40]
370
+ ```
371
+
372
+ **Forgery via CRS entry cancellation:**
373
+ For a wrong answer, only the output constraint (wire N) is violated. Find two CRS entries whose constraint contributions cancel:
374
+
375
+ 1. Wire N is touched by gate G AND the output constraint
376
+ 2. `pair(input1, input2)` of gate G is touched ONLY by gate G
377
+ 3. Adding `CRS[wire_N]` and subtracting `CRS[pair]` to the wrong proof cancels `b*r_G` terms
378
+ 4. The remaining deficit `b*r_output` also cancels
379
+ 5. Adjust `delta = -v[N] + 2*b*v[input1]*v[input2]` via `delta*G` on h2
380
+
381
+ **Learning secret v values via oracle:**
382
+ ```python
383
+ # At streak=0, submitting correct answer is "safe" — doesn't reset streak
384
+ # Use oracle to learn |v[i]| from unconstrained diagonal pairs:
385
+
386
+ for guess in range(257): # v[i] in [-256, 256], |v[i]| in [0, 256]
387
+ # Set pair(i,i) coefficient to guess^2
388
+ # If guess == |v[i]|, specific oracle response differs
389
+ response = oracle_query(guess)
390
+ if response == "hit":
391
+ abs_v_i = guess
392
+ break
393
+
394
+ # Learn signs from off-diagonal unconstrained pairs (1 query each)
395
+ # Learn product sign: v[a]*v[b] sign from pair(a,b)
396
+ ```
397
+
398
+ **Performance:** ~364 oracle queries for Phase 1 (~97s), ~300s for 20 forged proofs ≈ 400s total.
399
+
400
+ **Key insight:** When attacking DV-SNARGs with oracle access, the strategy is: (1) learn a small number of secret values from the verifier's randomness, (2) use algebraic cancellation between CRS entries to forge proofs. Unconstrained pair indices expose pure tensor products of the secret vector.
401
+
402
+ ---
403
+
404
+ ## KZG Pairing Oracle for Permutation Recovery (UNbreakable 2026)
405
+
406
+ **Pattern (toxicwaste):** KZG commitment scheme publishes shuffled points `{alpha^i * G1}` for i=0..n. The shuffle hides which point corresponds to which exponent. Recover the exponent ordering using bilinear pairings as an oracle, then extract the toxic waste `alpha`.
407
+
408
+ **Distortion map technique:** On supersingular pairing-friendly curves, a distortion map `psi((x,y)) = (zeta*x, y)` (where `zeta^3 = 1`) enables additive exponent comparisons:
409
+
410
+ ```python
411
+ from sage.all import *
412
+
413
+ # For points P_i = alpha^a_i * G1 and P_j = alpha^a_j * G1:
414
+ # e(P_i, psi(P_j)) = e(G1, psi(G1))^(alpha^(a_i + a_j))
415
+ # If e(P_i, psi(P_j)) == e(P_k, psi(G1)), then a_i + a_j == a_k
416
+
417
+ # Step 1: Identify G1 (alpha^0) — the only point where e(P, psi(P)) == e(G1, psi(G1))
418
+ g1 = None
419
+ base_pairing = None
420
+ for P in shuffled_points:
421
+ val = P.weil_pairing(psi(P), order)
422
+ if base_pairing is None:
423
+ base_pairing = val
424
+ g1 = P
425
+ elif val == base_pairing:
426
+ g1 = P
427
+ break
428
+
429
+ # Step 2: Walk the chain — find alpha*G1 via e(P_?, psi(G1)) comparisons
430
+ # Then alpha^2*G1 via e(alpha*G1, psi(alpha*G1)) == e(alpha^2*G1, psi(G1))
431
+ # Continue until full ordering recovered
432
+
433
+ # Step 3: With ordered points, solve A(x) = 0 over GF(q) to get alpha
434
+ # Step 4: Forge KZG opening proofs using recovered alpha
435
+ ```
436
+
437
+ **Key insight:** Bilinear pairings reveal additive relationships between exponents without solving discrete log. The pairing `e(P_i, psi(P_j))` depends on `alpha^(a_i + a_j)`, so comparing against known pairing values identifies which shuffled point has which exponent. This turns a cryptographic shuffle into a solvable ordering problem.
438
+
439
+ ---
440
+
441
+ ## Shamir Secret Sharing with Reused Polynomial Coefficients (PoliCTF 2017)
442
+
443
+ **Pattern:** When a Shamir SSS implementation reuses the same random polynomial coefficients for every character of the secret, share subtraction cancels the higher-order terms.
444
+
445
+ ```python
446
+ # Standard Shamir: y_i = f_i + a1*x + a2*x^2 + ... (different a_j per character)
447
+ # Broken: y_i = f_i + a1*x + a2*x^2 + ... (SAME a_j for all characters)
448
+ # Since higher-order terms are identical:
449
+ # y_1[i] - y_1[0] = f[i] - f[0] (for share x=1)
450
+ # If f[0] is known (e.g., 'f' from flag prefix):
451
+ flag = ''.join(chr(shares[i] - shares[0] + ord('f')) for i in range(len(shares)))
452
+ ```
453
+
454
+ **Key insight:** In correct Shamir SSS, each secret byte uses independent random coefficients. When coefficients are reused, subtracting any two shares at the same evaluation point cancels all randomness, leaving only the difference between the corresponding secret bytes.
455
+
456
+ **References:** PoliCTF 2017
@@ -0,0 +1,121 @@
1
+ # CTF Forensics - 3D Printing / CAD File Forensics
2
+
3
+ ## Table of Contents
4
+ - [PrusaSlicer Binary G-code (.g / .bgcode)](#prusaslicer-binary-g-code-g--bgcode)
5
+ - [QOIF (Quite OK Image Format)](#qoif-quite-ok-image-format)
6
+ - [G-code Analysis Tips](#g-code-analysis-tips)
7
+ - [G-code Side View Visualization (0xFun 2026)](#g-code-side-view-visualization-0xfun-2026)
8
+ - [Uncommon File Magic Bytes](#uncommon-file-magic-bytes)
9
+
10
+ ---
11
+
12
+ ## PrusaSlicer Binary G-code (.g / .bgcode)
13
+
14
+ **File magic:** `GCDE` (4 bytes)
15
+
16
+ The `.g` extension is PrusaSlicer's binary G-code format (bgcode). It stores G-code in a block-based structure with compression.
17
+
18
+ **File structure:**
19
+ ```text
20
+ Header: "GCDE"(4) + version(4) + checksum_type(2)
21
+ Blocks: [type(2) + compression(2) + uncompressed_size(4)
22
+ + compressed_size(4) if compressed
23
+ + type-specific fields
24
+ + data + CRC32(4)]
25
+ ```
26
+
27
+ **Block types:**
28
+ - 0 = FileMetadata (has encoding field, 2 bytes)
29
+ - 1 = GCode (has encoding field, 2 bytes)
30
+ - 2 = SlicerMetadata (has encoding field, 2 bytes)
31
+ - 3 = PrinterMetadata (has encoding field, 2 bytes)
32
+ - 4 = PrintMetadata (has encoding field, 2 bytes)
33
+ - 5 = Thumbnail (has format(2) + width(2) + height(2))
34
+
35
+ **Compression types:** 0=None, 1=Deflate, 2=Heatshrink(11,4), 3=Heatshrink(12,4)
36
+
37
+ **Thumbnail formats:** 0=PNG, 1=JPEG, 2=QOI (Quite OK Image)
38
+
39
+ **Parsing and extracting G-code:**
40
+ ```python
41
+ import struct, zlib
42
+ import heatshrink2 # pip install heatshrink2
43
+
44
+ with open('file.g', 'rb') as f:
45
+ data = f.read()
46
+
47
+ pos = 10 # After header
48
+ while pos < len(data) - 8:
49
+ block_type = struct.unpack('<H', data[pos:pos+2])[0]
50
+ compression = struct.unpack('<H', data[pos+2:pos+4])[0]
51
+ uncompressed_size = struct.unpack('<I', data[pos+4:pos+8])[0]
52
+ pos += 8
53
+ if compression != 0:
54
+ compressed_size = struct.unpack('<I', data[pos:pos+4])[0]
55
+ pos += 4
56
+ else:
57
+ compressed_size = uncompressed_size
58
+ # Type-specific extra header fields
59
+ if block_type in [0,1,2,3,4]:
60
+ pos += 2 # encoding field
61
+ elif block_type == 5:
62
+ pos += 6 # format + width + height
63
+ block_data = data[pos:pos+compressed_size]
64
+ pos += compressed_size + 4 # data + CRC32
65
+
66
+ if block_type == 1: # GCode block
67
+ if compression == 3: # Heatshrink 12/4
68
+ gcode = heatshrink2.decompress(block_data, window_sz2=12, lookahead_sz2=4)
69
+ elif compression == 1: # Deflate (zlib)
70
+ gcode = zlib.decompress(block_data)
71
+ # Search gcode for hidden comments/flags
72
+ ```
73
+
74
+ **Common hiding spots:**
75
+ - G-code comments (`;=== FLAG_CHAR ... ===`) at specific layer heights
76
+ - Custom G-code sections (`;TYPE:Custom`)
77
+ - Metadata fields (object names, filament info)
78
+ - Thumbnail images (extract and view QOIF/PNG)
79
+
80
+ ## QOIF (Quite OK Image Format)
81
+
82
+ **Magic:** `qoif` (4 bytes) + width(4 BE) + height(4 BE) + channels(1) + colorspace(1)
83
+
84
+ Lightweight image format used in PrusaSlicer thumbnails. Decode with Python struct or use the `qoi` library.
85
+
86
+ ## G-code Analysis Tips
87
+
88
+ ```bash
89
+ # Search for flag patterns in decompressed gcode
90
+ grep -i "flag\|meta\|ctf\|secret" output.gcode
91
+
92
+ # Look for custom comments at layer changes
93
+ grep ";.*FLAG\|;.*LAYER_CHANGE" output.gcode
94
+
95
+ # Extract XY coordinates for visual patterns
96
+ grep "^G1" output.gcode | awk '{print $2, $3}' > coords.txt
97
+ ```
98
+
99
+ ## G-code Side View Visualization (0xFun 2026)
100
+
101
+ **Pattern (PrintedParts):** Plot X vs Z (side view) with Y filtering. Extrusion segments at specific Y ranges form readable text.
102
+
103
+ ```bash
104
+ # Extract XY coordinates from G-code
105
+ grep "^G1" output.gcode | awk '{print $2, $3}' > coords.txt
106
+ # Plot with matplotlib for visual patterns
107
+ ```
108
+
109
+ **Lesson:** G-code is just coordinate lists. Side projections (XZ or YZ) reveal embossed/engraved text.
110
+
111
+ ---
112
+
113
+ ## Uncommon File Magic Bytes
114
+
115
+ | Magic | Format | Extension | Notes |
116
+ |-------|--------|-----------|-------|
117
+ | `GCDE` | PrusaSlicer binary G-code | `.g`, `.bgcode` | 3D printing, heatshrink compressed |
118
+ | `qoif` | Quite OK Image Format | `.qoi` | Lightweight image format, often embedded |
119
+ | `OggS` | Ogg container | `.ogg` | Audio/video |
120
+ | `RIFF` | RIFF container | `.wav`,`.avi` | Check subformat |
121
+ | `%PDF` | PDF | `.pdf` | Check metadata & embedded objects |