哈希长度扩展
哈希长度扩展
本文适合
已经理解哈希不是加密,准备处理 md5(secret + msg)、sha1(key || params) 这类认证绕过题的学习者。学完你能:判断一个 MAC 是否受长度扩展影响,枚举 secret 长度,构造追加参数和新的 digest,并解释为什么 HMAC 不走这条路线。
一句话判断
如果服务端把未知 secret 放在消息前面直接做 MD5/SHA1/SHA256,例如 hash(secret || message),攻击者知道原消息和 digest 时,就可能在不知道 secret 的情况下追加内容并伪造新 digest。
题目中常见信号
说明:可能是哈希认证
内容 2:data)`
内容 3:典型长度扩展入口
说明:可构造 msg + padding + append
说明:结构上可能受影响
说明:通常不受长度扩展影响
说明:可枚举 secret 长度
核心概念
MD5、SHA1、SHA256 这类哈希会把消息按块压缩,最终 digest 本质上是内部状态。长度扩展攻击利用这个状态继续压缩追加数据。攻击者不需要知道 secret 的内容,只需要猜中 len(secret),因为 padding 依赖的是长度。
受影响的是 hash(secret || message)。如果写成 hash(message || secret),不能直接扩展出合法后缀;如果使用 HMAC,外层结构会阻断这种继续压缩。
最小分析流程
- 找认证公式:确认是否是
secret/key拼在消息前面。 - 确认哈希类型:MD5、SHA1、SHA256 等 Merkle-Damgard 优先。
- 记录已知原消息和原 digest。
- 选择要追加的内容,例如
&admin=true。 - 枚举 secret 长度,生成
forged_msg和forged_digest。 - 发送请求验证,观察是否绕过签名检查。
最小验证示例
# pip install hashpumpy
import hashpumpy
orig_msg = "user=guest"
orig_mac = "0123456789abcdef0123456789abcdef"
append = "&admin=true"
for secret_len in range(4, 33):
new_mac, new_msg = hashpumpy.hashpump(orig_mac, orig_msg, append, secret_len)
print(secret_len, new_mac, new_msg)真实题里把 new_msg URL 编码后提交,并把 token 换成 new_mac。如果某个 secret 长度返回管理员页面或 flag,就说明长度扩展成立。
常见利用 / 解题路线
路线总览:
- URL 参数签名:
?data=user=guest&sign=md5(secret+data),追加&admin=true。 - Cookie/token 伪造:明文 token 带 digest,构造 padding 后的权限字段。
- 文件校验绕过:服务端只用
sha1(secret + content)验证上传内容。 - API 签名绕过:签名覆盖 query string,但拼接方式错误。
- SHA256 变体:工具不支持时手写状态注入或换 hlextend。
常见失败原因
排查动作:检查是否 HMAC、是否 msg + secret、是否签名字段也被纳入消息
排查动作:对 padding 字节做 URL 编码,确认 &admin=true 没被吞
排查动作:Python 版本和 hashpumpy 兼容性常见,换 hlextend 或手写实现
排查动作:参数解析可能取第一个值,尝试追加同名参数或改变顺序
排查动作:确认 digest 长度、大小写、hex/raw 格式和消息编码
迷你案例
服务端给:
data=user=guest
sign=md5(secret + data)目标是追加 &role=admin。枚举 secret 长度 8 时,工具生成:
data=user=guest%80...%78%00%00%00%00%00%00%00&role=admin
sign=<new_md5>请求返回 hello admin。WP 应说明:原签名方式是 secret 前缀 MAC,MD5 可长度扩展,secret 长度通过枚举命中,最终追加参数被服务端解析。