随机数与种子
随机数与种子
本文适合
已经知道密钥、nonce、token 都依赖随机性,准备处理时间种子、弱 PRNG、MT19937 和 nonce 复用题的学习者。学完你能:判断随机数来源是否可预测,缩小 seed 搜索范围,恢复 PRNG 状态,并把随机数弱点转化为解密、伪造或预测。
一句话判断
只要安全值来自可猜 seed、短状态、普通 PRNG 或重复 nonce,就把题目从“密码算法很强”转成“随机过程可复现”。
题目中常见信号
说明:时间种子可枚举
说明:种子空间小
说明:MT19937 不是 CSPRNG
说明:可恢复 MT19937 状态
说明:keystream 或认证风险
说明:私钥可能恢复
核心概念
PRNG 是确定性状态机:同一个 seed 会产生同一串输出。CSPRNG 的目标是即使看到部分输出也难以预测其他输出;普通 PRNG 只追求统计随机,不适合生成密钥、token、nonce。
随机数题的核心不是“猜运气”,而是把 seed 搜索范围缩到可枚举,或从足够多输出恢复内部状态。WP 中要写清楚 seed 来源、枚举窗口和验证条件。
最小分析流程
- 找随机数 API:
rand()、random、mt19937、os.urandom、secrets。 - 判断 seed 来源:固定值、时间、用户输入、系统随机。
- 收集输出格式:整数、浮点、小数、token、nonce、密钥。
- 估算搜索空间:秒级时间窗口、PID 范围、短 seed 位数。
- 写复现脚本,枚举 seed 或恢复状态。
- 用预测出的 key/token/nonce 解密或伪造,再验证 flag。
最小验证示例
import random
import time
observed = 1813382118
now = int(time.time())
for seed in range(now - 3600, now + 1):
random.seed(seed)
if random.getrandbits(31) == observed:
print("seed =", seed)
print("next token =", random.getrandbits(64))
break真实题要把 now 换成题目生成 token 的大致时间,比如响应头时间、日志时间或题目给出的日期。
常见利用 / 解题路线
路线总览:
- 时间种子枚举:根据生成时间前后几分钟/几小时爆破。
- 小 seed 爆破:seed 是 16/24/32 位或用户名哈希截断。
- MT19937 状态恢复:收集 624 个 32-bit 输出并 untemper。
- nonce 复用:AES-CTR/GCM、流密码、ECDSA/DSA 转向对应文章。
- 随机 token 预测:复现 PRNG 后预测下一次验证码、重置链接或会话值。
常见失败原因
排查动作:检查时区、毫秒/秒、是否先消耗了若干随机数
排查动作:版本、API、randint 边界和 getrandbits 位数要一致
排查动作:确认输出是完整 32-bit,浮点输出需要特殊恢复
排查动作:检查 base64/hex 编码、大小端和截断长度
排查动作:先确认同 key 下重复,且明文格式有足够已知信息
迷你案例
登录接口返回 token = hex(random.getrandbits(64)),源码显示 random.seed(int(time.time()))。响应头时间是 2026-06-01 12:00:05,枚举前后 300 秒,找到某个 seed 生成的第一个 token 与已知 token 相同。继续调用一次 getrandbits(64) 得到管理员重置 token。
这个案例的关键证据是:时间种子、可见输出、枚举窗口、下一次输出预测一致。