这次国赛遇到了这个漏洞,以前看过这个漏洞,但没有吃透导致这次国赛没做出来,这次准备好好研究一下,写下这篇学习笔记
hash原理
首先要讲hash算法(例如md5),但是也不需要太了解,只需要知道以下几点就可以了
MD5加密过程中512比特(64字节)为一组,属于分组加密,而且在运算的过程中,将512比特分为32bit*16块,分块运算
我们关键利用的是MD5的填充,对加密的字符串进行填充(比特第一位为1其余比特为0),使之(二进制)补到448模512同余,即长度为512的倍数减64,最后的64位在补充为原来字符串的长度,这样刚好补满512位的倍数,如果当前明文正好是512bit倍数则再加上一个512bit的一组。
MD5不管怎么加密,每一块加密得到的密文作为下一次加密的初始向量IV,这一点很关键!!!
下面以md5算法为代表简单讲解加密过程
MD5加密过程
比如计算字符串xianyu123
,首先要把它变成十六进制,十六进制为0x7869616e7975313233
补位
(1byte=8bit)消息必须进行补位,即使得其长度在对 512 取模后的值为 448。也就是说,len(message) % 512 == 448
。当消息长度不满 448 bit 时(注意是位,而不是字符串长度),消息长度达到 448 bit 即可。当然,如果消息长度已经达到 448 bit,也要进行补位。补位是必须的。
补位的方式的二进制表示是在消息的后面加上一个1,后面跟着无限个0,直到 len(message) % 512 == 448
。在 16 进制下,我们需要在消息后补80
,也就是 2 进制的10000000
。我们把消息xianyu123
进行补位到长度为56位的整数倍数
补长度
补位过后,第 57 个字节储存的是补位之前的消息长度。xianyu123
是 9个字符串,也就是 9 个字节,72 bit。换算成 16 进制为0x48
。其后跟着 7 个字节的 0x00,把消息补满 64 字节。
长度是小端存储的,也就是说高字节放在高地址中。
MD5中存储的都是小端方式!
MD5中存储的都是小端方式!
MD5中存储的都是小端方式!
重要的事情说三遍,举个例子:假如我们这一块值为0x12345678
那么在MD5运算时候存储的顺序是0x78563412
这也是之所以后8字节为长度,而第1字节先有数据的原因
计算消息摘要
计算消息摘要必须用补位已经补长度完成之后的消息来进行运算,拿出 512 bit的消息(即64字节)。 计算消息摘要的时候,有一个初始的链变量,用来参与第一轮的运算。MD5 的初始链变量为:
1 | A=0x67452301 |
我们不需要关心计算细节,我们只需要知道经过一次消息摘要后,上面的链变量将会被新的值覆盖,而最后一轮产生的链变量经过高低位互换(如:aabbccdd -> ddccbbaa)后就是我们计算出来的 md5 值。
hash长度扩展攻击的实现
2019年国赛华东南赛区分区赛
1 |
|
这题和ISCC2018的那题一模一样
我们虽然不知道$key
的值,但是我们知道$key
的长度,也知道md5($key.'guest')
的值。而我们得到的 hash 值正是最后一轮摘要后的经过高低位互换的链变量。在常规的摘要之后把我们控制的信息进行下一轮摘要,只需要知道上一轮消息产生的链变量
COOKIE中,md5($key') = 8b3dd2057d3216ca78bf31ede947c0e3
,以及$key
的长度为32。我们来进行哈希长度扩展攻击。
长度扩展
首先$key是32个未知位,这里就用问号代替,然后跟上guest
,接着我们把消息补到448bit,也就是56byte。然后补长度,37byte=296bit,也就是十六进制的128,然后用小段方式存储
然后跟上要附加的值root
然后去掉前面的假的 $key
,得到最终的 $username
。guest\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x01\x00\x00\x00\x00\x00\x00root
urlencode之后为guest%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%28%01%00%00%00%00%00%00root
带入enc函数返回的就是md5($key+guest+root)
的值。
然后把md5($key+guest)
值作为加密root的初始链变量。
这里把原hash值(8b3dd2057d3216ca78bf31ede947c0e3)拆分为四组,即为:
8b3dd205
,7d3216ca
,78bf31ed
,e947c0e3
在此将四组数值按照小端规则反序,可得如下四组
1 | A=0x05d23d8b |
然后用hashpump跑一下,得出了md5的值为dca0083ab43d715a2eb8be1949b77469
,然后放入cookie中得到flag
CTF中的小技巧
用hashpump
Input Signature: 这里写原始的md5,也就是上面的verify
Input Data: 这里写原始的值,也就是md5($key.$text);
与$key
拼接的后面的那串值$text
Input Key Length: 这里写key的长度
Input Data to Add: 这里写你想要的text的值,比如这里md5($key.'admin');
,那么这里就填写admin
防御
- 可以将消息摘要的值再进行消息摘要,这样就可以避免用户控制message了。也就是HMAC算法。该算法大概来说是这样:
MAC =hash(secret + hash(secret + message))
,而不是简单的对密钥连接message之后的值进行哈希摘要。具体HMAC的工作原理有些复杂,但你可以有个大概的了解。重点是,由于这种算法进行了双重摘要,密钥不再受本文中的长度扩展攻击影响。HMAC最先是在1996年被发表,之后几乎被添加到每一种编程语言的标准函数库中。 - 将secret放置在消息末尾也能防止这种攻击。比如 hash(m+secret),希望推导出 hash(m + secret||padding||m’),由于自动附加secret在末尾的关系,会变成hash(m||padding||m’||secret)。现在的附加值可以看作是m’||secret,secret值不知道,从而导致附加字符串不可控,hash值也就不可控,因而防止了这种攻击。