密码协议分析专题
密码协议分析专题
本文适合
已经理解 编码、哈希与加密、随机数与种子、签名算法与nonce复用 和基础 Web/API 交互的学习者。学完你能:把协议题拆成参与者、消息、密钥、认证目标和攻击面,定位重放、未绑定身份、降级、KDF 错误和签名范围错误
一句话判断
题目不是单独问 AES、RSA、哈希或签名,而是给出多轮交互、多个角色、令牌、握手、挑战响应、密钥协商或登录流程时,就按密码协议题分析。
密码协议题的重点通常不是“算法坏了”,而是算法组合、消息顺序、身份绑定、随机数、签名范围或状态机出了问题。
题目中常见信号
协议材料:
Alice, Bob, Server
nonce, timestamp, session_id
challenge, response
token, ticket, proof
sign(message)
mac(key, message)
KDF(secret, context)服务形态:
- TCP 交互服务。
- Web 登录/换票接口。
- 多个 JSON API。
- 给出客户端和服务端源码。
- 给出抓包、日志或 transcript。
危险信号:
- 签名只覆盖部分字段。
role、user、alg、kid、session可控但未认证。- nonce 可预测、复用或没有绑定会话。
- 加密后没有 MAC。
- 协商结果没有被签名或 MAC 覆盖。
- 不同协议阶段复用同一种消息格式。
核心概念
协议分析先看安全目标,再看每一步是否真的实现目标。
常见目标:
要问的问题:攻击者能不能读到明文或密钥
要问的问题:字段能不能被改而不被发现
要问的问题:A 怎么确认对方真的是 B
要问的问题:旧消息能不能再次使用
要问的问题:双方是否真的得到同一个密钥
要问的问题:签名者能否否认消息
密码协议里,“加密”和“认证”不能混为一谈。加密保护内容不被看见,认证保护内容不被篡改。很多 CTF 题故意只做了其中一个。
最小分析流程
1. 画参与者和消息表
先把交互写成表:
发送方:Client
接收方:Server
消息:user, nonce
保护方式:无
发送方:Server
接收方:Client
消息:challenge
保护方式:无
发送方:Client
接收方:Server
消息:token, mac
保护方式:MAC
不要一开始就套攻击。先把数据流画清楚。
2. 标注字段属性
每个字段标注:
- 谁生成。
- 谁能控制。
- 是否随机。
- 是否保密。
- 是否被签名/MAC 覆盖。
- 是否和身份、角色、会话绑定。
3. 找验证点
逐项问:
服务端验证了什么?
客户端验证了什么?
验证失败时返回什么?
字段解析是否一致?有字段被使用但没有被验证,就是重点。
4. 做最小正常复现
先写脚本跑通正常流程:
r1 = login(user)
r2 = challenge(r1["sid"])
token = make_token(user, r2["nonce"])
print(submit(token))跑通后再改一个字段,观察哪个修改能绕过验证。
5. 构造攻击消息
优先尝试:
- 重放旧消息。
- 替换身份字段。
- 替换角色字段。
- 替换算法字段。
- 复用另一个阶段的签名。
- 删除或插入 JSON 字段。
最小验证示例
验证签名范围错误
假设 token:
{
"user": "guest",
"role": "user",
"exp": 1710000000,
"sig": "sign(user || exp)"
}服务端只签了 user 和 exp,没有签 role。最小验证:
tok = get_guest_token()
tok["role"] = "admin"
print(submit(tok))如果返回管理员数据,漏洞不是签名算法被破解,而是签名范围没有覆盖关键字段。
验证重放攻击
保存一次成功请求:
POST /transfer
X-Token: ...
amount=10&to=bob&nonce=1234重复发送:
curl -i -X POST http://target/transfer \
-H 'X-Token: ...' \
--data 'amount=10&to=bob&nonce=1234'如果第二次仍成功,说明 nonce 或状态没有正确消费。
常见利用 / 解题路线
路线总览:
路线一:重放攻击
适合:
- 没有 nonce、时间戳、计数器。
- nonce 没绑定用户或会话。
- 成功请求可重复提交。
步骤:
- 抓一次合法请求。
- 原样重发。
- 改动金额、目标、角色等字段。
- 观察服务端是否拒绝旧消息或重复 nonce。
关联:随机数与种子。
路线二:身份或角色未绑定
适合:
user、role、uid、is_admin和签名/MAC 分离。- 服务端从不同字段读取身份。
步骤:
- 找 token 中所有身份字段。
- 分别修改。
- 判断哪个字段用于鉴权,哪个字段用于验签。
- 构造“验签通过但鉴权提升”的消息。
路线三:降级攻击
适合:
alg = none
mode = ECB
hash = md5
version = 1步骤:
- 修改协商字段。
- 检查协商结果是否被签名/MAC 覆盖。
- 强迫双方使用弱算法或空算法。
- 利用弱算法恢复明文或伪造消息。
路线四:密钥派生错误
适合:
- 直接把 Diffie-Hellman 结果当密钥。
- KDF 没有 salt 或上下文。
- 加密密钥和 MAC 密钥复用。
- 不同用户、不同用途共用密钥。
步骤:
- 找 KDF 输入。
- 检查是否包含身份、角色、协议版本、方向和用途。
- 构造跨用户或跨用途复用。
- 验证能否解密、伪造或反射消息。
路线五:签名范围和解析歧义
适合:
- JSON/XML/URL 参数有重复字段。
- 签名字符串和服务端解析结果不一致。
- 签名未覆盖关键字段。
步骤:
- 找签名输入字符串。
- 找业务逻辑读取的字段。
- 构造重复字段或编码差异。
- 让验签看到 A,业务看到 B。
关联:签名算法与nonce复用、哈希长度扩展。
常见失败原因
- 只看算法,不看消息是否被认证:AES/RSA 本身正确也可能被乱用。
- 以为有签名就安全:签名范围不覆盖关键字段就没用。
- 忽略状态机:登录、换票、提交三个阶段的消息可能被混用。
- 不区分加密和认证:密文可被篡改时,机密性不等于完整性。
- 没有跑通正常流程:攻击脚本失败可能只是字段格式错。
- 忽略解析差异:签名端和业务端对 JSON、URL 编码、重复参数处理不同。
- 没有记录时间线:协议题经常靠顺序和重放判断漏洞。
迷你案例
题目给登录 token:
{
"uid": "1001",
"role": "user",
"nonce": "8a31",
"sig": "HMAC(secret, uid || nonce)"
}服务端验证:
check_hmac(uid + nonce, sig)
if token["role"] == "admin":
show_flag()分析:
role被业务逻辑使用。role没有进入 HMAC。- 攻击者不需要破解 HMAC,只要改未覆盖字段。
最小攻击:
tok = {
"uid": "1001",
"role": "admin",
"nonce": "8a31",
"sig": original_sig
}
print(submit(tok))如果返回 flag,WP 要写:
漏洞类型:签名范围错误
关键证据:sig = HMAC(uid || nonce),未覆盖 role
利用方式:保留 uid/nonce/sig,修改 role=user 为 role=admin这就是协议题的典型闭环:不是破解算法,而是证明“被保护的字段”和“被信任的字段”不一致。