0x01前言
其实这个漏洞在好久之前就遇到过,只是当时水平有限,一直没有静下心来研究一下。最近一直在整理ctf的知识点,查缺补漏,下定决心要整明白这玩意。忙活了这两三天,算是初步搞定了吧(大体理解原理,知道如何利用)。。。在这里记录一下,尽最大努力说明白吧。。。
0x02漏洞简介
哈希长度扩展攻击(Hash Length Extension Attacks)是指针对某些允许包含额外信息的加密散列函数的攻击手段。
该攻击适用于在消息与密钥的长度已知的情形下,所有采取了 H(密钥 ∥ 消息) 此类构造的散列函数。MD5和SHA-1等基于Merkle–Damgård构造的算法均对此类攻击显示出脆弱性。
这种定义类的话一看就头大,我们以实验吧上的一个题目(让我进去)为例来说明。
关键代码如下:
1 | $secret = "XXXXXXXXXXXXXXX"; // This secret is 15 characters long for security! |
哈希长度扩展攻击适用于加密情况为:hash($SECRET, $message)
的情况,其中 hash 最常见的就是 md5、sha1。我们可以在不知道$SECRET的情况下推算出另外一个匹配的值。如上例所给的 PHP 代码:
- 我们知道md5($SECRET . urldecode($username . $password))的值
- 我们知道$SECRET的长度
- 我们可以算出另外一个 md5 值和另外一个 getmein 的值,使得
$COOKIE['getmein'] == md5($SECRET.urldecode($username.$password))
这样即可通过验证。如果要理解哈希长度扩展攻击,我们要先理解消息摘要算法的实现。以下拿 md5 算法举例。
0x03hash摘要算法的实现(md5为例)
md5算法的实现如下图
下面详细来说明一下md5的加密过程:
- 我们要实现对于字符串abc的 md5 的值计算。首先我们要把其转化为 16 进制。
- 补位
消息必须进行补位,即使得其长度在对 512 取模后的值为 448。也就是说,len(message) % 512 == 448。当消息长度不满 448 bit 时(注意是位,而不是字符串长度),消息长度达到 448 bit 即可。当然,如果消息长度已经达到 448 bit,也要进行补位。
补位的方式的二进制表示是在消息的后面加上一个1,后面跟着无限个0,直到 len(message) % 512 == 448。在 16 进制下,我们需要在消息后补80
,就是 2 进制的10000000。我们把消息abc进行补位到 448 bit,也就是 56 byte。 - 补长度
补位过后,倒数第8 个字节储存的是补位之前的消息长度。abc是 3 个字母,也就是 3 个字节,24 bit。换算成 16 进制为 0x18。其后跟着 7 个字节的 0x00,把消息补满 64 字节。 - 计算消息摘要
计算消息摘要必须用补位已经补长度完成之后的消息来进行运算,拿出 512 bit的消息(即64字节)。 计算消息摘要的时候,有一个初始的链变量,用来参与第一轮的运算。MD5 的初始链变量为:
A=0x67452301 B=0xefcdab89 C=0x98badcfe D=0x10325476 (注意这里这4个值是在md5算法中写死的
,写死的
。。。。)
我们不需要关心计算细节,我们只需要知道经过一次消息摘要后,上面的链变量将会被新的值覆盖,而最后一轮产生的链变量经过高低位互换(如:aabbccdd -> ddccbbaa)后就是我们计算出来的 md5 值。
0x04长度扩展攻击
问题就出在覆盖上。我们在不知道具体 $SECRET 的情况下,得知了其 hash 值,以及我们有一个可控的消息。
而我们得到的 hash 值正是最后一轮摘要后的经过高地位互换的链变量
。
我们可以想像一下,在常规的摘要之后把我们的控制的信息进行下一轮摘要,只需要知道上一轮消息产生的链变量(也就是上题中的md5($secret."adminadmin"))
。
上题符合:
1. 消息可控已知
2. 密钥长度已知
3. 使用MD5加密(基于Merkle–Damgård构造的算法)
可以使用hash长度扩展攻击 求出$x $y满足md5($secret.’admin$x’)=$y
首先将md5($secret."adminadmin")
也就是571580b26c65f306376d4f64e53cb5c7 高低位逆转作为初始链变量(此处具体过程是:将32位的md5平均分成4份,然后将这4个string转化为16进制,得到的就是初始链变量即上文提到的A,B,C,D。结合下面的代码会更容易理解)然后对附加值(随便一个长度不为0的字符串)以修改后的初始链变量进行MD5加密得到新的md5值。
下面是我从github上找的实现代码,做了简单的修改
1 | # -*- coding: UTF-8 -*- |
其实我们并不需要关心md5的加密算法是如何实现,重点看这个函数,真正实现攻击的就这个函数。
1 | def md5_attack(secret, msg_length, suffix): |
回到上面的题目,只要我们将数据填充到512(512的倍数)bit,再加上我们的附加值,就可以算出一个新的hash值,更换cookie,就可以拿到flag
长度扩展前,一个例子如下:
密文为$secret
数据为adminadmin
md5为571580b26c65f306376d4f64e53cb5c7
扩展后
密文为$secret
数据为adminadmin%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%c8%00%00%00%00%00%00%00pcat
md5为3e67e8f0c05e1ad68020df30bbc505f5
0x05利用工具HashPump
安装
1
2
3
4
5
6
7
8git clone https://github.com/bwall/HashPump
apt-get install g++ libssl-dev
cd HashPump
make
make install
#想在python里实现hashpump,可以使用hashpumpy插件:
pip install hashpumpy
python >>> import hashpumpy >>> help(hashpumpy.hashpump)使用
1
2
3
4
5# hashpump
Input Signature: 加密值 md5(secret.xxx)
Input Data: 附加值 xxx
Input Key Length: 明文长度 len(secret)
Input Data to Add: 想要添加的值 suffix
0x06漏洞实战
这种攻击一般出现在ctf中,在实战中利用较少,找来找去就只有p牛博客上的这个例子:
phpwind 利用哈希长度扩展攻击进行getshell
具体的分析过程文章写的很详细了,就不班门弄斧了。。。
0x07总结+参考资料
写了这么久,总是感觉有好多东西还没说清楚。。。很多都是直接复制人家的,专业性强的话还是写不出来。。。好在自己理解了。。。