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,380 @@
1
+ # CTF Crypto - Exotic Algebraic Structures (Part 2)
2
+
3
+ Covers 2017+ era exotic crypto attacks (BB-84 QKD, ElGamal variants, Paillier oracles, differential privacy, homomorphic bit extraction, Jordan normal form, OSS forgery, Cayley-Purser, BIP39 brute, Asmuth-Bloom, Rabin polynomial primes, LCG period, Vandermonde recovery). For Part 1 foundational exotic structures, see [exotic-crypto.md](exotic-crypto.md).
4
+
5
+ ## Table of Contents
6
+ - [BB-84 Quantum Key Distribution MITM Attack (PlaidCTF 2017)](#bb-84-quantum-key-distribution-mitm-attack-plaidctf-2017)
7
+ - [ElGamal Trivial DLP When B = p-1 (Hack.lu 2017)](#elgamal-trivial-dlp-when-b--p-1-hacklu-2017)
8
+ - [Paillier LSB Oracle via Homomorphic Doubling (CODE BLUE 2017)](#paillier-lsb-oracle-via-homomorphic-doubling-code-blue-2017)
9
+ - [Differential Privacy Laplace Noise Cancellation (Pwn2Win 2017)](#differential-privacy-laplace-noise-cancellation-pwn2win-2017)
10
+ - [Homomorphic Encryption Oracle Bit-Extraction (Tokyo Westerns 2017)](#homomorphic-encryption-oracle-bit-extraction-tokyo-westerns-2017)
11
+ - [ElGamal over Matrices via Jordan Normal Form (SharifCTF 8)](#elgamal-over-matrices-via-jordan-normal-form-sharifctf-8)
12
+ - [OSS (Ong-Schnorr-Shamir) Signature Forgery via Pollard's Method (SharifCTF 8)](#oss-ong-schnorr-shamir-signature-forgery-via-pollards-method-sharifctf-8)
13
+ - [Cayley-Purser Decryption Without Private Key (TJCTF 2018)](#cayley-purser-decryption-without-private-key-tjctf-2018)
14
+ - [BIP39 Partial-Mnemonic Brute Force via Checksum (SECCON 2018)](#bip39-partial-mnemonic-brute-force-via-checksum-seccon-2018)
15
+ - [Asmuth-Bloom Threshold Secret Sharing via CRT (X-MAS 2018)](#asmuth-bloom-threshold-secret-sharing-via-crt-x-mas-2018)
16
+ - [Rabin Cryptosystem with Polynomial Primes (X-MAS 2018)](#rabin-cryptosystem-with-polynomial-primes-x-mas-2018)
17
+ - [LCG Period Detection for Unlimited Output Prediction (X-MAS 2018)](#lcg-period-detection-for-unlimited-output-prediction-x-mas-2018)
18
+ - [Polynomial Coefficient Recovery via Vandermonde Linear System (X-MAS 2018)](#polynomial-coefficient-recovery-via-vandermonde-linear-system-x-mas-2018)
19
+ - [Rabin Decryption via Four-Roots CRT Combination (Pragyan CTF 2019)](#rabin-decryption-via-four-roots-crt-combination-pragyan-ctf-2019)
20
+
21
+ ---
22
+
23
+ ## BB-84 Quantum Key Distribution MITM Attack (PlaidCTF 2017)
24
+
25
+ **Pattern:** In simulated BB-84 QKD without authentication, perform a full man-in-the-middle by independently negotiating with both Alice and Bob.
26
+
27
+ ```python
28
+ # Strategy: Always use basis Z, always send value 1 to Bob
29
+ # Alice side: measure in random bases, record results
30
+ # Bob side: always receives 1 in basis Z
31
+ # Bob's key = all 1s (known to attacker)
32
+ # Alice's key = attacker's measured qbit values
33
+
34
+ # Heuristic: throttle Bob's correct-guess count to match Alice's
35
+ # Both parties verify by comparing subset of bits — attacker controls both sides
36
+ for qbit in alice_qbits:
37
+ my_basis = 'Z' # always measure in Z basis
38
+ my_value = measure(qbit, my_basis)
39
+ send_to_bob(basis='Z', value=1) # always send 1
40
+
41
+ # After basis reconciliation:
42
+ # key_with_alice = [measured values where bases matched]
43
+ # key_with_bob = [all 1s]
44
+ ```
45
+
46
+ **Key insight:** BB-84 QKD is secure only with authenticated classical channels. Without authentication, an attacker can independently negotiate keys with both parties. Forcing a constant value to one party makes their key entirely predictable, while the other party's key is captured through measurement.
47
+
48
+ **References:** PlaidCTF 2017
49
+
50
+ ---
51
+
52
+ ## ElGamal Trivial DLP When B = p-1 (Hack.lu 2017)
53
+
54
+ **Pattern:** ElGamal public key `B = g^key mod p`. If `B + 1 == p`, then `B = p-1 = -1 mod p`. By Euler's criterion, `g^((p-1)/2) ≡ -1 (mod p)` for any primitive root `g`. Therefore `g^key ≡ g^((p-1)/2) (mod p)`, so `key = (p-1)/2` directly. No DLP algorithm needed.
55
+
56
+ ```python
57
+ # Check for trivial case
58
+ if (B + 1) == p:
59
+ key = (p - 1) // 2
60
+ # Verify
61
+ assert pow(g, key, p) == B
62
+ # Decrypt ElGamal: shared_secret = pow(ephemeral, key, p)
63
+ ```
64
+
65
+ **Key insight:** The generator raised to `(p-1)/2` always equals `-1 mod p` (Euler's criterion for quadratic residues). When the public key `B` equals `p-1`, the private key is trivially `(p-1)/2`. Always check `B == p-1` (and `B == 1` for key=0) before attempting general DLP.
66
+
67
+ **References:** Hack.lu CTF 2017
68
+
69
+ ---
70
+
71
+ ## Paillier LSB Oracle via Homomorphic Doubling (CODE BLUE 2017)
72
+
73
+ **Pattern:** Paillier encryption is additively homomorphic: multiplying a ciphertext by itself (`ct^2 mod n^2`) doubles the plaintext. Doubling repeatedly and observing when the LSB changes (due to modular reduction by n) reveals plaintext bits one at a time — a binary search identical to the RSA LSB oracle.
74
+
75
+ **Attack (bit-by-bit recovery from MSB to LSB):**
76
+ ```python
77
+ def paillier_double(ct, n):
78
+ """Homomorphically double the plaintext."""
79
+ return pow(ct, 2, n * n)
80
+
81
+ def recover_plaintext(ct, oracle_lsb, n):
82
+ """Oracle returns LSB of decrypted plaintext."""
83
+ lower, upper = 0, n
84
+ current_ct = ct
85
+ for _ in range(n.bit_length()):
86
+ current_ct = paillier_double(current_ct)
87
+ lsb = oracle_lsb(current_ct)
88
+ mid = (lower + upper) // 2
89
+ if lsb == 1:
90
+ lower = mid # plaintext > n/2, wraparound occurred
91
+ else:
92
+ upper = mid
93
+ return lower
94
+
95
+ # Alternative: homomorphic subtraction to isolate each bit
96
+ def paillier_encrypt_scalar(m, n, g=None):
97
+ """Encrypt scalar m under Paillier (with r=1 for known randomness)."""
98
+ g = g or (n + 1)
99
+ return pow(g, m, n * n) # simplified (r=1)
100
+
101
+ def subtract_plaintext(ct, val, n):
102
+ """Compute E(pt - val) = ct * E(-val) mod n^2."""
103
+ neg_enc = paillier_encrypt_scalar(n - val, n)
104
+ return (ct * neg_enc) % (n * n)
105
+ ```
106
+
107
+ **Key insight:** Paillier's additive homomorphism enables a binary search oracle: doubling the plaintext via `ct^2` and observing LSB changes reveals one bit per query. Equivalently, use homomorphic subtraction of known masks to isolate each bit. Total queries: log2(n) ≈ 2048 for 2048-bit modulus.
108
+
109
+ **References:** CODE BLUE CTF 2017
110
+
111
+ ---
112
+
113
+ ## Differential Privacy Laplace Noise Cancellation (Pwn2Win 2017)
114
+
115
+ **Pattern:** Server implements differential privacy by adding Laplace noise (mean 0, scale λ) to character ordinals before returning them. Since Laplace noise has zero mean, querying the same position many times and averaging the results cancels the noise via the Law of Large Numbers.
116
+
117
+ ```python
118
+ import requests
119
+ import statistics
120
+
121
+ def recover_char(position, num_queries=1000):
122
+ """Average 1000 noisy responses to cancel Laplace noise."""
123
+ samples = []
124
+ for _ in range(num_queries):
125
+ noisy_val = query_server(position)
126
+ samples.append(noisy_val)
127
+ # Mean converges to true value as queries → ∞
128
+ true_val = round(statistics.mean(samples))
129
+ return chr(true_val)
130
+
131
+ flag = ''.join(recover_char(i) for i in range(flag_length))
132
+ ```
133
+
134
+ **Key insight:** Laplace differential privacy with zero mean is breakable with sufficient queries — averaging N samples reduces noise variance by factor N (standard error ∝ 1/sqrt(N)). With λ=1 and 1000 queries, the mean is within ±0.1 of the true value. Round to nearest integer to recover the exact character ordinal. This applies to any additive zero-mean noise mechanism.
135
+
136
+ **References:** Pwn2Win CTF 2017
137
+
138
+ ---
139
+
140
+ ## Homomorphic Encryption Oracle Bit-Extraction (Tokyo Westerns 2017)
141
+
142
+ **Pattern:** An encryption oracle has homomorphic properties — you can add 1 to the plaintext by performing a known operation on the ciphertext. Extract bits from an unknown plaintext by observing how the ciphertext changes as the plaintext value crosses power-of-2 boundaries.
143
+
144
+ **Low-bit extraction (observe overflow):**
145
+ ```python
146
+ # Increment plaintext by 1 repeatedly via homomorphic add-1
147
+ # Detect when bit N overflows: ciphertext "wraps" at value 2^N
148
+ ct = target_ciphertext
149
+ for bit_pos in range(num_bits):
150
+ threshold = 2 ** bit_pos
151
+ # Add 1 repeatedly until bit flips
152
+ increments = 0
153
+ prev_ct = ct
154
+ while True:
155
+ ct = homomorphic_add_one(ct)
156
+ increments += 1
157
+ if bit_has_flipped(ct, prev_ct, bit_pos):
158
+ low_bits = (threshold - increments) % threshold
159
+ break
160
+ ```
161
+
162
+ **High-bit extraction (divide by 2 on even values):**
163
+ ```python
164
+ # Subtract recovered low bits to make value even
165
+ even_ct = homomorphic_subtract(target_ct, low_bits)
166
+ # Repeatedly divide by 2 and observe the resulting high bits
167
+ for i in range(high_bit_count):
168
+ even_ct = homomorphic_halve(even_ct)
169
+ high_bits = (high_bits << 1) | observe_lsb(even_ct)
170
+ ```
171
+
172
+ **Key insight:** Homomorphic oracles enable bit-extraction: detect overflow in specific bit positions when incrementing for low bits; use division-by-2 on even numbers for high bits. The total number of queries scales linearly with the bit count of the plaintext.
173
+
174
+ **References:** Tokyo Westerns CTF 2017
175
+
176
+ ---
177
+
178
+ ## ElGamal over Matrices via Jordan Normal Form (SharifCTF 8)
179
+
180
+ **Pattern:** Discrete log on matrices: convert generator G to Jordan normal form, then extract exponent from off-diagonal elements.
181
+
182
+ ```sage
183
+ G = Matrix(GF(p), [[...]]) # generator matrix
184
+ H = Matrix(GF(p), [[...]]) # H = G^alpha
185
+ J, P = G.jordan_form(transformation=True)
186
+ H_prime = ~P * H * P # H in Jordan basis
187
+ # For Jordan block with eigenvalue lambda:
188
+ # J^alpha has alpha * lambda^(alpha-1) on super-diagonal
189
+ # alpha = J[3][3] * H_prime[3][4] / H_prime[3][3]
190
+ alpha = int(J[3][3] * H_prime[3][4] / H_prime[4][4])
191
+ ```
192
+
193
+ **Key insight:** Matrix DLP reduces to scalar DLP when the matrix is diagonalizable, or to polynomial extraction when Jordan blocks have repeated eigenvalues. The super-diagonal element of J^alpha is `alpha * lambda^(alpha-1)`, giving alpha directly via division when lambda is known. For diagonalizable matrices, the DLP decomposes into independent scalar DLPs per eigenvalue. Always compute the Jordan form first to determine which reduction applies.
194
+
195
+ **References:** SharifCTF 8 (2018)
196
+
197
+ ---
198
+
199
+ ## OSS (Ong-Schnorr-Shamir) Signature Forgery via Pollard's Method (SharifCTF 8)
200
+
201
+ **Pattern:** Given two valid OSS signatures, forge a signature for the product of their messages using Pollard's composition formula:
202
+
203
+ ```python
204
+ # OSS signature: (x, y) valid for message m if x^2 + k*y^2 = m (mod n)
205
+ # Pollard's forgery for m1*m2:
206
+ def forge_product(x1, y1, x2, y2, k, n):
207
+ X = (x1*x2 + k*y1*y2) % n
208
+ Y = (x1*y2 - x2*y1) % n
209
+ return X, Y
210
+ # (X, Y) is a valid signature for m1*m2 mod n
211
+ ```
212
+
213
+ **Key insight:** The OSS signature scheme is based on the quadratic form `x^2 + ky^2 = m (mod n)`. Pollard showed that these forms compose multiplicatively -- given signatures for m1 and m2, you can forge a signature for m1*m2 without the private key. This is a fundamental algebraic break, not an implementation bug. To sign an arbitrary target message `m_target`, factor it as a product of signed messages, or use the homomorphic property with `m1 = known_signed_message` and construct `m2 = m_target * modinv(m1, n)` if a signature for m2 is obtainable.
214
+
215
+ **References:** SharifCTF 8 (2018)
216
+
217
+ ---
218
+
219
+ ## Cayley-Purser Decryption Without Private Key (TJCTF 2018)
220
+
221
+ **Pattern:** Cayley-Purser is a matrix-based public-key system using 2x2 matrices modulo a prime. Public key is `(alpha, beta, gamma)` where `gamma = alpha^r * beta * alpha^(-r)` and `epsilon = gamma^s`. The private key is `r`, but decryption only needs a matrix `H` that satisfies `H * gamma = gamma * H`.
222
+
223
+ **Exploit:** Any matrix `H` commuting with `gamma` decrypts correctly, and the Cayley-Hamilton theorem lets you build one entirely from public values — no need to recover `r`.
224
+
225
+ ```python
226
+ from sage.all import matrix, identity_matrix
227
+ import operator
228
+
229
+ # Given public alpha, beta, gamma, epsilon, mu (ciphertext)
230
+ invalpha = alpha.inverse()
231
+ # Recover scaling entry h via elementwise division
232
+ h_elems = (invalpha * gamma - gamma * beta)
233
+ h_denom = (beta - invalpha)
234
+ h = matrix([[h_elems[i][j] / h_denom[i][j] for j in range(2)] for i in range(2)])
235
+
236
+ H = h[0][0] * identity_matrix(2) + gamma
237
+ plaintext = (H.inverse() * epsilon * H) * mu * (H.inverse() * epsilon * H)
238
+ ```
239
+
240
+ **Key insight:** Any commuting matrix works as the decryption key. Cayley-Hamilton guarantees that `H = c1 * I + c2 * gamma` commutes with `gamma`, and the needed scalar `c1` can be read off by comparing entries of `alpha^(-1) * gamma` against `gamma * beta`. Always check whether "private key" operations can be replaced by a commutation-equivalent derived from public data.
241
+
242
+ **References:** TJCTF 2018 — writeup 10680
243
+
244
+ ---
245
+
246
+ ## BIP39 Partial-Mnemonic Brute Force via Checksum (SECCON 2018)
247
+
248
+ **Pattern:** Challenge provides 23 of 24 BIP39 mnemonic words (Japanese wordlist) and the target flag = `md5(entropy)`. Each word encodes 11 bits, so the missing word costs only `2^11 = 2048` guesses. Validate each candidate by running BIP39's built-in SHA-256 checksum over the reassembled entropy — only the correct guess passes.
249
+
250
+ ```python
251
+ from mnemonic import Mnemonic
252
+ lg = Mnemonic("japanese")
253
+ known = ["...23 words..."]
254
+ for w in lg.wordlist:
255
+ try:
256
+ if lg.check(" ".join(known + [w])):
257
+ entropy = lg.to_entropy(" ".join(known + [w]))
258
+ print(md5(entropy).hexdigest())
259
+ except Exception: pass
260
+ ```
261
+
262
+ **Key insight:** BIP39 has a built-in 4-bit-per-word checksum, so partial mnemonics are self-verifying. Same trick applies to Electrum's seed format and any mnemonic scheme with internal parity.
263
+
264
+ **References:** SECCON 2018 — mnemonic, writeup 12053
265
+
266
+ ---
267
+
268
+ ## Asmuth-Bloom Threshold Secret Sharing via CRT (X-MAS 2018)
269
+
270
+ **Pattern:** Instead of Shamir's polynomial interpolation, Asmuth-Bloom splits a secret `S` into shares `(s_i, p_i)` where each `s_i = S mod p_i` and `p_i` are pairwise-coprime primes. Recover `S` by applying the Chinese Remainder Theorem to any threshold number of shares.
271
+
272
+ ```python
273
+ from sympy.ntheory.modular import crt
274
+ # shares = [(s1,p1), (s2,p2), ..., (sk,pk)]
275
+ residues = [s for s, _ in shares]
276
+ moduli = [p for _, p in shares]
277
+ S, M = crt(moduli, residues)
278
+ flag = long_to_bytes(int(S))
279
+ ```
280
+
281
+ **Key insight:** Threshold sharing schemes can be CRT-based, not polynomial. Recognise Asmuth-Bloom by the `(residue, modulus)` share format; Shamir's scheme only publishes `(x, y)` coordinates without moduli.
282
+
283
+ **References:** X-MAS CTF 2018 — writeup 12660
284
+
285
+ ---
286
+
287
+ ## Rabin Cryptosystem with Polynomial Primes (X-MAS 2018)
288
+
289
+ **Pattern:** Rabin key generator derives `p, q` from a polynomial in a base value `r`, e.g. `p = r^2 + 3`, `q = r^2 + 7`. Modulus `N = p*q` is a polynomial in `r`; solve for `r` via `iroot(N - known_constant, 4)`, then recover both primes and decrypt the four sqrt candidates in the usual way.
290
+
291
+ ```python
292
+ from gmpy2 import iroot
293
+ # N = (r^2+3)(r^2+7) = r^4 + 10 r^2 + 21
294
+ r, _ = iroot(N - 21, 4)
295
+ p, q = r*r + 3, r*r + 7
296
+ x_p = pow(ct, (p+1)//4, p)
297
+ x_q = pow(ct, (q+1)//4, q)
298
+ # CRT combine x_p, x_q; flag is the candidate with known padding
299
+ ```
300
+
301
+ **Key insight:** Any cryptosystem whose primes come from a polynomial in a small variable collapses under integer-root extraction. Recognize these by plotting `N` against hypothetical `r` guesses, or by seeing suspicious constant differences.
302
+
303
+ **References:** X-MAS CTF 2018 — writeups 12657, 12724
304
+
305
+ ---
306
+
307
+ ## LCG Period Detection for Unlimited Output Prediction (X-MAS 2018)
308
+
309
+ **Pattern:** Server uses an LCG for RNG with a short period. Send repeated requests until you see a previously-observed output — you've found the period. From that point forward, every future value is known because the LCG cycles.
310
+
311
+ ```python
312
+ seen = {}
313
+ for i in itertools.count():
314
+ v = fetch_next()
315
+ if v in seen:
316
+ period = i - seen[v]
317
+ break
318
+ seen[v] = i
319
+ # Now predict: future[i] == history[(i - period_start) % period]
320
+ ```
321
+
322
+ **Key insight:** All LCGs are periodic and the period is bounded by `m`. Any output buffer long enough to contain the period gives you free prediction for the rest of the game.
323
+
324
+ **References:** X-MAS CTF 2018 — writeups 12668, 12669
325
+
326
+ ---
327
+
328
+ ## Polynomial Coefficient Recovery via Vandermonde Linear System (X-MAS 2018)
329
+
330
+ **Pattern:** Oracle evaluates a hidden degree-`n` polynomial at `n+1` points. Build the Vandermonde matrix of evaluation inputs and solve the linear system for coefficients; recovered polynomial reveals the secret constants.
331
+
332
+ ```python
333
+ from sage.all import matrix, vector, GF
334
+ pts = [(x_i, f(x_i)) for x_i in range(degree+1)]
335
+ A = matrix([[xi**k for k in range(degree+1)] for xi, _ in pts])
336
+ b = vector([yi for _, yi in pts])
337
+ coeffs = A.solve_right(b)
338
+ ```
339
+
340
+ **Key insight:** Any secret polynomial, Shamir-style sharing, or "interpolate a curve" oracle falls to a Vandermonde solve with `degree + 1` points. Sage's `solve_right` handles huge degrees.
341
+
342
+ **References:** X-MAS CTF 2018 — writeup 12722
343
+
344
+ ---
345
+
346
+ ## Rabin Decryption via Four-Roots CRT Combination (Pragyan CTF 2019)
347
+
348
+ **Pattern (Help Rabin):** Rabin encrypts `c = m^2 mod n` with `n = p*q` and `p, q ≡ 3 mod 4`. Once `p, q` are recovered (here by Fermat-style square-root search because `q = nextPrime(p+1)` sits right next to `p`), compute `mp = c^((p+1)/4) mod p` and `mq = c^((q+1)/4) mod q`, then combine via extended GCD to yield four square roots `±r, ±s`. Only one of the four decodes to readable text — that's the plaintext.
349
+
350
+ ```python
351
+ from Crypto.Util.number import inverse
352
+
353
+ def ext_gcd(a, b):
354
+ c0, c1, a0, a1, b0, b1 = a, b, 1, 0, 0, 1
355
+ while c1:
356
+ q, r = divmod(c0, c1)
357
+ c0, c1 = c1, r
358
+ a0, a1 = a1, a0 - q * a1
359
+ b0, b1 = b1, b0 - q * b1
360
+ return a0, b0, c0
361
+
362
+ # p, q already recovered (e.g. via Fermat: p ~ sqrt(n))
363
+ pe, qe = (p + 1) // 4, (q + 1) // 4
364
+ mp, mq = pow(c, pe, p), pow(c, qe, q)
365
+ yp, yq, _ = ext_gcd(p, q) # yp*p + yq*q == 1
366
+
367
+ r1 = (yp * p * mq + yq * q * mp) % n
368
+ r2 = n - r1
369
+ s1 = (yp * p * mq - yq * q * mp) % n
370
+ s2 = n - s1
371
+
372
+ for cand in (r1, r2, s1, s2):
373
+ try:
374
+ pt = bytes.fromhex(hex(cand)[2:])
375
+ if pt.isascii(): print(pt) # pick the readable one
376
+ except Exception: pass
377
+ ```
378
+
379
+ **Key insight:** Rabin decryption inherently produces four candidates because `x^2 ≡ c mod n` has four roots mod `n = p*q`. When `p, q ≡ 3 mod 4`, per-prime roots are the closed-form exponentiation `c^((p+1)/4) mod p` — no Tonelli-Shelanks needed. Combine with Bezout coefficients `yp*p + yq*q = 1` to get the four CRT candidates `±(yp*p*mq ± yq*q*mp) mod n`, and select by plaintext sanity (ASCII, magic bytes, known prefix). The four-root ambiguity is why Rabin typically needs redundancy in the plaintext to be useful as a cryptosystem.
380
+