Fight For Freedom

09月 28

浅谈php的一处小bug

      闲来无事,在乌云zone上偶然看到了php challenage 2015,感觉比较有意思,就拿出来和大家分享分享。其中参考了官方的资料并结合自己的理解,若有错误,欢迎讨论。

      题目如下:

    

<?php 

/******************************************************************* 
* PHP Challenge 2015 
******************************************************************* 
* Why leave all the fun to the XSS crowd? 
* 
* Do you know PHP? 
* And are you up to date with all its latest peculiarities? 
* 
* Are you sure? 
* 
* If you believe you do then solve this challenge and create an 
* input that will make the following code believe you are the ADMIN. 
* Becoming any other user is not good enough, but a first step. 
* 
* Attention this code is installed on a Mac OS X 10.9 system 
* that is running PHP 5.4.30 !!! 
* 
* TIPS: OS X is mentioned because OS X never runs latest PHP 
*       Challenge will not work with latest PHP 
*       Also challenge will only work on 64bit systems 
*       To solve challenge you need to combine what a normal 
*       attacker would do when he sees this code with knowledge 
*       about latest known PHP quirks 
*       And you cannot bruteforce the admin password directly. 
*       To give you an idea - first half is: 
*          orewgfpeowöfgphewoöfeiuwgöpuerhjwfiuvuger 
* 
* If you know the answer please submit it to info@sektioneins.de 
********************************************************************/ 

$users = array( 
        "0:9b5c3d2b64b8f74e56edec71462bd97a" , 
        "1:4eb5fb1501102508a86971773849d266", 
        "2:facabd94d57fc9f1e655ef9ce891e86e", 
        "3:ce3924f011fe323df3a6a95222b0c909", 
        "4:7f6618422e6a7ca2e939bd83abde402c", 
        "5:06e2b745f3124f7d670f78eabaa94809", 
        "6:8e39a6e40900bb0824a8e150c0d0d59f", 
        "7:d035e1a80bbb377ce1edce42728849f2", 
        "8:0927d64a71a9d0078c274fc5f4f10821", 
        "9:e2e23d64a642ee82c7a270c6c76df142", 
        "10:70298593dd7ada576aff61b6750b9118" 
); 

$valid_user = false; 

$input = $_COOKIE['user']; 

$input[1] = md5($input[1]); 

foreach ($users as $user) 
{ 
        $user = explode(":", $user); 
        echo 'user = '.$user; 
        if ($input === $user) { 
                $uid = $input[0] + 0; 
                $valid_user = true; 
        } 
} 

if (!$valid_user) { 
        die("not a valid user\n"); 
} 

if ($uid == 0) { 

        echo "Hello Admin How can I serve you today?\n"; 
        echo "SECRETS ....\n"; 

} else { 
        echo "Welcome back user\n"; 
} 

?>

    题目给的很简单,就是服务端将cookie信息取出用来验证用户。而我们要做的就是来绕过判断,拿到secret。

    我觉得有两个难点:

    1. 数组比较的绕过,从题意上来看,很明显不是用彩虹表去破解md5;

    2. uid的绕过。uid要为0,那么就必须把相应的md5值破解出来。

    经过测试,只有uid为5的md5能破解出来。md5('hund') = 06e2b745f3124f7d670f78eabaa94809

    所以我们需要经过什么操作才能使 $input === $user(注意这是"===",值和类型相同),还能使uid=0。

 

    就在前不久php官方修复了一bug,但并未意识到这是安全问题。由于键值整数会越界的问题,不同的数组在比较时可能会相等。

var_dump([0 => 0] === [0x100000000 => 0]); // bool(true)
 
 漏洞分析如下:
ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered TSRMLS_DC)
{
        Bucket *p1, *p2 = NULL;
        int result;
        void *pData2;

        ...

        while (p1) {
                if (ordered && !p2) {
                        ...
                }
                if (ordered) {
                        if (p1->nKeyLength==0 && p2->nKeyLength==0) { /* numeric indices */
                                result = p1->h - p2->h;                <------------ POSSIBLE TRUNCATION
                                if (result!=0) {
                                        HASH_UNPROTECT_RECURSION(ht1);
                                        HASH_UNPROTECT_RECURSION(ht2);
                                        return result;
                                }


         The problem in the code above is that numerical indices got compared by subtracting their values from each other, which are stored in the h element of the bucket data type. A difference is then detected if the result is 0 or not. Unfortunately the h element of the structure bucket is defined as unsigned long, which is usually 64bit on 64bit systems, but the result variable is only a 32bit int data type. Therefore the comparison is not only true if the values of h are identical but every time the result of the subtraction has all zero lower 32 bits. Therefore the key 0 and the key 4294967296 and many other keys are considered identical


       大概意思就是说h的数据类型是unsigned long,在64位机器上是64位的,而result变量是32位int类型的,那么比较时如果h的相等,相减得到的result为0。所以键值0和4294967296(2^32)以及其它的等都会被认为是相等的。即在php在64bit的环境下会成功,数组内的键值会被当做int来处理,而在32位的php上跑这个是不成功的。(注:可根据PHP_ini_size来判断,若为8,即为64位;若为4,即为32位)。


       我们构造 Cookie: user[4294967296]=5;user[1]=hund; 即可绕过判断。"==="数组的比较,input[4294967296] === input[0] = 5,与之相对的构造好的md5值input[1]与user[5]也相等;但由于input[0]并没有声明,所以$uid=$input[0]+0时,$uid就等于0,那么随之也绕过了if ($uid == 0)。

       其实php官方发布补丁时并不承认这是一个安全问题,只认为它是一个小bug。但是我们可以看到,任何一处小bug都可能造成严重的安全问题。

 

参考资料:https://www.sektioneins.de/blog/15-08-03-php_challenge_2015_solution.html

标签:none

还不快抢沙发

添加新评论

captcha
请输入验证码

最新文章

最近回复

  • lynahex:...Orz
  • chybeta:师傅好!
  • lynahex:拜大佬
  • cdxy:师傅好
  • lynahex:十分感谢解惑,特别是解决问题的方法。
  • ld:1. 搜索CVE-2016-0701 进入 https://cv...
  • lynahex:好的。
  • xdxd:友链已加~~~希望有机会多多交流~~
  • lynahex:恩,重测了下是可以的。可能当时一些危险函数被我禁掉了。 thx
  • 过客:虽然博主文章过了好久了,本地system测试还是可以执行的
  • 友情链接

    分类

    其它