MD5…

Hash_Length_Extension_Attack(哈希长度扩展攻击)


0x01

提到Hash扩展攻击,首先出现在脑海中的是md5的扩展攻击.

What’s md5?

从图上可以知道,md5算法的流程:

  1. 先将原文消息进行填充
  2. 填充首位为1,其余为0的数据.满足(L*512)%448=0(单位是bit)
  3. 最后的56bit填充原文消息长
  4. 分为L个512bit块
  5. 传入一个初始变量和第一块进行64次加密得到新的变量,继续将变量和下一块进行加密…依次往下
  6. 最后得到的变量就是hash
Why Can Attack?

出现可攻击的原因就是因为在进行块加密的时候得到的变量被用作下一次块加密的变量

试想如果有最后一次的hash,你可以继续往下计算的话,就能得到新的hash.
你就能人为计算出下次的hash与服务器计算的hash是完全一样的.
不需要知道加密明文的内容是什么,就能预测hash内容.
这样就达到了攻击效果.

How?

必要条件:

  1. 明文长度
  2. 明文加密后的md5值

eg:

1
2
3
md5(x+y) √
length(x+y) √
md5[(x+y)+Padding(x+y)+z] √

由于x+y+Padding(x+y)的长度刚好是512的倍数,所以只需要得到md5(x+y)就继续计算.


0x02

Eg1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$secret = 'hash';
if (empty($_GET['a'])) {
$a = 'x';
}
else {
$a = $_GET['a'];
}
$str1 = md5($secret.$a);
if (!empty($_GET['key'])) {
if($_GET['key'] == $str1)
{
echo 'attack_success.';
}
else{
echo 'Come on...';
}
}
else{
echo 'You Fade...'.$str1;
}
?>

这里的人为计算使用iagox86的攻击脚本,还有参考文档,在这里可以利用$a可控达到攻击的效果.

这里$ax+Padding(x+$secret)+append(人添加的数据)赋值,得到的hash和服务器的hash一样.

Eg2:

PHPwindV9.0版本中hash扩展攻击

这里使用的是V9.0.1
漏洞出现在/src/windid/service/base/WindidUtility.php的appkey函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static function appKey($apiId, $time, $secretkey, $get, $post) {
// 注意这里需要加上__data,因为下面的buildRequest()里加了。
$array = array('windidkey', 'clientid', 'time', '_json', 'jcallback', 'csrf_token',
'Filename', 'Upload', 'token', '__data');
$str = '';
ksort($get);
ksort($post);
foreach ($get AS $k=>$v) {
if (in_array($k, $array)) continue;
$str .=$k.$v;
}
foreach ($post AS $k=>$v) {
if (in_array($k, $array)) continue;
$str .=$k.$v;
}
//echo $time.$str;
return md5(md5($apiId.'||'.$secretkey).$time.$str);
}

看到这里的md5(md5($apiId.'||'.$secretkey).$time.$str),由于$str是可控的,符合了hash扩展攻击的形式.
接下来寻找调用过appkey且可查看的地方,在/src/windid/service/WindidUtility.php中showFlash函数

1
2
3
4
$key = WindidUtility::appKey($appId, $time, $appKey, array('uid'=>$uid, 'type'=>'flash', 'm'=>'api', 'a'=>'doAvatar', 'c'=>'avatar'), array('uid'=>'undefined'));
$key2 = WindidUtility::appKey($appId, $time, $appKey, array('uid'=>$uid, 'type'=>'normal', 'm'=>'api', 'a'=>'doAvatar', 'c'=>'avatar'), array());

$postUrl = "postAction=ra_postAction&redirectURL=/&requestURL=" . urlencode(Wekit::app('windid')->url->base . "/index.php?m=api&c=avatar&a=doAvatar&uid=" . $uid . '&windidkey=' . $key . '&time=' . $time . '&clientid=' . $appId . '&type=flash') . '&avatar=' . urlencode($this->getAvatar($uid, 'big') . '?r=' . rand(1,99999));

这个函数在修改头像处被调用.

这里得到的md5的明文是有md5(...)+10位时间戳+adoAvatarcavatarmapitypeflashuid2uidundefined,后面的一串是$key在形成时传递了array('uid'=>$uid, 'type'=>'flash', 'm'=>'api', 'a'=>'doAvatar', 'c'=>'avatar'), array('uid'=>'undefined')经过组合而成的,其中的2是uid,所以长度是87.

条件: 1. md5 √ 2. 明文长度 √

接下来是利用这个appkey来绕过验证进行攻击,在/src/application/windidserver/api/controller/OpenBaseController.php的beforeAction函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  function beforeAction($handlerAdapter) {
parent::beforeAction($handlerAdapter);
$charset = 'utf-8';
$_windidkey = $this->getInput('windidkey', 'get');
$_time = (int)$this->getInput('time', 'get');
$_clientid = (int)$this->getInput('clientid', 'get');
if (!$_time || !$_clientid) $this->output(WindidError::FAIL);
$clent = $this->_getAppDs()->getApp($_clientid);
if (!$clent) $this->output(WindidError::FAIL);
if (WindidUtility::appKey($clent['id'], $_time, $clent['secretkey'], $this->getRequest()->getGet(null), $this->getRequest()->getPost()) != $_windidkey) $this->output(WindidError::FAIL);
$time = Pw::getTime();
if ($time - $_time > 1200) $this->output(WindidError::TIMEOUT);
$this->appid = $_clientid;
}

这里有appkey$_wididkey匹配,绕过这个就行了.(这里的time参数会过期)

1
md5(md5()+time+adoAvatarcavatarmapitypeflashuid2uidundefined+$get+$post) == $_windidkey

其中的$get和$post换成Padding+可控的内容,一旦匹配成功就可以调用相应的方法进行攻击.

还可以调用其它方法达到修改密码等操作…(注意$get中添加的其它的参数也需要算入append中)


0x03

这里只解释了md5的扩展攻击,有兴趣的话可以去研究下其它的扩展攻击.

2017-06-29 23:50:30 星期四
(ง •_•)ง