哈希长度扩展攻击


一、MD5哈希算法

1.1 MD5简介

MD5(Message Digest Algorithm 5)是一种广泛使用的加密散列函数,它可以产生一个128位(16字节)的散列值(hash value),通常用一个32位的十六进制字符串表示,且具有不可逆性(即理论上从散列值无法推导出明文)。MD5由Ron Rivest在1991年设计,最初被用来作为一种安全的密码散列算法。它接收任意长度的输入,并产生一个固定长度的输出,这个输出被认为是输入数据的“指纹”。然而,由于后来发现MD5存在安全漏洞,它不再被推荐用于密码存储等安全敏感的应用。尽管如此,MD5仍然被用于一些非安全相关的场合,比如文件完整性检查。

1.1.1 php 中md5函数的用法示例

<?php
echo md5("test string");
?>
= php5.6 index.php
6f8db599de986fab7a21625b7916589c

1.1.2 哈希函数与加密函数的区别

哈希将目标转换为具有相同长度的、不可逆的杂凑字符串;

加密则是将目标转化为不同长度的、可逆的密文,长度一般随明文增长而增加;

1.1.3 常见的哈希算法介绍

当前最常用的哈希算法有MD5SHA-1SHA-2(SHA-224、SHA-256、SHA-384,和SHA-512并称为SHA-2)等。

1.2 md5哈希算法流程详解

1.2.0 步骤概览

MD5算法的详细流程可以概括为以下几个步骤:

1)明文填充

2)初始化变量

3)分块处理

4)循环压缩函数处理

5)更新寄存器

6)生成哈希值

具体的数据处理流程如图所示:

算法数据处理流程概览

1.2.1 明文填充

MD5以512bit为分块处理输入信息,每个分块又被分为16*32的子分组,最终输出为4*32的分组,即32位的16进制字符串。

  • 填充

输入信息的长度(bit)对512求余不等于448时,使用OneAndZeroes对输入信息进行填充使得对512求余448。

假设对”abc”进行MD5计算,填充步骤:

明文填充示意图
  • 记录信息长度

填充完成后,用64bit存储信息的长度,”abc”共有24bit,即0x18bit,记录的消息长度为:

长度记录

1.2.2 初始化变量

MD5的哈希结果长度为128位,按每32位分成一组共4组。这4组结果是由4个初始值A、B、C、D经过不断演变得到。算法初始化四个32位的寄存器A、B、C、D,这些寄存器同时用于存储中间计算结果

MD5的官方实现中,A、B、C、D的初始值如下(16进制):

A=0x01234567

B=0x89ABCDEF

C=0xFEDCBA98

D=0x76543210

1.2.3 分块处理

将填充后的数据按照512位(64字节)一块进行处理,每块包含16个32位字。

1.2.4 循环压缩函数处理

对每个512位的数据块进行四轮循环压缩函数处理,每轮处理包括四个步骤:F函数、G函数、H函数和I函数。这些非线性函数在循环压缩函数处理中起着重要作用。

主要流程是以512位的分块为单位,每一分块经过4轮循环,每轮循环16次迭代,输出128位的结果,存放在缓冲区中,作为下一轮循环缓冲区的输入。

4轮循环的逻辑如图所示:

512bit的数据处理流程

从缓冲区输入128位,从消息分组输入512位,输出结果128位,要注意结果是由循环的结果加上缓冲区的值得到的(加法为模$2^32$加法)。A,B,C,D就是哈希值的四个分组。每一次循环都会让旧的ABCD产生新的ABCD。一共进行多少次循环呢?由原文长度决定。假设处理后的原文长度是M,主循环次数 = M / 512,每个主循环中包含 (512/32) * 4 = 64 次子循环。上面这张图所表达的是单次子循环的流程。

每轮循环中单次迭代的逻辑如图所示:

单轮循环中的单次迭代逻辑

每轮循环迭代运算的逻辑:

(1)对A迭代:a <—— b+((a+g(b,c,d)+X[k]+T[i])<<<s)
(2)缓冲区(A,B,C,D)作循环轮换:(B,C,D,A) <——(A,B,C,D)
  • a,b,c,d是缓冲区的当前值
  • g是4个轮函数之一,输入输出都是32bit,进行不同的逻辑运算
  • <<<s(CLS(s))是指把bit32循环左移s位,s可查表得到
  • X[k]是当前处理消息分组的第k32bit(一共512/32=16个字),在每一轮循环中都由不同的公式计算出来
  • T[i]通过查表得到,32bit字
  • 所有的加法都是模$2^32$加法

