Hash长度拓展攻击

前言

看到smi1e师傅的一篇博客,感觉挺有意思,就学习并记录了一下。

Hash算法简介

Hash加密算法,又叫散列算法或者摘要算法;是一种通过单向函数加密明文生成消息摘要的算法。

常见的Hash函数有md5和sha-1,这里我们着重讲一下md5。

hash函数获得字符串输入时,需要先将消息进行分组,分组长度为512位,也就是64字节;另外加密字符串的长度需要满足M mod 512 == 448,若不满足则对分组进行字符填充,输出128位,一般转为32位的16进制表示。

补位

前面已经说过,当需要进行消息摘要的明文长度不满足与448模512同余时,需要进行补位。这里需要注意的是:补位是必须进行的操作;即使消息长度刚好为448bit,一样需要补位,并且补位的长度为512bit

补位的方式是在消息的后面加上一个1,然后无限补0,直到满足 M mod 512 == 448。

在十六进制下,我们需要在消息的后面补80,因为(hex)80 = (bin)10000000,然后一直补0,直到448位。

补位完成

储存长度

为什么补位只补到448位(即56字节)呢?因为剩下的8字节用来储存原消息的长度,单位为bit*

这里强调一点:MD5的存储方式为小端存储

用一句话来概括就是数据的高位字节保存在高地址,低位字节保存在低地址;

相反大端存储就是数据的高位字节保存在低地址,低位字节保存在高地址。

例如有一串数据为:0x12 34 56 78

那么在MD5运算中的存储顺序为:0x78 56 34 12

所以我在Winhex上的例子中,消息长度为17字节,136位,十六进制为88所以应该如下填充:

填充完成

详细了解:大小端存储

计算消息摘要

当准备工作都好了,每个分组都是512bit后,就会开始进行加密运算。

加密的具体细节有点复杂,这里我只大体讲一下加密模式,以及什么情况下存在可利用点。

加密流程

根据加密流程图可以发现,每一轮的加密密钥为上一轮加密后的输出(第一轮由给定的初始链变量作为密钥), 这个我们一般称为密文链接模式。

如果我们已知上一轮的加密密钥(即上一轮的hash值),通过添加字符拓展消息长度使得MD5进行多一轮的加密运算,很容易可以得到新的hash值来绕过某些过滤。

Hash长度拓展攻击的实现

ISCC2018的一道题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
include "secret.php";
@$username=(string)$_POST['username']; //POST传参
function enc($text){
global $key;
return md5($key.$text);//关键代码
}
if(enc($username) === $_COOKIE['verify']){//要求出md5($key.$username)的hash值
if(is_numeric(strpos($username, "admin"))){//传参中还要有"admin"
die($flag);
}
else{
die("you are not admin");
}
}
else{
//已知 md5($key."guest")
setcookie("verify", enc("guest"), time()+60*60*24*7);
//已知$key的长度
setcookie("len", strlen($key), time()+60*60*24*7);
}
show_source(__FILE__);
?>

通过抓包我们可以知道:

md5($key.”guest”)=f8d7a112644f7e71e1e8ad068f144f61

len($key)=21

下面我们来进行哈希拓展长度攻击。

长度拓展

补位,因为$key是21字节长度,我们用21个’x’来暂时代替key的值;紧跟着的是字符串’guest’,然后根据补位方法把消息补到448bit,然后计算数据长度:hex( (21+5)*8 ) =0xD0,然后根据小端储存填充剩余位数。

补充完成

然后因为代码要求POST的值要有”admin”,我们添加一个”admin”作为拓展。

拓展完成

然后去掉前面设的x,得到最终的$username。

1
guest\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xD0\x00\x00\x00\x00\x00\x00\x00admin

这里注意要把hex转换为url编码,用python将r’\x’替换成’%’即可。

1
guest%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%d0%00%00%00%00%00%00%00admin

接下来为了让判断成立,我们计算出拓展之后的cookie为多少。

1
if(enc($username) === $_COOKIE['verify'])

这里直接用hashpump可以直接跑出来.

hashpump脚本跑出md5

得到我们需要的cookie值,即$_COOKIE[‘verify’]。

这样就满足了所有条件,最后得到flag。

flag

总结

总的来说,hash拓展长度攻击就是在未知被加密的数据$key、但是md5()函数输入的消息长度可控、而且已知被加密的位数和相应的消息摘要,通过拓展被加密数据的长度,将原信息摘要作为密钥输入,对拓展后数据生成新的摘要,从而满足过滤条件。

0%