后面加上随机值:
这就引出本文的攻击方式。
MD5 算法计算逻辑
md5算法本质上是一种压缩算法,将长度小于2^64
bit的任意字符压缩成128bit固定长度字符。同AES之类的分组加密算法一样,md5也需要进行分组计算。
- 分组&填充
- 具体计算
1.分组&填充
首先会对明文按照512bit的长度进行分组,最后一个分组可能会发生长度不足512bit,或者刚刚512bit。
- 假设明文刚好能被512整除,需要新增一个分组,在末尾
- 假设整除512bit余数大于0
- 且余数大于
512-8*8 =448 bit
则需要继续填充10000
(0x80000.....)至下一个分组的448bit,剩余的bit按照小端存储填充原始明文长度 - 余数小于448bit,末尾填充原始明文长度,中间剩余部分填充填充
10000
(0x80000.....)
- 且余数大于
8*8=64bit
按照小端存储放入原始明文的长度,分组中间剩余的bit 按照10000000
的方式进行填充,形成一个总长512bit的新分组。
以上两个步骤用代码表示即为:
m_l = len(message # 原始消息长度
length = struct.pack('<Q', (m_l * 8 # 长度转化为小端 unsigned long long 8B
blank_padding = b""
message += b'\x80' # 10000000
# 此分组不足以填充长度时
if 56 < len(message < 64:
blank_padding += b'\x00' * (56 + 64 - len(message # 填充至下一个分组
# 分组能填充长度
else:
blank_padding = b'\x00' * (56 - len(message % 64 # 本分组填充
if len(blank_padding > 0:
# 填充10000
message += blank_padding
# 填充长度
message += length
2. 具体计算
其实具体的计算过程,我们不用关注,把这个过程当做一个黑盒(关注细节的可以关注文末github地址)就可以:
哈希长度拓展攻击
了解了md5的计算逻辑,再回到这张图,上一次的的输出作为下一次的输入这种方式可能会导致一个问题。假设存在明文分组abc
,明文分组产生md5的过程可以简化为:
- 分组a:h.a = md5(iv,a, md5计算需要两个参数,iv 为初始序列,h.a 为压缩计算结果
- 分组b:h.ab = md5(h.a,b
- 分组c或者最终md5: h.abc = md5(h.ab,c
即:h.abcd = md5(h.abc,d
接下来我们根据实际的例子来实操下
实操
good_price修改,会因为签名校验不通过:
<?php
$total_score = 300;
$flag = 'xxxxxxxxxxxx';
$secret_key = "??????????????????????????????????????"; // 前端未知
$post_data =urldecode(file_get_contents("php://input";
$user_sign = $_GET['signature'];
$sign = md5($secret_key.$post_data;
if ($user_sign === $sign {
$price = $_POST['good_price'];
if ($price > $total_score{
echo '对不起,您的积分余额不足,交易失败!';
}else{
echo "恭喜,购买成功!$flag";
}
}else{
echo '签名数据被篡改!';
}
?>
后端存在一个签名逻辑,会验证用户的post参数加上密钥的md5值,如果用户修改了post参数,但因为不知道密钥也就没发生成合法的md5,所以验证会不通过。
而且这个地方密钥被放在了明文前面拼接,针对哈希长度扩展攻击,利用起来还挺简单的,可以使用现成的工具,比如hashpump
,按照提示输入内容即可:
总结
参考
- 《白帽子讲web安全》
- 先知社区
- hashpump