4个轮函数逻辑如图所示:

4个轮函数

每轮循环中X[k]所取的k的计算方法为:

j为当前迭代轮次

  1. 第一轮循环:k = j
  2. 第二轮循环:k = (1 + 5 * j) % 16
  3. 第三轮循环:k = (5 + 3 * j) % 16
  4. 第四轮循环:k = (7 * j) % 16

1.2.5 更新寄存器

根据每轮的计算结果更新寄存器A、B、C、D的值。

1.3.6 生成哈希值

最后一轮得到的结果经过高低位互换后就是最终的结果。

6f8db599de986fab7a21625b7916589c

A=0x99b58d6f
B=0xab6f98de
C=0x5b62217a
D=0x9c581679

A B C D 分别按字节反转后拼接构成最后的哈希值。

二、哈希长度扩展攻击原理

2.1 攻击场景

2.1.1 攻击场景1-文件下载权限验证

Message Authentication Codes (MACs)是用于验证信息真实性的算法。最简单的MAC算法是这样的:服务器把key和message连接到一起,然后用摘要算法如MD5SHA1取出摘要。

例如有一个网站,在用户下载文件之前需验证下载权限。

这个网站会用如下的算法产生一个关于文件名的MAC:

def Create_MAC(key, filename)
   	return Digest::MD5.hexdigest(key + filename)
end

key对于攻击者来说是未知的。

用户请求下载test.pdf文件时提交如下请求:

http://www.example.com/download?file=test.pdf&mac=ca21cf672b66a5ee6fa7fc7c1c314ff3

当用户发起请求要下载一个文件时,会执行下面这个函数:

def verify_mac(key, filename, userMAC)
    	validMAC = create_MAC(key, filename)
    	if (validMAC == userMAC) do
        	initiateDownload()
    	else
        	displayError()
    	end
end

服务端根据key和用户提交的文件名生成一个哈希值,跟用户提交的哈希值做比对,比对成功才会允许下载文件,这种情况下要求文件名和用户提供的mac值都是合法的。

本意是通过key的保密性来验证身份,因为正常情况下只有服务端和合法客户端才掌握key。

这样,只有当用户没有擅自更改文件名时服务器才会执行initiateDownload()开始下载。

但是这种生成MAC的方式,会给攻击者在文件名后添加自定义的字符串留下隐患。

这种方法就是哈希长度拓展攻击。

攻击结果:不知道key、不知道合法文件名,但是能通过验证。

2.1.2 攻击场景2

在一道web题目中遇到了以下判断: if ($COOKIE["md5hash"] === md5($secret . $input))

在该题目中我们可以掌握的参数有md5hashinput的值,secret的md5值和长度,我们需要想办法让这个判断通过。

难点在于:不知道$secret的情况下,传递一个哈希值和input,使得哈希值===md5($secret.$input)。

2.2 哈希长度扩展攻击简介

哈希长度扩展攻击(Hash Length Extension Attacks)是一种针对某些加密散列函数的攻击手段,特别适用于那些基于Merkle–Damgård结构的算法,如MD5和SHA-1。这类攻击的核心在于,如果你知道一个消息(message)和密钥(key)的组合的哈希值,即使不知道密钥的具体值,只要知道密钥的长度,你就能在这个消息后面添加额外的信息,并计算出新的哈希值。

md5(xxxxx+"plainText")=cdf1ea..
md5(xxxxx+"plainText"+"abcdfafa")=ade24242..

攻击条件:

  1. 消息可控已知
  2. 密钥长度已知
  3. 使用MD5加密且结果可知

2.3 攻击步骤

2.3.1 以某CTF赛题为例子

  • 代码如下:
CTF赛题实例

2.3.2 条件分析

从代码中已知$this->sess=md5($this->token.$this->username),在不传递任何参数的情况下,$sess为token(20个未知字符)与”admin”组成的字符串的md5值,并且会在cookie中返回。

