@shaobaobaoer
2018-03-06T04:02:34.000000Z
字数 6161
阅读 1511
CTF
1月份的时候(当自己完全是个小白的时候),做到了哈希长度扩展攻击的题目,那时候一点也不会,也没人教自己,看解析也看不懂。
今天花了点时间,终于搞明白了。
参考网站
https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks
想做一个脚本小子的话,直接翻到最后吧
但是该会的还是要会的吧
说什么理论,我不是很喜欢,单刀直入说个例题,这是实验吧的题目,也是我当初死活没搞懂的题目
<?php
$flag = "flag{flag is here}";
$secret = "aaaaabbbbbccccc"; // This secret is 15 characters long for security!
@$username = $_POST["username"];
@$password = $_POST["password"];
if (!empty($_COOKIE["getmein"])) {
if (urldecode($username) === "admin" && urldecode($password) != "admin") {
if ($_COOKIE["getmein"] === md5($secret . urldecode($username . $password))) {
echo "Congratulations! You are a registered user.\n";
die ("The flag is ". $flag);
}
else {
die ("Your cookies don't match up! STOP HACKING THIS SITE.");
}
}
else {
die ("You are not an admin! LEAVE.");
}
}
setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7));
if (empty($_COOKIE["source"])) {
setcookie("source", 0, time() + (60 * 60 * 24 * 7));
}
else {
if ($_COOKIE["source"] != 0) {
echo ""; // This source code is outputted here
}
}
?>
md5哈希扩展攻击,都有一个普遍的规律,我们需要知道字符串的长度
username和password不能都没admin,有个cookie叫getmein其需要等于md5(secret+username+password)的值。
- 1.首先$secret
变量我们不知道,但是我们知道它的长度
- 2.从下面setcookie函数我们可以通过sample-hash这个变量拿到md5($secret+”admin”+”admin”)
的md5值
- 3.我们登陆成功的条件,username==admin&&password!=admin 并且我们要cookie getmein的值等于md5($secret+username+password)
MD5最神奇的地方,就是无论多长的东西,都给你整成一定长度的东西。
MD5算法最重要的就是补位,md5的加密算法的具体算法,非常的复杂。但是过程的话,在此简略写一下
补位
转换成16进制后,对消息进行补位操作,当字符串不满 448 bit时候,将它们补 0 加到 448 bit,然后加上80表示填充开始。注意是位而不是字符串长度,若是说字符串长度的话 应该是 56 的长度 (按照ascii)
补位的时候也是小端绪的。就是本来应该是0000...长度(16)。啥是小端绪的话,看看下面的就行。
补上初始长度
在57字节之后是存储的字符串长度。紧跟在57位后面,并计算成16位。不包含填充字符串的长度,注意是比特数而不是字符串的长度。(十进制下,长度*8)后面继续用0b00补齐成64位。
计算摘要
利用初始变量进行计算,最后输出结果,MD5初始链变量(magic number):
其中的计算过程太过于繁杂了。总之经过一次消息摘要后,上面的链变量将会被新的值覆盖,而最后一轮产生的链变量经过高低位互换(如:aabbccdd -> ddccbbaa 这个很关键)后就是我们计算出来的 md5 值。
MD5不管怎么加密,每一块加密得到的密文作为下一次加密的初始向量IV,这一点也很关键
找了半天,找到了一张图,我一看就明白了很多
举个栗子
let secret = "secret"
let data = "data"
let H = md5()
let signature = hash(secret || data) = 6036708eba0d11f6ef52ad44e8b74d5b
let append = "append"
那么在md5计算后就是这样子的
具体参数如下所示
"secret" = secret
"data" = data
80 00 00 ... — The 46 bytes of padding, starting with 0x80
50 00 00 00 00 00 00 00 — The bit length in little endian
具体 $SECRET
的情况下,得知了其 hash 值,以及我们有一个可控的消息。而我们得到的 hash 值正是最后一轮摘要后的经过高地位互换的链变量。我们可以想像一下,在常规的摘要之后把我们的控制的信息进行下一轮摘要,只需要知道上一轮消息产生的链变量。
那么这个应该如何实现呢?
···
为了方便理解,我们把之前的那个题目重新写一下
<?php
$SECRET="123456";
$auth = "I_L0vE_L0li";
if (isset($_COOKIE["auth"])) {
$hsh = $_COOKIE["hsh"];
if ($hsh !== md5($SECRET . $_COOKIE["auth"])) {
die("F4ck_U!");
}
} else {
setcookie("auth", $auth);
setcookie("hsh", md5($SECRET . $auth));
die("F4ck_U!");
}
die("I_aM_A_L0li_dA_Yo~");
?>
通过获取cookie中 hsh的值,我们就能得到 hsh =
自己动手写个函数,或者用手算一下,可以得到下一轮的链变量就是:
然后进行补位。因为 $SECRET 的长度是 6,我们用 6 个 a 来填补一下,紧跟着就是 auth 的值。然后我们把消息补到 448 bit。接着进行补长度。
得到如下参数:
secret_admin = "aaaaaaI_L0vE_L0li" + '\x80' + '\x00' * 38 + '\x88' + '\x00' * 7 + "whaleCTF"
然后,md5的加密中,这个东西会被填补成 512*2 bit 的东西,当然,我们需要计算的只是后面的东西,也就是"whaleCTF"+填充的东西。
转换成16进制,也就是:
之后就看以开展我们的哈希长度扩展攻击了,将这个16进制数作为第二轮中的输入,并将s1,s2,s3,s4作为初始的链变量。我们就能够得到 一个新的hsh为 57105e676f1e378fe5d6435d3a285c8a
之后就是就题目论事了。那我们这道题目而言,我们需要的是
$hsh !== md5($SECRET . $_COOKIE["auth"])
所以说
57105e676f1e378fe5d6435d3a285c8a== md5(xxxxxx+"I_L0vE_L0li" + '\x80' + '\x00' * 38 + '\x88' + '\x00' * 7 + "whaleCTF")
(注意url编码)
是成立的,所以只要在cookie中输入
得到flag
那么对于实验吧的这道题目的?其实也是一样的。源码在上面
我们获取了sample_hash为
那么,下一轮变量的IV就是
payload就是如下
secret_admin = "aaaaabbbbbcccccadminadmin" + '\x80' + '\x00' * 30 + '\xc8' + '\x00' * 7 + "admin"
对于这道题目而言,只要将username=admin
,password="admin" + '\x80' + '\x00' * 30 + '\xc8' + '\x00' * 7 + "admin"
URL编码 post提交
··· PS:实验吧这道题好像被宕了,不知道咋回事
url: http://daka.whaledu.com/web/web44/
源码
<?php
echo "已知一组role为root,salt长度为6,hash为a0566a65f9d6bfd9abf2c116ef1ca2af,想要扩展的字符串是whaleCTF"."<br>";
$flag = "**********";
$role = $_REQUEST["role"];
$hash = $_REQUEST["hash"];
$salt = "***********"; //The length is 6
if ($hash !== md5($salt.$role)){
echo 'wrong!';
exit;
}
if ( $role == 'root'){
echo 'no no no !, hash cann\'t be root';
exit;
}
//echo "You are ".$role.'</br>';
echo 'Congradulation! The flag is'.$flag;
?>
攻击过程。md5pad.py 哈希值 扩展的字符串 原始盐加上信息的长度(10)
md5-extension-attack/md5pad.py" a0566a65f9d6bfd9abf2c116ef1ca2af whaleCTF 10
Payload: '\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00whaleCTF'
Payload urlencode: %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%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00P%00%00%00%00%00%00%00whaleCTF
md5: aab9a3180e92bd4126d56011b672711f
然后post提交数据即可
本文中的部分知识也是来自该作者的博客,全英文的,耐下心子看很好理解。
https://github.com/iagox86/hash_extender
利用方法也相当得方便
root@ninthdevil:~/桌面/Cknife/hash_extender# ./hash_extender -d I_L0vE_L0li -a whaleCTF -l 6 -f md5 -s d607a0da7fd77724621092333d8fdee8 --out-data-format html
Type: md5
Secret length: 6
New signature: 57105e676f1e378fe5d6435d3a285c8a
New string: I%5fL0vE%5fL0li%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%00%00%00%00%00%00%00%00%88%00%00%00%00%00%00%00whaleCTF
个人感觉比上面那个好用,因为上面那个输入的参数有些多,实际上输入盐加原来字符的长度是不是就够了呢?
https://github.com/JoyChou93/md5-extension-attack
python md5pad.py [hash] [padding] [len(salt+message)]
> md5-extension-attack/md5pad.py" a0566a65f9d6bfd9abf2c116ef1ca2af whaleCTF 10
> Payload: '\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00whaleCTF'
> Payload urlencode: %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%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00P%00%00%00%00%00%00%00whaleCTF
> md5: aab9a3180e92bd4126d56011b672711f