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,524 @@
1
+ # CTF Crypto - Lattice and LWE Attacks
2
+
3
+ ## Table of Contents
4
+ - [Quick Triage: Is This a Lattice Problem?](#quick-triage-is-this-a-lattice-problem)
5
+ - [Core Tools: LLL, BKZ, Babai, CVP, SVP](#core-tools-lll-bkz-babai-cvp-svp-asis-ctf-finals-2015-ctfzone-2017)
6
+ - [LLL](#lll)
7
+ - [BKZ](#bkz)
8
+ - [Babai nearest plane](#babai-nearest-plane)
9
+ - [CVP vs SVP](#cvp-vs-svp)
10
+ - [Hidden Number Problem (HNP): Partial Nonce / Biased Nonce](#hidden-number-problem-hnp-partial-nonce--biased-nonce-nullcon-hackim-2020-ledger-donjon-ctf-2020)
11
+ - [Minimal ECDSA partial-nonce workflow](#minimal-ecdsa-partial-nonce-workflow)
12
+ - [LCG and Truncated Output as a Lattice Problem](#lcg-and-truncated-output-as-a-lattice-problem-x-mas-ctf-2018-fwordctf-2020)
13
+ - [Minimal truncated-LCG workflow](#minimal-truncated-lcg-workflow)
14
+ - [LWE via Embedding and CVP](#lwe-via-embedding-and-cvp-plaidctf-2016-aero-ctf-2020)
15
+ - [Embedding-style lattice](#embedding-style-lattice)
16
+ - [For ternary or sparse secrets](#for-ternary-or-sparse-secrets)
17
+ - [Ring-LWE / Module-LWE Recognition Notes](#ring-lwe--module-lwe-recognition-notes-plaidctf-2016-dicectf-2022)
18
+ - [Flattening Ring-LWE to plain LWE](#flattening-ring-lwe-to-plain-lwe)
19
+ - [Orthogonal Lattices: HSSP / AHSSP Style Recovery](#orthogonal-lattices-hssp--ahssp-style-recovery-zer0pts-ctf-2022)
20
+ - [Subset Sum / Knapsack via Lattice Reduction](#subset-sum--knapsack-via-lattice-reduction-hitcon-ctf-2017-backdoorctf-2023)
21
+ - [Common Failure Modes](#common-failure-modes)
22
+ - [Quick Checklist Before You Commit to Lattices](#quick-checklist-before-you-commit-to-lattices)
23
+
24
+ ---
25
+
26
+ ## Quick Triage: Is This a Lattice Problem?
27
+
28
+ Use lattice tools when the challenge gives you:
29
+
30
+ - many modular equations plus a promise that the hidden values are small, sparse, or close to each other
31
+ - partial leakage of a secret nonce, seed, or state bits
32
+ - linear relations with bounded error terms
33
+ - vectors or matrices over `Z_q` where the true solution should be unusually short
34
+ - a subset-sum or knapsack instance that "looks too structured"
35
+
36
+ Typical CTF phrasing:
37
+
38
+ - "high bits of k are known"
39
+ - "the error is small"
40
+ - "the secret coefficients are in {-1,0,1}"
41
+ - "recover seed from truncated outputs"
42
+ - "find a short vector"
43
+ - "solve noisy linear equations modulo q"
44
+
45
+ **First question to ask:** what is supposed to be small?
46
+
47
+ - the secret itself
48
+ - the error vector
49
+ - the nonce difference
50
+ - a subset indicator vector in `{0,1}^n`
51
+ - a correction term caused by modular wraparound
52
+
53
+ That "small thing" is usually what the lattice is trying to expose.
54
+
55
+ ---
56
+
57
+ ## Core Tools: LLL, BKZ, Babai, CVP, SVP (ASIS CTF Finals 2015, CTFZone 2017)
58
+
59
+ ### LLL
60
+
61
+ Default first move. Fast, easy, often enough for CTF-sized parameters.
62
+
63
+ Use it when:
64
+
65
+ - dimensions are moderate
66
+ - the hidden vector is very short
67
+ - the challenge author clearly expects a standard embedding attack
68
+ - you want structure first, exact recovery second
69
+
70
+ ```python
71
+ from sage.all import Matrix, ZZ
72
+
73
+ M = Matrix(ZZ, basis_rows)
74
+ R = M.LLL()
75
+ print(R[0])
76
+ ```
77
+
78
+ ### BKZ
79
+
80
+ Use when LLL almost works but not quite.
81
+
82
+ - better for harder CVP/SVP instances
83
+ - useful when the gap between the target vector and random lattice vectors is small
84
+ - in CTFs, `BKZ(block_size=20..35)` is often already enough
85
+
86
+ ```python
87
+ R = M.BKZ(block_size=25)
88
+ ```
89
+
90
+ ### Babai nearest plane
91
+
92
+ Good for approximate CVP after reduction.
93
+
94
+ - reduce basis with `LLL` or `BKZ` first
95
+ - then apply Babai to recover the nearby vector
96
+ - often enough for ternary or small-error LWE
97
+
98
+ ```python
99
+ from fpylll import IntegerMatrix, CVP
100
+
101
+ # After building and reducing the lattice basis:
102
+ closest = CVP.babai(B, target)
103
+ ```
104
+
105
+ ### CVP vs SVP
106
+
107
+ - **SVP:** "find an unusually short non-zero lattice vector"
108
+ - **CVP:** "find the lattice vector closest to a target"
109
+
110
+ Rule of thumb:
111
+
112
+ - if you only know "some relation must be very short", think SVP / embedding
113
+ - if you already have a target vector and want the nearest valid lattice point, think CVP / Babai
114
+
115
+ ---
116
+
117
+ ## Hidden Number Problem (HNP): Partial Nonce / Biased Nonce (nullcon HackIM 2020, Ledger Donjon CTF 2020)
118
+
119
+ **Pattern:** signatures or RNG equations leak a few bits of a hidden value `k`, or `k` is sampled from a small / biased range.
120
+
121
+ This is the classic route from:
122
+
123
+ - ECDSA partial nonce leakage
124
+ - Schnorr biased nonce leakage
125
+ - custom congruence systems where only high bits or low bits are known
126
+
127
+ Generic shape:
128
+
129
+ `a_i * x + b_i ≡ e_i (mod q)`
130
+
131
+ where:
132
+
133
+ - `x` is the secret key
134
+ - `e_i` is small or partially known
135
+
136
+ That "small error" is what turns the problem into a lattice instance.
137
+
138
+ **When to use:**
139
+
140
+ - repeated signatures with leaked high bits / low bits of `k`
141
+ - same signing scheme with biased or short nonces
142
+ - LCG-like recurrence where each output leaks only part of the internal state
143
+
144
+ **Practical workflow:**
145
+
146
+ 1. normalize all equations so the secret key is the same unknown in every row
147
+ 2. isolate the bounded error term
148
+ 3. scale rows so all coordinates have comparable size
149
+ 4. run `LLL`
150
+ 5. test the candidate secret against the original equations
151
+
152
+ Skeleton:
153
+
154
+ ```python
155
+ from sage.all import Matrix, ZZ
156
+
157
+ def build_hnp_lattice(q, coeffs, bounds):
158
+ n = len(coeffs)
159
+ rows = []
160
+ for i in range(n):
161
+ row = [0] * (n + 1)
162
+ row[i] = q
163
+ rows.append(row)
164
+
165
+ last = [c for c in coeffs] + [bounds]
166
+ rows.append(last)
167
+ return Matrix(ZZ, rows)
168
+ ```
169
+
170
+ **Key insight:** HNP attacks usually do not require a perfect lattice model. In CTFs, once the true secret produces a vector much shorter than random noise, `LLL` often exposes it directly or gets you close enough to brute-force the last few bits.
171
+
172
+ ### Minimal ECDSA partial-nonce workflow
173
+
174
+ If a challenge leaks the top bits of each nonce `k_i`, write:
175
+
176
+ `k_i = leaked_i * 2^t + delta_i`
177
+
178
+ where `delta_i` is small. For ECDSA:
179
+
180
+ `s_i * k_i - h_i ≡ r_i * d (mod q)`
181
+
182
+ Substitute the leaked form of `k_i`:
183
+
184
+ `r_i * d - s_i * delta_i ≡ s_i * leaked_i * 2^t - h_i (mod q)`
185
+
186
+ Now the unknowns are:
187
+
188
+ - the private key `d`
189
+ - a set of small corrections `delta_i`
190
+
191
+ That is the lattice hook.
192
+
193
+ Minimal starter code:
194
+
195
+ ```python
196
+ from sage.all import Matrix, ZZ
197
+
198
+ def build_ecdsa_partial_nonce_lattice(q, rs, ss, hs, leaked, t):
199
+ n = len(rs)
200
+ M = Matrix(ZZ, n + 2, n + 2)
201
+
202
+ for i in range(n):
203
+ M[i, i] = q
204
+
205
+ for i in range(n):
206
+ M[n, i] = ss[i]
207
+ M[n + 1, i] = (hs[i] - ss[i] * leaked[i] * (1 << t)) % q
208
+
209
+ M[n, n] = 1
210
+ M[n + 1, n + 1] = q // (1 << t)
211
+ return M
212
+ ```
213
+
214
+ What to do next:
215
+
216
+ 1. build the lattice
217
+ 2. run `LLL`
218
+ 3. inspect short rows for a plausible `d`
219
+ 4. verify `d` against all signatures
220
+ 5. if one or two bits are off, brute-force the remaining uncertainty
221
+
222
+ **When this works best:** many signatures, enough leaked bits per nonce, and a single long-term signing key shared across all samples.
223
+
224
+ ---
225
+
226
+ ## LCG and Truncated Output as a Lattice Problem (X-MAS CTF 2018, FwordCTF 2020)
227
+
228
+ **Pattern:** internal state follows an affine recurrence, but you only see:
229
+
230
+ - high bits
231
+ - low bits
232
+ - several states with unknown parameters
233
+ - several consecutive outputs plus a small hidden correction
234
+
235
+ Typical examples:
236
+
237
+ - unknown seed, known modulus
238
+ - known modulus, known `a`, known `b`, only top bits of outputs
239
+ - unknown `a`, unknown `b`, several exact or truncated outputs
240
+
241
+ The trick is to rewrite:
242
+
243
+ `state_i = observed_i * 2^t + hidden_i`
244
+
245
+ where `hidden_i` is small. Then the recurrence becomes a modular linear relation in those small hidden values.
246
+
247
+ **When to use:**
248
+
249
+ - high-bit leakage from LCG states
250
+ - recurrence modulo a large prime
251
+ - multiple consecutive outputs
252
+ - exact algebra seems messy but every step differs only by a small hidden remainder
253
+
254
+ **Key insight:** truncated-state recovery is often just HNP wearing different clothes. If the unknown carries per row are small enough, the lattice will expose them.
255
+
256
+ ### Minimal truncated-LCG workflow
257
+
258
+ Suppose:
259
+
260
+ `x_{i+1} = a*x_i + b (mod m)`
261
+
262
+ but the service leaks only the high bits:
263
+
264
+ `y_i = x_i >> t`
265
+
266
+ Then write:
267
+
268
+ `x_i = y_i * 2^t + z_i`
269
+
270
+ where `z_i` is the hidden low-bit part and is small.
271
+
272
+ Plugging into the recurrence gives:
273
+
274
+ `y_{i+1} * 2^t + z_{i+1} ≡ a*(y_i * 2^t + z_i) + b (mod m)`
275
+
276
+ Rearrange:
277
+
278
+ `z_{i+1} - a*z_i ≡ a*y_i*2^t + b - y_{i+1}*2^t (mod m)`
279
+
280
+ Now the unknowns are the small `z_i`. That is exactly the kind of bounded modular relation lattices like.
281
+
282
+ Minimal starter code:
283
+
284
+ ```python
285
+ from sage.all import Matrix, ZZ
286
+
287
+ def build_truncated_lcg_lattice(m, a, b, ys, t):
288
+ n = len(ys) - 1
289
+ M = Matrix(ZZ, n + 1, n + 1)
290
+
291
+ for i in range(n):
292
+ M[i, i] = m
293
+
294
+ for i in range(n):
295
+ rhs = (a * ys[i] * (1 << t) + b - ys[i + 1] * (1 << t)) % m
296
+ M[n, i] = rhs
297
+
298
+ M[n, n] = 1 << t
299
+ return M
300
+ ```
301
+
302
+ What to do next:
303
+
304
+ 1. use several consecutive outputs
305
+ 2. run `LLL`
306
+ 3. recover candidate low bits `z_i`
307
+ 4. reconstruct full states `x_i`
308
+ 5. verify the recurrence exactly
309
+
310
+ **When this works best:** modulus is known, leakage is consecutive, and the hidden low part is much smaller than the modulus.
311
+
312
+ ---
313
+
314
+ ## LWE via Embedding and CVP (PlaidCTF 2016, Aero CTF 2020)
315
+
316
+ **Pattern:** given `A`, `b`, modulus `q`, and the promise:
317
+
318
+ `b = A*s + e (mod q)`
319
+
320
+ where:
321
+
322
+ - `s` is small or sparse
323
+ - `e` is small
324
+
325
+ This is the standard LWE shape.
326
+
327
+ **Immediate checks:**
328
+
329
+ - are coefficients of `s` in `{-1,0,1}` or a tiny range?
330
+ - is the error noticeably smaller than `q`?
331
+ - does the challenge give many rows and only a few columns?
332
+ - does solving over the integers almost work except for modular wraparound?
333
+
334
+ ### Embedding-style lattice
335
+
336
+ ```python
337
+ from sage.all import Matrix, ZZ, identity_matrix, zero_matrix, block_matrix
338
+
339
+ def lwe_embedding(A, q):
340
+ m, n = A.nrows(), A.ncols()
341
+ top = block_matrix([[q * identity_matrix(m), zero_matrix(ZZ, m, n)]])
342
+ bottom = block_matrix([[A.transpose(), identity_matrix(n)]])
343
+ return block_matrix([[top], [bottom]])
344
+ ```
345
+
346
+ Then:
347
+
348
+ - reduce the basis
349
+ - use Babai / nearest-plane on the target
350
+ - recover the short secret / error pair
351
+
352
+ ### For ternary or sparse secrets
353
+
354
+ After CVP:
355
+
356
+ - map near-zero values back into `{-1,0,1}`
357
+ - test both endian choices
358
+ - test both "row vectors" and "column vectors" conventions
359
+
360
+ **Key insight:** many CTF LWE instances are intentionally below the "real cryptography" hardness line. The challenge is usually not defeating production-grade LWE, but noticing that the secret or error was chosen tiny enough for `LLL + Babai` to work.
361
+
362
+ ---
363
+
364
+ ## Ring-LWE / Module-LWE Recognition Notes (PlaidCTF 2016, DiceCTF 2022)
365
+
366
+ You should suspect Ring-LWE / Module-LWE when:
367
+
368
+ - objects are polynomials modulo `x^n ± 1`
369
+ - multiplication is cyclic or negacyclic convolution
370
+ - samples look like `(a(x), b(x)=a(x)s(x)+e(x))`
371
+ - coefficients are reduced modulo `q`
372
+
373
+ In many CTFs, the intended shortcut is not a full Ring-LWE attack, but one of these:
374
+
375
+ - coefficients are tiny enough to lift to integers directly
376
+ - the ring structure decouples into easier scalar problems
377
+ - the service leaks enough evaluations to turn the problem into plain LWE
378
+ - one representation bug breaks the intended hardness
379
+
380
+ **Practical advice:**
381
+
382
+ - first try to flatten the polynomial problem into vectors
383
+ - test coefficient embedding before chasing deeper algebra
384
+ - check whether NTT / inverse NTT is used incorrectly
385
+ - check sign conventions, endian order, and whether coefficients were centered into `[-q/2, q/2]`
386
+
387
+ ### Flattening Ring-LWE to plain LWE
388
+
389
+ ```python
390
+ from sage.all import Matrix, ZZ, vector
391
+
392
+ def ring_lwe_to_matrix(a_poly, n, q):
393
+ """Flatten a(x) in Z_q[x]/(x^n+1) to its negacyclic rotation matrix."""
394
+ coeffs = list(a_poly) + [0] * (n - len(list(a_poly)))
395
+ rows = []
396
+ for i in range(n):
397
+ row = [0] * n
398
+ for j in range(n):
399
+ idx = (i - j) % n
400
+ sign = -1 if (i - j) < 0 and ((i - j) % n) != 0 else 1
401
+ # negacyclic: x^n = -1
402
+ if j <= i:
403
+ row[j] = coeffs[i - j]
404
+ else:
405
+ row[j] = -coeffs[n + i - j]
406
+ rows.append(row)
407
+ return Matrix(ZZ, rows)
408
+ # After flattening, treat as plain LWE: b_vec = A_mat * s_vec + e_vec (mod q)
409
+ ```
410
+
411
+ **Key insight:** most Ring-LWE / Module-LWE CTF challenges are weakened by implementation mistakes, tiny errors, or over-structured secrets. Flatten to plain LWE first and check whether standard lattice tools solve it before pursuing ring-specific attacks.
412
+
413
+ ---
414
+
415
+ ## Orthogonal Lattices: HSSP / AHSSP Style Recovery (zer0pts CTF 2022)
416
+
417
+ **Pattern:** you do not directly know the secret matrix or subset, but you can construct vectors that should be orthogonal to it modulo `M` or `p`.
418
+
419
+ This often appears in hidden-subset style problems:
420
+
421
+ - recover a hidden binary matrix
422
+ - recover a hidden low-weight subspace
423
+ - reconstruct unknown rows from modular inner-product relations
424
+
425
+ Core workflow:
426
+
427
+ 1. build a lattice whose short vectors represent orthogonal relations
428
+ 2. reduce it
429
+ 3. recover the orthogonal lattice
430
+ 4. take the kernel / orthogonal complement
431
+ 5. reduce again to expose the hidden binary or short basis
432
+
433
+ ```python
434
+ from sage.all import Matrix, ZZ, identity_matrix, block_matrix
435
+
436
+ def orthogonal_lattice_recovery(H, M):
437
+ """Recover hidden binary basis from h = alpha * A (mod M).
438
+
439
+ H: observed matrix (k x n) over Z_M
440
+ M: modulus
441
+ Returns: LLL-reduced orthogonal lattice whose kernel reveals A.
442
+ """
443
+ k, n = H.nrows(), H.ncols()
444
+ # Build lattice: [M*I_k | 0; H^T | I_n]
445
+ top = block_matrix([[M * identity_matrix(k), Matrix(ZZ, k, n)]])
446
+ bot = block_matrix([[H.change_ring(ZZ).transpose(), identity_matrix(n)]])
447
+ L = block_matrix([[top], [bot]])
448
+ L_reduced = L.LLL()
449
+ # Short rows in the bottom-right block are orthogonal to the hidden basis
450
+ return L_reduced
451
+ ```
452
+
453
+ **When to use:**
454
+
455
+ - challenge gives `h = αA` or affine variants of that relation
456
+ - unknown matrix entries are in `{0,1}` or another tiny alphabet
457
+ - direct solving fails because the structure lives in an unknown subspace
458
+
459
+ **Key insight:** in these problems, the shortest vectors are not the answer itself. They are the doorway to the answer: first recover the orthogonal space, then turn back and reconstruct the hidden basis.
460
+
461
+ ---
462
+
463
+ ## Subset Sum / Knapsack via Lattice Reduction (HITCON CTF 2017, BackdoorCTF 2023)
464
+
465
+ **Pattern:** recover a binary vector `x_i ∈ {0,1}` such that:
466
+
467
+ `sum(a_i * x_i) = target`
468
+
469
+ This is the classic subset-sum / knapsack lattice setup.
470
+
471
+ Use it when:
472
+
473
+ - the instance is intentionally low-density
474
+ - the hidden vector is binary
475
+ - direct meet-in-the-middle is still too large
476
+
477
+ Skeleton:
478
+
479
+ ```python
480
+ from sage.all import Matrix, ZZ
481
+
482
+ def knapsack_lattice(weights, target):
483
+ n = len(weights)
484
+ M = Matrix(ZZ, n + 1, n + 1)
485
+ for i in range(n):
486
+ M[i, i] = 1
487
+ M[i, n] = weights[i]
488
+ M[n, n] = -target
489
+ return M
490
+ ```
491
+
492
+ Then:
493
+
494
+ - run `LLL`
495
+ - look for a row whose last coordinate is `0`
496
+ - check whether the remaining coordinates are in `{0,1}` or `{−1,0,1}`
497
+
498
+ **Key insight:** the lattice is built so that the correct subset produces a vector with an abnormally small final coordinate. In easy CTF instances, that vector survives reduction.
499
+
500
+ ---
501
+
502
+ ## Common Failure Modes
503
+
504
+ - **Wrong scaling:** one coordinate dominates the basis and hides the short vector.
505
+ - **Wrong centering:** values should be mapped to `[-q/2, q/2]`, not kept in `[0, q)`.
506
+ - **Wrong orientation:** rows vs columns are swapped.
507
+ - **Too few samples:** the lattice exists, but not enough equations pin the secret down.
508
+ - **Noise too large:** `LLL` is not enough; try `BKZ`, better scaling, or a different embedding.
509
+ - **Mistaken problem type:** what looks like LWE may actually be plain linear algebra, CRT, or a bugged encoding problem.
510
+ - **Forgot brute-force finish:** lattice often gets you "almost correct"; the last few bits or signs may still need a tiny brute force.
511
+
512
+ ---
513
+
514
+ ## Quick Checklist Before You Commit to Lattices
515
+
516
+ - Can I write the unknown as "small secret" or "small error"?
517
+ - Is there a bounded term that should make one vector much shorter than random?
518
+ - Did I try centering coefficients?
519
+ - Did I test both row/column conventions?
520
+ - Did I try `LLL` first before building something more exotic?
521
+ - If `LLL` almost works, did I try `BKZ` or Babai?
522
+ - If the instance is polynomial-based, did I first flatten it into coefficient vectors?
523
+
524
+ If most answers are "yes", the challenge is very likely meant to be solved with lattice reduction.