$username=$_COOKIE['username'];
$sess=$_COOKIE['session'];

从上面的代码可知,$sess和$username的值是用户提交的。

获取flag的条件是:提交一个伪造的非”admin”的username,并且提交该username与token拼接之后的md5值(也就是$sess),但是由于toekn不为我们所知晓,我们也无从计算出正确的md5值。

我们目前掌握的信息:

1)????????????????????admin 的哈希值 (?表示的内容为未知token),记为H1。

2)未知token的长度为20。

3)username和sess的值是可控的。

2.3.3 核心要点

巧妙利用md5哈希算法的分组运算机制。

将H1当作某个明文(这个明文我们需要伪造)中的第一个数据块(512bit)的散列值。根据md5算法以每512bit为数据块计算散列值的原理,当计算第二个数据块的散列值时,会以H1作为ABCD寄存器的缓存值。

我们如果能把字符串扩展到512bit以上,并保证第一段的md5运算结果不改变,那么我们在不知道第一段的加密内容的情况下仍然能够求得整体的md5值。此所谓”扩展”的含义。

2.3.4 攻击实施

1)模仿md5算法的填充

所以我们首先要做的就是”模仿”md5算法的补位方式来进行补位。

????????????????????admin的长度=20+5=25个byte=25*8=200bit

表示成16进制即为:0xC8=12*16+8=200

所以填充的结果如下(称为M1):

【????????????????????admin】【8000000....000000】【c800000000000000】

【(????????????????????admin)】为消息内容。

【8000000….000000】为填充值。

【c800000000000000】为长度填充。

如此一来,M1经过md5算法计算的结果,跟md5(????????????????????admin)计算的结果是一致的。我们只是作了md5算法本身就该做的事情。

md5(M1)===md5(????????????????????admin)

如前文所述,实际上在本实例中上述值我们是知道的,eg:ae8b63d93b14eadd1adb347c9e26595a(H1)

2)将已知的H1值作为作为ABCD寄存器的缓存值

ae8b63d93b14eadd1adb347c9e26595a分成8字节唯一组的四组,ae8b63d9,3b14eadd,1adb347c,9e26595a

由于md5是小端存储,进行相应的调整:

A=0xd9638bae
B=0xddea143b
C=0x7c34db1a
D=0x5a59269e

3)拼接第二段内容

M1+fakeadmin

将我们求出来的ABCD序列作为初始序列,利用md5算法对fakeadmin这个字符串进行md5加密。得到md5值:

比如是:bdbe1c6fb9d921e4ba3d9d4072b702f7(H2)

这个H2===md5(M1+fakeadmin)

2.3.5 修改cookie获取flag

username=M1+fakeadmin
session=bdbe1c6fb9d921e4ba3d9d4072b702f7

三、攻击工具

3.1 hash_extender

https://github.com/iagox86/hash_extender 【hash_extender】

-d 被扩展的明文
-a 附加的到原来hash的padding
-l 盐的长度
-f 加密方式
-s 带盐加密的hash值
--out-data-format 输出格式
--quiet 仅输出必要的值

3.2 hashpump

3.2.1 安装

1)Mac

brew install hashpump

报错:

Error: hashpump has been disabled because it has a removed upstream repository! It was disabled on 2024-09-16.

2)kali

sudo apt-get update
sudo apt-get install libssl-dev
//git clone https://github.com/bwall/HashPump.git 这个地址已经不存在了
git clone https://github.com/2H-K/hashpumpy_changed
cd hashpumpy_changed
make
make install

3.2.2 使用

  • -s--signature:从已知消息中获取的签名(哈希值)。
  • -d--data:已知消息的数据。
  • -a--additional:你想要添加到已知消息中的信息。
  • -k--keylength:用于签署原始消息的密钥的字节长度。

3.2.2 实战使用

见本站《web-buuoj-([De1CTF 2019]SSRF Me)》一文。

┌──(root💀kali)-[~]
└─# hashpump
Input Signature: 8100319869013029db5beab17bfa9ba9
Input Data: scan
Input Key Length: 24
Input Data to Add: read
677ae112e086b6faf3c0f2088773371c
scan\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\xe0\x00\x00\x00\x00\x00\x00\x00read


文章作者: 司晓凯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 司晓凯 !
  目录