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,487 @@
1
+ # CTF Crypto - RSA Attacks
2
+
3
+ ## Table of Contents
4
+ - [Small Public Exponent (Cube Root)](#small-public-exponent-cube-root)
5
+ - [Common Modulus Attack](#common-modulus-attack)
6
+ - [Wiener's Attack (Small Private Exponent)](#wieners-attack-small-private-exponent)
7
+ - [Pollard's p-1 Factorization](#pollards-p-1-factorization)
8
+ - [Hastad's Broadcast Attack](#hastads-broadcast-attack)
9
+ - [RSA with Consecutive Primes (Fermat Factorization)](#rsa-with-consecutive-primes-fermat-factorization)
10
+ - [Multi-Prime RSA](#multi-prime-rsa)
11
+ - [RSA with Restricted-Digit Primes (LACTF 2026)](#rsa-with-restricted-digit-primes-lactf-2026)
12
+ - [Coppersmith for Structured RSA Primes (LACTF 2026)](#coppersmith-for-structured-rsa-primes-lactf-2026)
13
+ - [Manger's RSA Padding Oracle Attack (Nullcon 2026)](#mangers-rsa-padding-oracle-attack-nullcon-2026)
14
+ - [Manger's Attack on RSA-OAEP via Timing Oracle (HTB Early Bird)](#mangers-attack-on-rsa-oaep-via-timing-oracle-htb-early-bird)
15
+ - [Polynomial Hash with Trivial Root (Pragyan 2026)](#polynomial-hash-with-trivial-root-pragyan-2026)
16
+ - [Polynomial CRT in GF(2)\[x\] (Nullcon 2026)](#polynomial-crt-in-gf2x-nullcon-2026)
17
+ - [Affine Cipher over Non-Prime Modulus (Nullcon 2026)](#affine-cipher-over-non-prime-modulus-nullcon-2026)
18
+ - [Hastad Broadcast Attack with Linear Padding -- Coppersmith (PlaidCTF 2017)](#hastad-broadcast-attack-with-linear-padding----coppersmith-plaidctf-2017)
19
+ - [Franklin-Reiter Related Message Attack on RSA e=3 (N1CTF 2018)](#franklin-reiter-related-message-attack-on-rsa-e3-n1ctf-2018)
20
+ - [Coppersmith Attack on Linearly-Related RSA Primes (ASIS CTF 2018)](#coppersmith-attack-on-linearly-related-rsa-primes-asis-ctf-2018)
21
+ - [rsa-attacks-2.md: RSA p=q Validation Bypass (BearCatCTF 2026)](rsa-attacks-2.md#rsa-pq-validation-bypass-bearcatctf-2026)
22
+ - [rsa-attacks-2.md: RSA Cube Root CRT when gcd(e, phi) > 1 (BearCatCTF 2026)](rsa-attacks-2.md#rsa-cube-root-crt-when-gcde-phi--1-bearcatctf-2026)
23
+ - [rsa-attacks-2.md: Factoring n from Multiple of phi(n) (BearCatCTF 2026)](rsa-attacks-2.md#factoring-n-from-multiple-of-phin-bearcatctf-2026)
24
+ - [rsa-attacks-2.md: RSA Signature Forgery via Multiplicative Homomorphism (MMA CTF 2015)](rsa-attacks-2.md#rsa-signature-forgery-via-multiplicative-homomorphism-mma-ctf-2015)
25
+ - [rsa-attacks-2.md: Weak RSA Key Generation via Base Representation (Sharif CTF 2016)](rsa-attacks-2.md#weak-rsa-key-generation-via-base-representation-sharif-ctf-2016)
26
+ - [rsa-attacks-2.md: RSA with gcd(e, phi(n)) > 1 (CSAW 2015)](rsa-attacks-2.md#rsa-with-gcde-phin--1-csaw-2015)
27
+ - [rsa-attacks-2.md: Batch GCD for Shared Prime Factoring (BSidesSF 2025)](rsa-attacks-2.md#batch-gcd-for-shared-prime-factoring-bsidessf-2025)
28
+ - [rsa-attacks-2.md: RSA Partial Key Recovery from dp dq qinv (0CTF 2016)](rsa-attacks-2.md#rsa-partial-key-recovery-from-dp-dq-qinv-0ctf-2016)
29
+ - [rsa-attacks-2.md: RSA-CRT Fault Attack / Bit-Flip Recovery (CSAW CTF 2016)](rsa-attacks-2.md#rsa-crt-fault-attack--bit-flip-recovery-csaw-ctf-2016)
30
+ - [rsa-attacks-2.md: RSA Homomorphic Decryption Oracle Bypass (ECTF 2016)](rsa-attacks-2.md#rsa-homomorphic-decryption-oracle-bypass-ectf-2016)
31
+ - [rsa-attacks-2.md: RSA with Small Prime Factors and CRT Decomposition (Hack The Vote 2016)](rsa-attacks-2.md#rsa-with-small-prime-factors-and-crt-decomposition-hack-the-vote-2016)
32
+
33
+ ---
34
+
35
+ ## Small Public Exponent (Cube Root)
36
+
37
+ **Pattern:** Small `e` (typically 3) with small message. When `m^e < n`, the ciphertext is just `m^e` without modular reduction — take the integer eth root.
38
+
39
+ ```python
40
+ import gmpy2
41
+
42
+ def small_e_attack(c, e):
43
+ """Recover plaintext when m^e < n (no modular wrap)."""
44
+ m, exact = gmpy2.iroot(c, e)
45
+ if exact:
46
+ return int(m)
47
+ return None
48
+
49
+ # Usage
50
+ m = small_e_attack(c, e=3)
51
+ print(bytes.fromhex(hex(m)[2:]))
52
+ ```
53
+
54
+ **When it fails:** If `m^e > n` (message padded or large), the modular reduction destroys the simple root. In that case, try Hastad's broadcast attack or Coppersmith's short-pad attack.
55
+
56
+ ---
57
+
58
+ ## Common Modulus Attack
59
+
60
+ **Pattern:** Same message encrypted with same `n` but two different public exponents `e1`, `e2` where `gcd(e1, e2) = 1`. Recover plaintext without factoring `n`.
61
+
62
+ ```python
63
+ from math import gcd
64
+
65
+ def common_modulus_attack(c1, c2, e1, e2, n):
66
+ """Recover plaintext from two encryptions with same n, coprime e1/e2."""
67
+ # Extended GCD: find a, b such that a*e1 + b*e2 = 1
68
+ def extended_gcd(a, b):
69
+ if a == 0: return b, 0, 1
70
+ g, x, y = extended_gcd(b % a, a)
71
+ return g, y - (b // a) * x, x
72
+
73
+ g, a, b = extended_gcd(e1, e2)
74
+ assert g == 1, "e1 and e2 must be coprime"
75
+
76
+ # m = c1^a * c2^b mod n
77
+ # Handle negative exponent by using modular inverse
78
+ if a < 0:
79
+ c1 = pow(c1, -1, n)
80
+ a = -a
81
+ if b < 0:
82
+ c2 = pow(c2, -1, n)
83
+ b = -b
84
+ m = (pow(c1, a, n) * pow(c2, b, n)) % n
85
+ return m
86
+ ```
87
+
88
+ **Key insight:** Two encryptions of the same message under the same modulus but different exponents leak the plaintext via Bezout's identity. No factoring required.
89
+
90
+ ---
91
+
92
+ ## Wiener's Attack (Small Private Exponent)
93
+
94
+ **Pattern:** Private exponent `d` is small (d < N^0.25). The continued fraction expansion of `e/n` reveals `d`.
95
+
96
+ ```python
97
+ def wiener_attack(e, n):
98
+ """Recover d when d < N^0.25 using continued fraction expansion of e/n."""
99
+ def continued_fraction(num, den):
100
+ cf = []
101
+ while den:
102
+ q, r = divmod(num, den)
103
+ cf.append(q)
104
+ num, den = den, r
105
+ return cf
106
+
107
+ def convergents(cf):
108
+ convs = []
109
+ h0, h1 = 0, 1
110
+ k0, k1 = 1, 0
111
+ for a in cf:
112
+ h0, h1 = h1, a * h1 + h0
113
+ k0, k1 = k1, a * k1 + k0
114
+ convs.append((h1, k1))
115
+ return convs
116
+
117
+ cf = continued_fraction(e, n)
118
+ for k, d in convergents(cf):
119
+ if k == 0:
120
+ continue
121
+ # Check if d is valid: phi = (e*d - 1) / k must be integer
122
+ if (e * d - 1) % k != 0:
123
+ continue
124
+ phi = (e * d - 1) // k
125
+ # phi = (p-1)(q-1) = n - p - q + 1, so p+q = n - phi + 1
126
+ s = n - phi + 1
127
+ # p and q are roots of x^2 - s*x + n = 0
128
+ discriminant = s * s - 4 * n
129
+ if discriminant < 0:
130
+ continue
131
+ from math import isqrt
132
+ t = isqrt(discriminant)
133
+ if t * t == discriminant:
134
+ return d
135
+ return None
136
+
137
+ # Usage
138
+ d = wiener_attack(e, n)
139
+ m = pow(c, d, n)
140
+ ```
141
+
142
+ **When to use:** Very large `e` (close to `n`) often indicates small `d`. Also try `owiener` Python package: `pip install owiener`.
143
+
144
+ ---
145
+
146
+ ## Pollard's p-1 Factorization
147
+
148
+ **Pattern:** One prime factor `p` has a smooth `p-1` (all prime factors of `p-1` are small). Compute `a^(B!) mod n`; GCD with `n` reveals `p`.
149
+
150
+ ```python
151
+ from math import gcd
152
+
153
+ def pollard_p1(n, B=100000):
154
+ """Factor n when p-1 is B-smooth for some prime factor p."""
155
+ a = 2
156
+ for j in range(2, B + 1):
157
+ a = pow(a, j, n)
158
+ d = gcd(a - 1, n)
159
+ if 1 < d < n:
160
+ return d, n // d
161
+ return None
162
+
163
+ # Usage
164
+ result = pollard_p1(n)
165
+ if result:
166
+ p, q = result
167
+ ```
168
+
169
+ **Key insight:** By Fermat's little theorem, if `p-1` divides `B!`, then `a^(B!) ≡ 1 (mod p)`, so `gcd(a^(B!) - 1, n)` gives `p`. Increase `B` for larger smooth bounds. CTF primes generated with `getStrongPrime()` or similar are resistant.
170
+
171
+ ---
172
+
173
+ ## Hastad's Broadcast Attack
174
+
175
+ **Pattern:** Same plaintext `m` encrypted with `e` different public keys (all with exponent `e`, typically `e=3`). Use CRT to reconstruct `m^e`, then take the eth root.
176
+
177
+ ```python
178
+ from functools import reduce
179
+
180
+ def hastad_broadcast(ciphertexts, moduli, e):
181
+ """Recover m from e encryptions with the same exponent e."""
182
+ assert len(ciphertexts) >= e and len(moduli) >= e
183
+
184
+ # Chinese Remainder Theorem
185
+ def crt(remainders, moduli):
186
+ N = reduce(lambda a, b: a * b, moduli)
187
+ result = 0
188
+ for r, m in zip(remainders, moduli):
189
+ Ni = N // m
190
+ Mi = pow(Ni, -1, m)
191
+ result += r * Ni * Mi
192
+ return result % N
193
+
194
+ # CRT gives m^e (mod N1*N2*...*Ne)
195
+ # Since m < each Ni, m^e < N1*N2*...*Ne, so no modular reduction occurred
196
+ me = crt(ciphertexts[:e], moduli[:e])
197
+
198
+ import gmpy2
199
+ m, exact = gmpy2.iroot(me, e)
200
+ if exact:
201
+ return int(m)
202
+ return None
203
+
204
+ # Usage (e=3, three encryptions)
205
+ m = hastad_broadcast([c1, c2, c3], [n1, n2, n3], e=3)
206
+ print(bytes.fromhex(hex(m)[2:]))
207
+ ```
208
+
209
+ **Key insight:** CRT reconstructs `m^e` exactly (no modular reduction) because `m < min(n_i)` and therefore `m^e < n_1 * n_2 * ... * n_e`. Taking the integer eth root recovers `m`.
210
+
211
+ ---
212
+
213
+ ## Hastad Broadcast Attack with Linear Padding -- Coppersmith (PlaidCTF 2017)
214
+
215
+ **Pattern:** Extension of Hastad's broadcast attack when each recipient applies a known linear transform `c_i = (a_i * m + b_i)^e mod n_i` before encryption.
216
+
217
+ ```python
218
+ # Standard Hastad requires identical plaintext
219
+ # With linear padding: each ciphertext encrypts a_i*m + b_i
220
+ # Use CRT + Coppersmith's small_roots on the resulting polynomial
221
+
222
+ from sage.all import *
223
+ # Combine via CRT
224
+ N = prod(n_values)
225
+ T = [crt_coefficient(i, n_values) for i in range(e)]
226
+
227
+ P = PolynomialRing(Zmod(N), 'x')
228
+ x = P.gen()
229
+ poly = sum(T[i] * ((a[i]*x + b[i])**e - c[i]) for i in range(e))
230
+ poly = poly.monic()
231
+
232
+ # Coppersmith's method finds small root
233
+ roots = poly.small_roots(epsilon=1/30)
234
+ flag = int(roots[0])
235
+ ```
236
+
237
+ **Key insight:** When the same message is encrypted with `e` different moduli but each applies a known affine transform `a_i * m + b_i`, CRT combines the congruences into a single polynomial of degree `e` over `Z/NZ`. Coppersmith's method recovers `m` as a small root, generalizing Hastad's attack beyond identical plaintexts.
238
+
239
+ **References:** PlaidCTF 2017
240
+
241
+ ---
242
+
243
+ ### Franklin-Reiter Related Message Attack on RSA e=3 (N1CTF 2018)
244
+
245
+ **Pattern:** When server encrypts `m+padding` where `padding = sha256(user_input)` and `e=3`, two ciphertexts with known padding difference allow polynomial GCD in `Zmod(n)` to recover `m`. (N1CTF 2018)
246
+
247
+ ```python
248
+ # SageMath
249
+ def franklin_reiter(n, pad1, pad2, c1, c2):
250
+ R.<X> = PolynomialRing(Zmod(n))
251
+ f1 = (X + pad1)^3 - c1
252
+ f2 = (X + pad2)^3 - c2
253
+ return -gcd(f1, f2).coefficients()[0]
254
+ ```
255
+
256
+ **Key insight:** With RSA e=3, if the same message `m` is encrypted with two known affine transformations (`m+pad1`, `m+pad2`), polynomial GCD over `Zmod(n)` recovers `m` directly. Works whenever the padding difference is known, even without knowing the full padding.
257
+
258
+ ---
259
+
260
+ ### Coppersmith Attack on Linearly-Related RSA Primes (ASIS CTF 2018)
261
+
262
+ **Pattern:** When RSA primes have a near-linear relation `q ~ 4p`, approximate `q` from `sqrt(4*n)`, then use Coppersmith's `small_roots` to find the error term. (ASIS CTF 2018)
263
+
264
+ ```python
265
+ # SageMath
266
+ qbar = isqrt(4 * n)
267
+ R.<x> = PolynomialRing(Zmod(n))
268
+ f = x + qbar
269
+ roots = f.small_roots(X=2^200, beta=0.5) # find small error term
270
+ q = qbar + int(roots[0])
271
+ p = n // q
272
+ ```
273
+
274
+ **Key insight:** When `q ~ k*p` for known `k`, then `q ~ sqrt(k*n)`. The difference between `q` and this approximation is small enough for Coppersmith's method. This generalizes Fermat factorization to non-consecutive primes with known ratio.
275
+
276
+ ---
277
+
278
+ ## RSA with Consecutive Primes (Fermat Factorization)
279
+
280
+ **Pattern (Loopy Primes):** q = next_prime(p), making p ~ q ~ sqrt(N). Also known as Fermat factorization — works whenever `|p - q|` is small.
281
+
282
+ **Factorization:** Find first prime below sqrt(N):
283
+ ```python
284
+ from sympy import nextprime, prevprime, isqrt
285
+
286
+ root = isqrt(n)
287
+ p = prevprime(root + 1)
288
+ while n % p != 0:
289
+ p = prevprime(p)
290
+ q = n // p
291
+ ```
292
+
293
+ **Multi-layer variant:** 1024 nested RSA encryptions, each with consecutive primes of increasing bit size. Decrypt in reverse order.
294
+
295
+ ---
296
+
297
+ ## Multi-Prime RSA
298
+
299
+ When N is product of many small primes (not just p*q):
300
+ ```python
301
+ # Factor N (easier when many primes)
302
+ from sympy import factorint
303
+ factors = factorint(n) # Returns {p1: e1, p2: e2, ...}
304
+
305
+ # Compute phi using all factors
306
+ phi = 1
307
+ for p, e in factors.items():
308
+ phi *= (p - 1) * (p ** (e - 1))
309
+
310
+ d = pow(e, -1, phi)
311
+ plaintext = pow(ciphertext, d, n)
312
+ ```
313
+
314
+ ---
315
+
316
+ ## RSA with Restricted-Digit Primes (LACTF 2026)
317
+
318
+ **Pattern (six-seven):** RSA primes p, q composed only of digits {6, 7}, ending in 7.
319
+
320
+ **Digit-by-digit factoring from LSB:**
321
+ ```python
322
+ # At each step k, we know p mod 10^k -> compute q mod 10^k = n * p^{-1} mod 10^k
323
+ # Prune: only keep candidates where digit k of both p and q is in {6, 7}
324
+ candidates = [(6,), (7,)] # p ends in 6 or 7
325
+ for k in range(1, num_digits):
326
+ new_candidates = []
327
+ for p_digits in candidates:
328
+ for d in [6, 7]:
329
+ p_val = sum(p_digits[i] * 10**i for i in range(len(p_digits))) + d * 10**k
330
+ q_val = (n * pow(p_val, -1, 10**(k+1))) % 10**(k+1)
331
+ q_digit_k = (q_val // 10**k) % 10
332
+ if q_digit_k in {6, 7}:
333
+ new_candidates.append(p_digits + (d,))
334
+ candidates = new_candidates
335
+ ```
336
+
337
+ **General lesson:** When prime digits are restricted to a small set, digit-by-digit recovery from LSB with modular arithmetic prunes exponentially. Works for any restricted character set.
338
+
339
+ ---
340
+
341
+ ## Coppersmith for Structured RSA Primes (LACTF 2026)
342
+
343
+ **Pattern (six-seven-again):** p = base + 10^k * x where base is fully known and x is small (x < N^0.25).
344
+
345
+ **Attack via SageMath:**
346
+ ```python
347
+ # Construct f(x) such that f(x_secret) = 0 (mod p) and thus (mod N)
348
+ # p = base + 10^k * x -> x + base * (10^k)^{-1} = 0 (mod p)
349
+ R.<x> = PolynomialRing(Zmod(N))
350
+ f = x + (base * inverse_mod(10**k, N)) % N
351
+ roots = f.small_roots(X=2**70, beta=0.5) # x < N^0.25
352
+ ```
353
+
354
+ **When to use:** Whenever part of a prime is known and the unknown part is small enough for Coppersmith bounds (< N^{1/e} for degree-e polynomial, approximately N^0.25 for linear).
355
+
356
+ ---
357
+
358
+ ## Manger's RSA Padding Oracle Attack (Nullcon 2026)
359
+
360
+ **Pattern (TLS, Nullcon 2026):** RSA-encrypted key with threshold oracle. Phase 1: double f until `k*f >= threshold`. Phase 2: binary search. ~128 total queries for 64-bit key.
361
+
362
+ See [advanced-math.md](advanced-math.md) for full implementation.
363
+
364
+ ---
365
+
366
+ ## Manger's Attack on RSA-OAEP via Timing Oracle (HTB Early Bird)
367
+
368
+ **Pattern:** Flask app implements RSA-OAEP with custom hash (PBKDF2, 2M iterations). Python's short-circuit `or` evaluation creates a timing oracle: if the first byte Y != 0, PBKDF2 is never called (~0.6s). If Y == 0, PBKDF2 runs (~2s).
369
+
370
+ **Vulnerable code pattern:**
371
+ ```python
372
+ if Y != 0 or not self.H_verify(self.L, DB[:self.hLen]) or self.os2ip(PS) != 0:
373
+ return {"ok": False, "error": "decryption error"}
374
+ ```
375
+
376
+ **Oracle mapping:** Fast response → Y != 0 (decrypted message >= B). Slow response → Y == 0 (decrypted message < B = 2^(8*(k-1))).
377
+
378
+ **Calibration for network reliability:**
379
+ ```python
380
+ def calibrate(n, e, k):
381
+ B = pow(2, 8 * (k - 1))
382
+ slow_times, fast_times = [], []
383
+ for i in range(5):
384
+ # Known-slow: encrypt values < B
385
+ enc = pow(B - 1 - i*100, e, n).to_bytes(k, 'big')
386
+ slow_times.append(measure(enc))
387
+ # Known-fast: encrypt values > B
388
+ enc = pow(B + 1 + i*100, e, n).to_bytes(k, 'big')
389
+ fast_times.append(measure(enc))
390
+ FAST_UPPER = max(fast_times) * 1.5
391
+ SLOW_LOWER = min(slow_times) * 0.9
392
+ ```
393
+
394
+ **Oracle with retry for ambiguous results:**
395
+ ```python
396
+ def padding_oracle(c_int):
397
+ while True:
398
+ total = measure_response_time(c_int)
399
+ if SLOW_LOWER < total < SLOW_UPPER:
400
+ return True # Y == 0 (below B)
401
+ elif total < FAST_UPPER:
402
+ return False # Y != 0 (above B)
403
+ # Ambiguous: retry
404
+ ```
405
+
406
+ **Full 3-step Manger's attack (~1024 iterations for 1024-bit RSA):**
407
+ ```python
408
+ # Step 1: Find f1 where f1 * m >= B
409
+ f1 = 2
410
+ while oracle((pow(f1, e, n) * c) % n):
411
+ f1 *= 2
412
+
413
+ # Step 2: Find f2 where n <= f2 * m < n + B
414
+ f2 = (n + B) // B * f1 // 2
415
+ while not oracle((pow(f2, e, n) * c) % n):
416
+ f2 += f1 // 2
417
+
418
+ # Step 3: Binary search narrowing m to exact value
419
+ mmin, mmax = ceil_div(n, f2), floor_div(n + B, f2)
420
+ while mmin < mmax:
421
+ f = floor_div(2 * B, mmax - mmin)
422
+ i = floor_div(f * mmin, n)
423
+ f3 = ceil_div(i * n, mmin)
424
+ if oracle((pow(f3, e, n) * c) % n):
425
+ mmax = floor_div(i * n + B, f3)
426
+ else:
427
+ mmin = ceil_div(i * n + B, f3)
428
+ m = mmin
429
+ ```
430
+
431
+ **Post-recovery OAEP decode:**
432
+ ```python
433
+ from Crypto.Signature.pss import MGF1
434
+ maskedSeed = EM[1:hLen+1]
435
+ maskedDB = EM[hLen+1:]
436
+ seed = bytes(a ^ b for a, b in zip(maskedSeed, MGF1(maskedDB, hLen, HF)))
437
+ DB = bytes(a ^ b for a, b in zip(maskedDB, MGF1(seed, k - hLen - 1, HF)))
438
+ # DB[:hLen] should match lHash; rest is 0x00...0x01 || message
439
+ ```
440
+
441
+ **Key insight:** Python's `or` short-circuits left-to-right. When expensive operations (PBKDF2, bcrypt, argon2) appear in chained conditions, the first condition becomes a timing oracle. RFC 8017 explicitly warns implementations must not let attackers distinguish error conditions — timing differences violate this.
442
+
443
+ **Detection:** RSA-OAEP with custom hash or slow KDF. Flask/Python backend. `/verify-token` or similar decryption endpoint returning generic errors. Timing differences between responses.
444
+
445
+ ---
446
+
447
+ ## Polynomial Hash with Trivial Root (Pragyan 2026)
448
+
449
+ **Pattern (!!Cand1esaNdCrypt0!!):** RSA signature scheme using polynomial hash `g(x,a,b) = x(x^2 + ax + b) mod P`.
450
+
451
+ **Vulnerability:** `g(0) = 0` for all parameters `a,b`. RSA signature of 0 is always 0 (`0^d mod n = 0`).
452
+
453
+ **Exploitation:** Craft message suffix so `bytes_to_long(prefix || suffix) = 0 (mod P)`:
454
+ ```python
455
+ P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61 # 128-bit prime
456
+ # Compute required suffix value mod P
457
+ req = (-prefix_val * pow(256, suffix_len, P)) % P
458
+ # Brute-force partial bytes until all printable ASCII
459
+ while True:
460
+ high = os.urandom(32).translate(printable_table)
461
+ low_val = (req - int.from_bytes(high, 'big') * shift) % P
462
+ low = low_val.to_bytes(16, 'big')
463
+ if all(32 <= b <= 126 for b in low):
464
+ suffix = high + low
465
+ break
466
+ # Signature is simply 0
467
+ ```
468
+
469
+ **General lesson:** Always check if hash function has trivial inputs (0, 1, -1). Factoring the polynomial often reveals these.
470
+
471
+ ---
472
+
473
+ ## Polynomial CRT in GF(2)[x] (Nullcon 2026)
474
+
475
+ **Pattern (Going in Circles, Nullcon 2026):** `r = flag mod f` where f is random GF(2) polynomial. Collect ~20 pairs, filter coprime, CRT combine.
476
+
477
+ See [advanced-math.md](advanced-math.md) for GF(2)[x] polynomial arithmetic and CRT implementation.
478
+
479
+ ---
480
+
481
+ ## Affine Cipher over Non-Prime Modulus (Nullcon 2026)
482
+
483
+ **Pattern (Matrixfun, Nullcon 2026):** `c = A @ p + b (mod m)` with composite m. Chosen-plaintext difference attack. For composite modulus, solve via CRT in each prime factor field separately.
484
+
485
+ See [advanced-math.md](advanced-math.md) for CRT approach and Gauss-Jordan implementation.
486
+
487
+ See also: [rsa-attacks-2.md](rsa-attacks-2.md) for specialized RSA techniques (p=q bypass, cube root CRT, phi(n) multiple factoring, signature forgery, weak keygen, batch GCD, partial key recovery, CRT fault attack, homomorphic bypass).