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,257 @@
1
+ # CTF Crypto - PRNG Attacks (CTF-Era Techniques)
2
+
3
+ Advanced CTF-specific PRNG attacks from 2017 onward. For foundational PRNG recovery (MT19937, LCG parameter recovery, ChaCha20, V8 XorShift128+, password cracking), see [prng.md](prng.md).
4
+
5
+ ## Table of Contents
6
+ - [Mersenne Twister Seed Recovery from Subset Sum (Tokyo Westerns 2017)](#mersenne-twister-seed-recovery-from-subset-sum-tokyo-westerns-2017)
7
+ - [MT19937 State Recovery via Constraint Propagation (HITCON 2017)](#mt19937-state-recovery-via-constraint-propagation-hitcon-2017)
8
+ - [Rule 86 Cellular Automaton PRNG Reversal via Z3 (Insomni'hack 2018)](#rule-86-cellular-automaton-prng-reversal-via-z3-insomnihack-2018)
9
+ - [Java LCG Seed Meet-in-the-Middle via Partial Modulo (P.W.N. CTF 2018)](#java-lcg-seed-meet-in-the-middle-via-partial-modulo-pwn-ctf-2018)
10
+ - [LCG Backward Stepping via Multiplicative Inverse (P.W.N. CTF 2018)](#lcg-backward-stepping-via-multiplicative-inverse-pwn-ctf-2018)
11
+ - [LFSR Bit-Fold Recovery from ASCII Parity (X-MAS CTF 2018)](#lfsr-bit-fold-recovery-from-ascii-parity-x-mas-ctf-2018)
12
+ - [Z3 Solve-Time Timing Oracle on PRNG (X-MAS CTF 2018)](#z3-solve-time-timing-oracle-on-prng-x-mas-ctf-2018)
13
+ - [randcrack-Fed DSA k Prediction (CSAW CTF 2018)](#randcrack-fed-dsa-k-prediction-csaw-ctf-2018)
14
+ - [Time-Seeded PRNG Offset via Format-String Global Write (FireShell 2019)](#time-seeded-prng-offset-via-format-string-global-write-fireshell-2019)
15
+ - [NTP-Poisoned PRNG State Leak via UUID XOR (RuCTFe 2018)](#ntp-poisoned-prng-state-leak-via-uuid-xor-ructfe-2018)
16
+
17
+ ---
18
+
19
+ ## Mersenne Twister Seed Recovery from Subset Sum (Tokyo Westerns 2017)
20
+
21
+ **Pattern:** MT19937 seeded with a 32-bit value generates subset-sum problems (e.g., "which elements from this set sum to target?"). Solving small subset-sum problems leaks specific MT output values. Two recovered outputs at indices 0 and 227 are sufficient to invert the MT seeding process.
22
+
23
+ **MT twist function relationship:**
24
+ ```text
25
+ mt[i] = mt[i-624] XOR twist(mt[i-624], mt[i-623])
26
+ ```
27
+ At the wrap-around: `mt[624]` depends on `mt[0]` (new cycle) and `mt[397]` (old cycle). Recovering `mt[0]` and `mt[227]` (which is related to `mt[624-227] = mt[397]`) via subset-sum solutions reveals enough to invert the twist recurrence.
28
+
29
+ ```python
30
+ import random
31
+
32
+ def crack_seed_from_two_outputs(mt0_val, mt227_val):
33
+ """Try all 2^32 seeds until MT outputs match recovered values."""
34
+ for seed in range(2**32):
35
+ r = random.Random()
36
+ r.seed(seed)
37
+ # Generate enough to reach indices 0 and 227
38
+ outputs = [r.getrandbits(32) for _ in range(228)]
39
+ if outputs[0] == mt0_val and outputs[227] == mt227_val:
40
+ return seed
41
+ return None
42
+
43
+ # After recovering seed, all future (and past) outputs are predictable
44
+ r = random.Random()
45
+ r.seed(recovered_seed)
46
+ ```
47
+
48
+ **Key insight:** MT19937 seeds recoverable from as few as two state values (indices 0 and 227) via the twist function's wrap-around relationship. Any challenge that exposes MT state values through solvable mathematical puzzles is vulnerable to full seed recovery.
49
+
50
+ **References:** Tokyo Westerns CTF 2017
51
+
52
+ ---
53
+
54
+ ## MT19937 State Recovery via Constraint Propagation (HITCON 2017)
55
+
56
+ **Pattern:** Server generates problems that leak 24-120 bits of PRNG output per round (e.g., partial bit-patterns, subset sums, modular reductions). Rather than collecting 624 full 32-bit outputs, model the MT state as an array of per-cell candidate sets and propagate constraints bidirectionally through the MT recurrence.
57
+
58
+ **MT recurrence dependencies:**
59
+ ```text
60
+ state[i] = state[i-624] XOR twist(state[i-624], state[i-623])
61
+ ```
62
+ This means `state[x]` depends on `state[x-624]`, `state[x-623]`, and `state[x-227]` (via the generate step). Partial knowledge at any index propagates in both directions.
63
+
64
+ **Constraint propagation approach:**
65
+ ```python
66
+ # Model: each state word starts as a set of 2^32 candidates
67
+ # Partial observation: narrow candidates for observed indices
68
+ # Propagate: for each constrained cell, narrow related cells
69
+
70
+ def propagate_forward(state_candidates, idx):
71
+ """MT: state[idx+624] = f(state[idx], state[idx+1])"""
72
+ for s0 in state_candidates[idx]:
73
+ for s1 in state_candidates[idx + 1]:
74
+ new_val = mt_twist(s0, s1)
75
+ state_candidates[idx + 624].add(new_val)
76
+
77
+ def propagate_backward(state_candidates, idx):
78
+ """Invert MT twist to constrain earlier states from later ones."""
79
+ for val in state_candidates[idx]:
80
+ # Recover state[idx-624] given state[idx] and state[idx-623]
81
+ for s1 in state_candidates[idx - 623]:
82
+ s0 = mt_untwist(val, s1)
83
+ state_candidates[idx - 624].add(s0)
84
+
85
+ # After ~20 partial observations across different positions:
86
+ # Most cells converge to single candidates → full state determined
87
+ ```
88
+
89
+ **Key insight:** MT19937's recurrence dependencies allow bidirectional constraint propagation — partial knowledge at multiple positions narrows candidates until the full 624-word state is determined. The number of partial observations needed scales inversely with bits leaked per observation: ~20 observations of 24+ bits each typically suffice.
90
+
91
+ **References:** HITCON CTF 2017
92
+
93
+ ---
94
+
95
+ ## Rule 86 Cellular Automaton PRNG Reversal via Z3 (Insomni'hack 2018)
96
+
97
+ **Pattern:** Wolfram elementary cellular automaton Rule 86 used as PRNG. Reverse through 128 rounds using Z3 Bool arrays:
98
+
99
+ ```python
100
+ from z3 import *
101
+
102
+ def RULE86(x, y, z):
103
+ return Or(And(Not(x), Not(y), z), And(Not(x), y, Not(z)),
104
+ And(x, Not(y), Not(z)), And(x, y, Not(z)))
105
+
106
+ s = Solver()
107
+ state = [Bool(f'b{i}') for i in range(256)]
108
+ # Forward-compute 128 rounds symbolically
109
+ for round in range(128):
110
+ new_state = [RULE86(state[(i-1)%256], state[i], state[(i+1)%256]) for i in range(256)]
111
+ state = new_state
112
+ # Constrain final state to known output
113
+ for i, bit in enumerate(known_output):
114
+ s.add(state[i] == (bit == 1))
115
+ s.check()
116
+ model = s.model()
117
+ ```
118
+
119
+ **Key insight:** Elementary cellular automata are NOT injective -- multiple preimages may exist. But Z3 handles the search efficiently by treating each cell as a boolean variable and each rule application as a CNF clause. For Rule 86 specifically, the DNF has 4 terms (bits 1,2,4,6 of rule number 86 = 01010110). Use `s.push()`/`s.pop()` to iteratively backtrack through rounds. This approach generalizes to any elementary CA rule used as a PRNG: encode the rule's truth table as a boolean formula, compose symbolically for N rounds, and constrain to the known output.
120
+
121
+ **References:** Insomni'hack CTF 2018
122
+
123
+ ---
124
+
125
+ ## Java LCG Seed Meet-in-the-Middle via Partial Modulo (P.W.N. CTF 2018)
126
+
127
+ **Pattern:** Java `Random` outputs are only visible mod 62 (e.g., characters in a password). Full 48-bit seed search is infeasible, but partial output still leaks the LSB (`output mod 2`) and the high bits can be recovered independently because Java's LCG takes `(seed*a + c) >> 16`. Split into `2^18` low-bit search and `2^30` high-bit search.
128
+
129
+ ```python
130
+ # Phase 1: enumerate 2^18 low-18-bit candidates whose nextInt(62) parities match known chars
131
+ for low in range(1 << 18):
132
+ if simulate(low)[: K] == known_prefix: candidates.append(low)
133
+
134
+ # Phase 2: extend each candidate to 48 bits, matching next outputs
135
+ for low in candidates:
136
+ for high in range(1 << 30):
137
+ seed = (high << 18) | low
138
+ if simulate(seed) == full_known: return seed
139
+ ```
140
+
141
+ **Key insight:** Java's LCG discards the lowest 16 bits, so the low 18 bits only affect the LSB of each `nextInt()`. Split the seed at that boundary to turn an infeasible `2^48` search into two `2^18 + 2^30` passes.
142
+
143
+ **References:** P.W.N. CTF 2018 — PW API, writeup 12065
144
+
145
+ ---
146
+
147
+ ## LCG Backward Stepping via Multiplicative Inverse (P.W.N. CTF 2018)
148
+
149
+ **Pattern:** After recovering a forward seed, step backward with the modular inverse of the multiplier: `prev = a^-1 * (state - c) mod m`. For Java: `a^-1 = -35320271006875` mod `2^48`.
150
+
151
+ ```python
152
+ M = 1 << 48
153
+ a_inv = pow(25214903917, -1, M) # Java multiplier inverse
154
+ prev_state = (a_inv * (state - 11)) % M
155
+ ```
156
+
157
+ **Key insight:** Every power-of-two-modulus LCG is invertible; once you have any state, you can walk the chain both directions without re-simulating from seed.
158
+
159
+ **References:** P.W.N. CTF 2018 — PW API, writeup 12065
160
+
161
+ ---
162
+
163
+ ## LFSR Bit-Fold Recovery from ASCII Parity (X-MAS CTF 2018)
164
+
165
+ **Pattern:** Custom PRNG XORs shifts of a 32-bit state into an 8-bit output byte. Observed bytes are ASCII (`top bit = 0`), so each observed byte leaks one parity bit of the internal state. Combine enough parity constraints via Gaussian elimination over GF(2) to recover state without brute force.
166
+
167
+ ```python
168
+ # Collect parity constraints: each observed ASCII byte gives top_bit == 0
169
+ # Each bit of each byte is a linear combination of state bits
170
+ # Stack rows, solve in GF(2)
171
+ import numpy as np
172
+ A = np.array(constraint_rows, dtype=np.uint8)
173
+ b = np.array(known_bits, dtype=np.uint8)
174
+ state = gf2_solve(A, b)
175
+ ```
176
+
177
+ **Key insight:** Output byte folding does not hide the state; each output bit is still linear in the state. Use the ASCII constraint to turn output bytes into free parity equations.
178
+
179
+ **References:** X-MAS CTF 2018 — Probably Really Nice Goodies from Santa, writeup 12686
180
+
181
+ ---
182
+
183
+ ## Z3 Solve-Time Timing Oracle on PRNG (X-MAS CTF 2018)
184
+
185
+ **Pattern:** Cannot directly check a PRNG guess, but `Solver.check()` takes measurably longer for correct inputs because the constraint graph becomes UNSAT-hard instead of trivially SAT. Brute-force each character by timing Z3 with a tight timeout.
186
+
187
+ ```python
188
+ from z3 import Solver, sat
189
+ for c in string.printable:
190
+ s = Solver()
191
+ s.set('timeout', 500)
192
+ s.add(prng_constraints(flag + c, ciphertext))
193
+ t = time.time()
194
+ if s.check() == sat and (time.time() - t) > 0.4:
195
+ flag += c
196
+ break
197
+ ```
198
+
199
+ **Key insight:** Many oracles run the solver internally; wrong guesses return fast because the SAT instance is already obviously satisfiable. Set a tight timeout and promote slow-solve candidates.
200
+
201
+ **References:** X-MAS CTF 2018 — Probably Really Nice Goodies from Santa, writeup 12686
202
+
203
+ ---
204
+
205
+ ## randcrack-Fed DSA k Prediction (CSAW CTF 2018)
206
+
207
+ **Pattern:** DSA signing uses `random.randrange()` to pick `k`. If the server also exposes a "forgot password" endpoint that leaks Python `random` outputs, feed 624 × 32-bit samples to `randcrack`, predict the next `k`, and solve `x = (s*k - h) * r^-1 mod q` for the private key.
208
+
209
+ ```python
210
+ from randcrack import RandCrack
211
+ rc = RandCrack()
212
+ for _ in range(624 // 2):
213
+ v = getrand64()
214
+ rc.submit(v & 0xffffffff); rc.submit(v >> 32)
215
+ k = rc.predict_randrange(2, q)
216
+ x = ((s*k - h) * pow(r, -1, q)) % q
217
+ ```
218
+
219
+ **Key insight:** Python's `random` is shared across all calls. Any endpoint that leaks `random` output poisons every other RNG consumer including DSA signing.
220
+
221
+ **References:** CSAW CTF 2018 — Disastrous Security Apparatus, writeup 12495
222
+
223
+ ---
224
+
225
+ ## Time-Seeded PRNG Offset via Format-String Global Write (FireShell 2019)
226
+
227
+ **Pattern:** Server seeds `srand((time(0)/10) + bet)` where `bet` is a writable global. Use a format string primitive to set `bet`, then predict all subsequent `rand()` outputs offline with matching libc.
228
+
229
+ ```python
230
+ # 1. Format-string: %Xc%Y$n writes desired bet value at &bet (0x602020)
231
+ # 2. Locally: predict rand() with C stdlib
232
+ from ctypes import CDLL
233
+ libc = CDLL('libc.so.6')
234
+ libc.srand((int(time.time())//10) + bet_value)
235
+ predicted = [libc.rand() for _ in range(n)]
236
+ ```
237
+
238
+ **Key insight:** Shifting a known seed by a writable constant turns a time-of-day RNG into a deterministic RNG because the attacker now controls the whole seed.
239
+
240
+ **References:** FireShell CTF 2019 — casino, writeup 12916
241
+
242
+ ---
243
+
244
+ ## NTP-Poisoned PRNG State Leak via UUID XOR (RuCTFe 2018)
245
+
246
+ **Pattern:** Server derives UUIDs as `uuid = time ^ hash_state`. Register a user with a custom NTP endpoint returning `0x00`; the returned UUID now equals `hash_state` directly. Future UUIDs are predictable: `target_uuid ^ hash_state = required_timestamp`.
247
+
248
+ ```python
249
+ # 1. Point the server at attacker-controlled NTP that returns 0
250
+ # 2. Register; received UUID == internal state
251
+ # 3. For desired uuid, compute needed timestamp = uuid ^ state
252
+ # 4. Send the crafted timestamp and read the message
253
+ ```
254
+
255
+ **Key insight:** Any randomness mixed via XOR with a user-controlled value is the same as giving the attacker the state directly. Check all time sources for user control.
256
+
257
+ **References:** RuCTFe 2018 — vch, writeup 12146