hash扩展攻击

这次国赛遇到了这个漏洞,以前看过这个漏洞,但没有吃透导致这次国赛没做出来,这次准备好好研究一下,写下这篇学习笔记

hash原理

首先要讲hash算法(例如md5),但是也不需要太了解,只需要知道以下几点就可以了

  1. MD5加密过程中512比特(64字节)为一组,属于分组加密,而且在运算的过程中,将512比特分为32bit*16块,分块运算

  2. 我们关键利用的是MD5的填充,对加密的字符串进行填充(比特第一位为1其余比特为0),使之(二进制)补到448模512同余,即长度为512的倍数减64,最后的64位在补充为原来字符串的长度,这样刚好补满512位的倍数,如果当前明文正好是512bit倍数则再加上一个512bit的一组。

  3. 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
2
3
4
A=0x67452301
B=0xefcdab89
C=0x98badcfe
D=0x10325476

我们不需要关心计算细节,我们只需要知道经过一次消息摘要后,上面的链变量将会被新的值覆盖,而最后一轮产生的链变量经过高低位互换(如:aabbccdd -> ddccbbaa)后就是我们计算出来的 md5 值。

hash长度扩展攻击的实现

2019年国赛华东南赛区分区赛

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);
include "flag.php";
$user=$_POST['user'];
function encrypt($text){
global $key;
return md5($key.$text);
}
if (encrypt($user)===$_COOKIE['verify']) {
if(is_numeric(strpos($user,'root'))){
die($flag);
}
else{
die('not root!!!');
}
}
else{
setcookie("verify",encrypt("guest"),time()+60*60*24);
setcookie("len",strlen($key),time()+60*60*24);
}
//show_source(__FILE__);

这题和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
2
3
4
A=0x05d23d8b
B=0xca16327d
C=0xed31bf78
D=0xe3c047e9

然后用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

防御

  1. 可以将消息摘要的值再进行消息摘要,这样就可以避免用户控制message了。也就是HMAC算法。该算法大概来说是这样:MAC =hash(secret + hash(secret + message)),而不是简单的对密钥连接message之后的值进行哈希摘要。具体HMAC的工作原理有些复杂,但你可以有个大概的了解。重点是,由于这种算法进行了双重摘要,密钥不再受本文中的长度扩展攻击影响。HMAC最先是在1996年被发表,之后几乎被添加到每一种编程语言的标准函数库中。
  2. 将secret放置在消息末尾也能防止这种攻击。比如 hash(m+secret),希望推导出 hash(m + secret||padding||m’),由于自动附加secret在末尾的关系,会变成hash(m||padding||m’||secret)。现在的附加值可以看作是m’||secret,secret值不知道,从而导致附加字符串不可控,hash值也就不可控,因而防止了这种攻击。

Reference

  1. hash哈希长度扩展攻击解析(记录一下,保证不忘)
  2. Hash长度扩展攻击
  3. 用MD5实现hash长度扩展攻击 By Assassin
  4. 哈希长度拓展攻击(Hash Length Extension Attacks)

本文标题:hash扩展攻击

文章作者:xianyu123

发布时间:2019年07月09日 - 15:50

最后更新:2021年02月17日 - 23:15

原始链接:http://0clickjacking0.github.io/2019/07/09/hash扩展攻击/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------    本文结束  感谢您的阅读    -------------