浅析哈希长度拓展攻击

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JBlock/article/details/78446080

Hash一般译作散列,也有直接音译做哈希,本文就直接音译吧,哈哈!所谓散列算法就是,把任意长度的输入,经过复杂的运算,转化为固定长度的输出。简单来说,就是把任意长度的字节压缩为固定长度的函数。
攻击条件:
1.知道密文(SECRET)的哈希。
2.知道密文的长度。

原理:

当知道MD5(secret)时,在不知道secret的情况下,可以轻松推算出MD5(secret||padding||m’)。
在这里m’是任意数据,||是连接符,可以为空。padding是secret最后的填充字节。MD5的padding字节包含整个消息的长度,因此,为了能准确计算出padding的值,secret的长度我们也是需要知道的。

算法简析

MD5算法会对消息进行分组,每组64字节,不足64字节的部分用padding补齐。padding的规则是,在最末一个字节之后填充ox80,其余部分填充为ox00,padding最后的8个字节用来哈希的消息长度。
这里写图片描述
这是一张哈希算法流程图,根据这张图我们可以把哈希算法的流程,简单分为下面几步:
1.把消息分为n个消息块。
2.对最后一个消息块进行消息填充。
3.每个消息块会和一个输入量做运算,把运算结果作为下一个输入量。

理解MD5

这个算法十分复杂,至于算法实现细节,在这里便不再班门弄斧,我们只关心那些我们需要的部分。
根据上面讲解下面更加专业的流程:
1. Append Padding Bits(填充bits)
2. Append Length(填充长度)
3.Initialize MD Buffer(初始化向量)
4.Process Message in 16-Word Blocks(复杂的函数运算)
而要实现我们的攻击,我们只关心前三步,也就是不再再纠结复杂的算法运算。
下面用例子来解释下前三个流程:

1.MD5中填充bits的算法,该算法的流程如下:

  1. 根据消息的长度确定填充的字节数,即填充后消息长度 mod 512bit = 448bit。举个例子:一个消息是”message”,则这个消息是56bit,所以需要填充392bit。
  2. 最小填充1bit最多填充512bit,即使消息长度 mod 512 = 448bit。也就是说,不管消息长度是多少,都必须进行填充。
  3. 填充信息的第一个字节是0x80,剩余数据用0x00填充。

2.填充长度的流程:

填充长度的大小是64bit
长度是小端存储的,也就是说高字节放在高地址中
如果消息的长度大于2 ^ 64,也就是大于2048PB。那么64bit无法存储消息的长度,则取低64bit。
下图是补位的示例,要进行哈希运算的消息是字符串”message”,则:
这里写图片描述

3.初始化向量为固定值(硬编码):

A. 01 23 45 67 0x67452301
B .89 AB CD EF 0xEFCDAB89
C. FE DC BA 98 0x98BADCFE
D .76 54 32 10 0x10325476
然后,用初始化向量和补位后的消息进行复杂的函数运算,最终得到消息”message”的MD5值为78e731027d8fd50ed642340b7c9a63b3。
理解MD5长度扩展攻击
如果一个消息长度大于512bit,则会对消息按512bit进行切分,最后一个消息块进行填充操作。
假设我们知道一个7字节也就是56bit的消息的MD5值是78e731027d8fd50ed642340b7c9a63b3。
则MD5算法对其进行运算时,会先补位,由于消息的内容我们不知道,所以补位的结果如下图
这里写图片描述
然后会和初始向量进行复杂的函数运算,因为MD5值为78e731027d8fd50ed642340b7c9a63b3,故得到的结果为
A=0x0231e778
B=0x0ed58f7d
C=0x0b3442d6
D=0xb3639a7c
若向补位后的消息再追加一条消息字符串”admin”,则会对这个字符串进行补位,再利用上一个运算算出的值作为初始向量进行函数运算,最终得到的MD5值为e53a681a30ff99e3f6522270ca7db244。
这样就完成了在不知道消息的情况下,计算出消息+填充+追加消息的MD5值。
我们可以验证一下,假设验证用户登录的程序是这样的:

1.import sys 
2.  from urllib import unquote 
3. 
4.  def login(password, hash_val): 
5.      m = hashlib.md5() 
6.      secret_key = "message" 
7.      m.update(secret_key + password) 
8.      if(m.hexdigest() == hash_val): 
9.          print "Login Successful!" 
10.      else: 
11.          print "Login Failed" 
12.   
13.  if __name__ == "__main__": 
14.      password = unquote(sys.argv[1]) 
15.      hash_val = unquote(sys.argv[2]) 
16.      login(password, hash_val)

现在只知道一组用户名和hash,root和f3c36e01c874865bc081e4ae7af037ea
这里写图片描述

由分析可知,我们在知道secret_key长度的情况下,可以伪造padding,再通过追加字符串可以算出secret+padding+追加字符串的MD5值。假设我们追加的字符串为admin,则通过哈希扩展攻击计算出md5(secret+padding+追加字符串) = e53a681a30ff99e3f6522270ca7db244。然后我们测试一下,显示登录成功:

这里写图片描述

总结来说,长度扩展的原理就是将已知压缩后的结果,直接拿过来作为新的压缩输入。在这个过程中,只需要上次压缩后的结果,而不需要知道原来的消息内容时什么。假如我们知道了,md5(“secret”)我们不需要知道secret是什么,只要知道长度,人为将secret填充完,在新加一块假设为add,之前得到的MD5值作为最后一块加密的初始向量IV,最后加密得到的结果和MD5(secret+add)结果是一样的!

阅读更多
换一批

没有更多推荐了,返回首页