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.
- package/AGENTS.md +131 -0
- package/Dockerfile +192 -0
- package/README.md +522 -0
- package/agents/ctf-analyzer.md +58 -0
- package/agents/ctf-controller.md +151 -0
- package/agents/ctf-speedrun.md +74 -0
- package/bin/ctf-agent.js +119 -0
- package/docker-compose.yml +13 -0
- package/mcp_config.json +19 -0
- package/package.json +44 -0
- package/references/ctf-triage-ladder.md +68 -0
- package/references/exploit-databases.md +575 -0
- package/references/llm-safety-and-policy-compliance.md +173 -0
- package/references/multi-agent-orchestration-and-policy-routing.md +278 -0
- package/references/security-events-and-intelligence.md +566 -0
- package/references/version-matrix.md +59 -0
- package/rules/ctf-execution-rules.md +44 -0
- package/rules/ctf-safety-framing-rules.md +140 -0
- package/rules/flag-validation-rules.md +25 -0
- package/scripts/__init__.py +1 -0
- package/scripts/ctf-tools.lock +205 -0
- package/scripts/ctf_init.py +1026 -0
- package/scripts/ctf_update.py +583 -0
- package/scripts/ctfd_client.py +81 -0
- package/scripts/cve_lookup.py +453 -0
- package/scripts/extract_flags.py +155 -0
- package/scripts/install_as_agent.py +278 -0
- package/scripts/install_ctf_tools.sh +603 -0
- package/scripts/parallel_triage.py +395 -0
- package/scripts/prompt_policy_sanitizer.py +283 -0
- package/scripts/scope_guard.py +444 -0
- package/scripts/skill_validator.py +504 -0
- package/scripts/workspace_cleaner.py +141 -0
- package/skills/ctf-ai-ml/SKILL.md +120 -0
- package/skills/ctf-ai-ml/adversarial-ml.md +623 -0
- package/skills/ctf-ai-ml/llm-attacks.md +487 -0
- package/skills/ctf-ai-ml/model-attacks.md +422 -0
- package/skills/ctf-crypto/SKILL.md +320 -0
- package/skills/ctf-crypto/advanced-math.md +798 -0
- package/skills/ctf-crypto/classic-ciphers.md +651 -0
- package/skills/ctf-crypto/ecc-attacks.md +347 -0
- package/skills/ctf-crypto/exotic-crypto-2.md +380 -0
- package/skills/ctf-crypto/exotic-crypto.md +528 -0
- package/skills/ctf-crypto/historical.md +113 -0
- package/skills/ctf-crypto/lattice-and-lwe.md +524 -0
- package/skills/ctf-crypto/modern-ciphers-2.md +563 -0
- package/skills/ctf-crypto/modern-ciphers-3.md +453 -0
- package/skills/ctf-crypto/modern-ciphers.md +649 -0
- package/skills/ctf-crypto/prng-attacks.md +257 -0
- package/skills/ctf-crypto/prng.md +664 -0
- package/skills/ctf-crypto/rsa-attacks-2.md +792 -0
- package/skills/ctf-crypto/rsa-attacks.md +487 -0
- package/skills/ctf-crypto/stream-ciphers.md +390 -0
- package/skills/ctf-crypto/zkp-and-advanced.md +456 -0
- package/skills/ctf-forensics/3d-printing.md +121 -0
- package/skills/ctf-forensics/SKILL.md +379 -0
- package/skills/ctf-forensics/disk-advanced.md +497 -0
- package/skills/ctf-forensics/disk-and-memory.md +491 -0
- package/skills/ctf-forensics/disk-recovery.md +699 -0
- package/skills/ctf-forensics/linux-forensics.md +511 -0
- package/skills/ctf-forensics/network-advanced.md +583 -0
- package/skills/ctf-forensics/network.md +645 -0
- package/skills/ctf-forensics/peripheral-capture.md +287 -0
- package/skills/ctf-forensics/signals-and-hardware.md +713 -0
- package/skills/ctf-forensics/steganography.md +694 -0
- package/skills/ctf-forensics/stego-advanced-2.md +475 -0
- package/skills/ctf-forensics/stego-advanced.md +481 -0
- package/skills/ctf-forensics/stego-image.md +691 -0
- package/skills/ctf-forensics/windows.md +625 -0
- package/skills/ctf-malware/SKILL.md +181 -0
- package/skills/ctf-malware/c2-and-protocols.md +274 -0
- package/skills/ctf-malware/pe-and-dotnet.md +108 -0
- package/skills/ctf-malware/scripts-and-obfuscation.md +449 -0
- package/skills/ctf-misc/SKILL.md +498 -0
- package/skills/ctf-misc/bashjails.md +323 -0
- package/skills/ctf-misc/ctfd-navigation.md +465 -0
- package/skills/ctf-misc/dns.md +255 -0
- package/skills/ctf-misc/encodings-advanced.md +504 -0
- package/skills/ctf-misc/encodings.md +431 -0
- package/skills/ctf-misc/games-and-vms-2.md +254 -0
- package/skills/ctf-misc/games-and-vms-3.md +690 -0
- package/skills/ctf-misc/games-and-vms-4.md +229 -0
- package/skills/ctf-misc/games-and-vms.md +529 -0
- package/skills/ctf-misc/linux-privesc.md +333 -0
- package/skills/ctf-misc/pyjails.md +671 -0
- package/skills/ctf-misc/rf-sdr.md +91 -0
- package/skills/ctf-osint/SKILL.md +198 -0
- package/skills/ctf-osint/geolocation-and-media.md +464 -0
- package/skills/ctf-osint/social-media.md +312 -0
- package/skills/ctf-osint/web-and-dns.md +341 -0
- package/skills/ctf-pwn/SKILL.md +214 -0
- package/skills/ctf-pwn/advanced-exploits-2.md +579 -0
- package/skills/ctf-pwn/advanced-exploits-3.md +598 -0
- package/skills/ctf-pwn/advanced-exploits-4.md +590 -0
- package/skills/ctf-pwn/advanced-exploits-5.md +119 -0
- package/skills/ctf-pwn/advanced-exploits.md +773 -0
- package/skills/ctf-pwn/advanced.md +326 -0
- package/skills/ctf-pwn/field-notes.md +245 -0
- package/skills/ctf-pwn/format-string.md +694 -0
- package/skills/ctf-pwn/heap-fsop.md +285 -0
- package/skills/ctf-pwn/heap-techniques-2.md +333 -0
- package/skills/ctf-pwn/heap-techniques.md +513 -0
- package/skills/ctf-pwn/kernel-bypass.md +421 -0
- package/skills/ctf-pwn/kernel-techniques.md +366 -0
- package/skills/ctf-pwn/kernel.md +636 -0
- package/skills/ctf-pwn/overflow-basics.md +611 -0
- package/skills/ctf-pwn/rop-advanced.md +725 -0
- package/skills/ctf-pwn/rop-and-shellcode.md +659 -0
- package/skills/ctf-pwn/sandbox-escape.md +313 -0
- package/skills/ctf-reverse/SKILL.md +163 -0
- package/skills/ctf-reverse/anti-analysis-ctf.md +204 -0
- package/skills/ctf-reverse/anti-analysis.md +693 -0
- package/skills/ctf-reverse/field-notes.md +376 -0
- package/skills/ctf-reverse/languages-compiled.md +666 -0
- package/skills/ctf-reverse/languages-platforms.md +592 -0
- package/skills/ctf-reverse/languages.md +553 -0
- package/skills/ctf-reverse/patterns-ctf-2.md +397 -0
- package/skills/ctf-reverse/patterns-ctf-3.md +797 -0
- package/skills/ctf-reverse/patterns-ctf.md +670 -0
- package/skills/ctf-reverse/patterns-runtime.md +274 -0
- package/skills/ctf-reverse/patterns.md +572 -0
- package/skills/ctf-reverse/platforms-hardware.md +387 -0
- package/skills/ctf-reverse/platforms.md +664 -0
- package/skills/ctf-reverse/tools-advanced-2.md +421 -0
- package/skills/ctf-reverse/tools-advanced.md +407 -0
- package/skills/ctf-reverse/tools-dynamic.md +679 -0
- package/skills/ctf-reverse/tools-emulation.md +319 -0
- package/skills/ctf-reverse/tools.md +573 -0
- package/skills/ctf-web/SKILL.md +153 -0
- package/skills/ctf-web/auth-and-access-2.md +82 -0
- package/skills/ctf-web/auth-and-access.md +783 -0
- package/skills/ctf-web/auth-infra.md +321 -0
- package/skills/ctf-web/auth-jwt.md +186 -0
- package/skills/ctf-web/client-side-advanced.md +739 -0
- package/skills/ctf-web/client-side.md +529 -0
- package/skills/ctf-web/cves.md +373 -0
- package/skills/ctf-web/field-notes.md +482 -0
- package/skills/ctf-web/node-and-prototype.md +200 -0
- package/skills/ctf-web/server-side-2.md +337 -0
- package/skills/ctf-web/server-side-advanced-2.md +559 -0
- package/skills/ctf-web/server-side-advanced-3.md +125 -0
- package/skills/ctf-web/server-side-advanced-4.md +480 -0
- package/skills/ctf-web/server-side-advanced.md +378 -0
- package/skills/ctf-web/server-side-deser.md +443 -0
- package/skills/ctf-web/server-side-exec-2.md +799 -0
- package/skills/ctf-web/server-side-exec.md +457 -0
- package/skills/ctf-web/server-side.md +629 -0
- package/skills/ctf-web/sql-injection.md +790 -0
- package/skills/ctf-web/web3.md +374 -0
- package/skills/ctf-writeup/SKILL.md +90 -0
- package/skills/solve-challenge/SKILL.md +269 -0
- 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
|