本文作者:W(信安之路新成员:投稿文章首次入选发布即为信安之路新成员)
官网地址:
http://dbshop.net/
审计版本:v1.3 20190215
(目前 dbshop 的最新版本)
从官网介绍得知,dbshop 是一个基于 ZendFramework 2
开发的。很是蛋疼,这个框架,没用过,草草的找了 本文档边看边审计:
https://www.kancloud.cn/thinkphp/zendframework2-quickstart/35940
可以看到,这个 cms 应该是用了自定义路由,其路由文件在模板目录下的 config 文件
其路由规则为:
http://localhost/list
访问 Shopfront/
下的 Goodslist 里面的 index
http://localhost/list/ajaxGoodsGroupPrice/
访问 Shopfront/
下的 Goodslist 里面的 ajaxGoodsGroupPrice
前台能访问的目录有 Shopfront
、Mobile
其他都是导入了 admin 相关类库,要登入后台才能访问,而我觉得后台注入就有点鸡肋了。暂时先放一放。
先看看 shopfront,作为审计菜鸡,我的审计方法就是一个一个方法去看。比如,寻找 sql 注入,找哪个地方可控参数,拼接 sql,再追踪函数,看看能不能构造 exp。
ZendFramework2 中的常见获取方法有 getQuery 和 getPost
所以全局搜索 $this->request->getPost
和 $this->request->getQuery
看看哪处可控,
我只关注前台,所以我只是关注 Shopfront
和 Mobile
这两个下的
很多都是用 int 处理,没有 int,则是一些步骤,不进入数据库处理,继续往下看吧
呼,终于找到一处。
在下面文件中的 ajaxGoodsGroupPrice 函数如图:
Shopfront\src\Shopfront\Controller\GoodslistController.php
这里,可以知道 goodsIdStr
是通过 post 传入,userGroupId
则是获取用户登入信息,不过没什么关系,只是普通会员,而不是管理员登入。随便注册一个就好了。判断完之后,则将 where 拼接,传入到 listGoodsUsergroupPrice,跟进到 listGoodsUsergroupPrice:
这里我用了 xedbug+phpstorm
来一步一步跟进,而且为了分析方便,我把 getPost
改成了 getQuery
,同时也删掉了用户信息判断
在这里下个断点,单步进行 where
:
调用了 addPredicates
,继续下一步,看看 addPredicates
这里可以看到,判断传入的 predicates
,如果是数组,则遍历出来。如果数组的值为字符串,就用预处理来处理 sql,否则直接传入:
这里我们思路缕一缕。这处注入,问题大概就是产生这里了,传入了数组,但是数组键值没赋值,PHP 则会默认赋值为 0 ,我们来看一看代码
array($where)
键值为 0,is_string(0)
则为 false
:
所以思路回到 addPredicates
,这里就绕过了预处理。
让我们来再次验证一下思路是否正确。因为原代码写的是 array($where)
, 键值是 0,is_string
判断为 false
,所以跳过预处理。所以我修改代码,变成这样子
尝试一下 输入 1'
, 访问:
http://127.0.0.1/safe/DBShop_1.3_Release_20190215/DBShop/list/ajaxGoodsGroupPrice?goodsIdStr=1'
已经执行到了预处理模式
看看数据库最后得到的语句是
大致思路是正确的,那么现在来构造 poc。
这里需要注意,这处是需要会员登入的,注册一个普通的用户登入就可以了。
Post:
{"goodsIdStr":"1)and updatexml(1,concat(0x5e,version()),1)-- -"}
可以看到数据库已经执行了我们的代码,但是网站没有返回我们需要的东西,我觉得应该是用了指定的错误页面,只能采用盲注了
再来 post:让它不执行报错
{"goodsIdStr":"1)and (select case when (substring((select version()) from 1 for 1)=6) then (exp(800)) else 0 end)-- -"}
显示正常,没有报错。再让它执行报错
post:
{"goodsIdStr":"1)and (select case when (substring((select version()) from 1 for 1)=5) then (exp(800)) else 0 end)-- -"}
所以可以根据这个写个 poc,来进行注入
Poc:
漏洞验证
官网地址:
http://demo.dbshop.net/home
密码:1984113052
账户:1984113052
这是我的第三次代码审计,本想着全局代码都看一次,从底层看起,但是实在有些代码生涩难懂,加上没有开发经验,只能抱着本入门文档猜测想法。从这个漏洞,起因是开发者为了用自己的拼接 sql,放弃了使用预处理模式来处理 sql,而恰好没有做过滤处理而导致的。