hash长度拓展攻击
一、hash长度攻击的简要介绍
1、首先什么是hash长度拓展攻击?
简单来说,由于hash的生成机制原因,使得我们可以认为的在原先明文数据的基础上添加新的拓展字符,使得原本的加密链变长,进而控制加密链的最后一节,使得我们得以控制最终结果。
也就是说当我们知道hash(secret+data)的值以及secret的长度的情况下,我们就可以推算出hash(secret+data||padding||a)在这里padding是secret后面的填充内容,包含整个消息的长度,a可以是任何数据,我们需要知道secret的长度,这样才能够计算出padding。
2、什么是hash算法?
哈希算法(Hash算法)是一种将任意长度的消息压缩到固定长度的消息摘要的数学函数。哈希算法将输入消息(也称为明文)作为输入,并生成唯一的固定长度的输出,该输出称为哈希值,摘要或指纹。哈希值通常用于数字签名,数据完整性校验,数据索引和加密等安全应用中。常见的hash算法包括md5,sha-1,sha-256等。
二、MD5算法的加密流程
想要搞清楚hash长度拓展攻击的逻辑,就要先理清楚hash算法的加密流程。
这里以md5加密为例进行分析。
1 | md5的加密流程,大概分为以下几部分: |
下面我们大概分析一下每个步骤的过程:
1、填充消息
将原始消息(字节序列)填充到长度为448 mod 512的位置,使得填充后的消息长度为512的整数倍。填充方式为在原始消息末尾添加一个1,后面再补0直到长度满足要求。
也就是说当消息长度小于56个字节时要讲其填充到56个字节,大于等于56字节的要填充到对64取余的余数为8个字节.
如下图所示:
加入我们对,message进行填充:
这里的80是16进制,其代表的是二进制下面的10000000,那么这里就是补一个1和若干0,把消息补位到56个字节,也就是448bit.
2、存储长度信息
上面补位后,上面消息长度以及达到了56字节,从第57字节开始存储补位之前消息的长度
长度是小端存储。也就是高字节存放在高地址
我们还以上面的例为例:
字符串message的长度为7个字母,也就是56byte 换算成16进制是0x38
即:
3、初始化状态
md5使用四个32为寄存器(A,B,C,D)保存中间运算结果,初始值为常量,具体来说,A,B,C,D的初始值如下:
1 | A = 0x67452301 |
4、分组处理进行复杂函数处理
将填充后的消息分为若干个512位的消息块。对于每个消息块,MD5算法执行四轮循环,每轮循环包含16次操作,共计64次操作。每次操作都使用一个消息块中的32位字作为输入,对寄存器A、B、C、D进行修改,最终输出新的A、B、C、D的值。
也就是说第一个数据块与初始向量进行四轮循环,生成第一个新的字符串,保存在寄存器A,B,C,D中,寄存器继续与第二个数据块进行运算,直到最后一个数据块。
其过程可以理解为:
5、结果的输出
假设最终生成的寄存器的值是:
1 | A=0xab45bc01 |
先两两一组进行组合,得到下面的数据
1 | ab 45 bc 01 |
在进行高低位互换:
1 | 01 bc 45 ab |
最终拼接在一起就能够得到md5的值
这就是md5加密的大概过程
下面这是网上找来的流程图:
下面是我按自己的理解搞得一个流程图:
三、hash长度拓展攻击逻辑分析
上面我们对md5的加密流程进行了大概的分析
下面我们通过md5的加密流程对hash长度拓展攻击逻辑进行分析
我们这里通过一道题目进行分析:
这是22年12月举办的铁三信息安全比赛的一道原题:
代码:
1 |
|
这里可以看到
获得flag的条件:
1 | if (encrypt($user) === $_COOKIE['verify']) { |
我们输入的user经过md5加密后要与cookie里面的vefify相等,并且在输入的user里面要有root
看一下响应包:
可以得到hash(secert+guest)的值为382441859bb6709d0d9fa11ef3c255b9,secert的长度为13
那我们这里重复一下md5加密的流程:
首先是数据填充:(这里假设secret是13个A)
然后进行原始消息数据填充
18个字符,144byte 转换为16进制表示为0x90
后面就是将填充后的消息分成若干个512位的位块,然后与初始向量进行四轮循环运算,这里不再详细讲,
直到最后一个数据块与寄存器中的向量值进行四轮损害运算,得到最终向量值(A’,B’,C’,D’),在经过高低位运算就可以得到最终的md5加密的hash值。
那么我们回到题目,通过响应包我们可以得到hash(secret+guest)的值382441859bb6709d0d9fa11ef3c255b9
那我们可以推出最后得到向量值为:
1 | A'=0x85412438 |
题目要求要匹配到root,但是我们并不知道secret,所以这里可以使用hash长度拓展攻击
首先我们把要添加的数据添加上去
根据md5填充规则进行填充:
去掉前面的我们假设的secret给去除掉,可以得到
1 | guest\0x80\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00....\0x00\0x00\0x00\0x00\0x00\0x00\0x90\0x00\0x00\0x00\0x00\0x00\0x00\0x00root |
进行url编码:
1 | guest%00x80%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00..x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x00%00x90%00x00%00x00%00x00%00x00%00x00%00x00%00x00root |
这里我们虽然通过添加的方法使得字符串中有了root,但是由于我们并不知道secret,导致我们并没有办法计算出md5加密后hash值。但是由于我们实在hash(secret+guest)的hash值,但是我们这里填加了数据,所以导致还要再进行一轮运算,那么我们就可以再不知道secret的情况下计算出正确的md5加密的hash值。
这里我们上面计算出了最后一轮加密的向量值为:
1 | A'=0x85412438 |
由于我们添加了新的字符串,导致分组时分的数据块会增加,那么hash(secret+guest)的值并不是我们最终要得到的md5值,而是做完向量串继续与后面新增加的数据块进行运算,那么这样的话我们就可以再不知道secret的情况下获得md5加密的hash值。
但是这个计算式非常复杂的,我们这里使用找到的脚本进行计算:
1 | my_md5.py: |
1 | exp.py |
这里使用脚本没有计算出来,可能是自己哪里的配置有错误,但是脚本不知道怎么改,就只能使用其他师傅写好的工具去运算。
流程图:
四、hashpump工具的安装与使用
1 | git clone https://github.com/bwall/HashPump |
使用方法:
那么我们回到题目进行验证:
进行url编码:
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%00%00%00%00%00%00%00%00%90%00%00%00%00%00%00%00root |
可以看到以及攻击成功了。
五、防御方法
1 | 以下是一些防御哈希长度拓展攻击的方法: |
- 标题: hash长度拓展攻击
- 作者: GTL-JU
- 创建于: 2023-04-08 16:12:24
- 更新于: 2023-04-08 16:20:46
- 链接: https://gtl-ju.github.io/2023/04/08/hashc长度拓展攻击/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。