xyhcms1.5代码审计
前言
这是我第一次尝试审计cms,这次成功发现了位于xyhai.php页面后台的一个注入漏洞,还顺带找了个验证码绕过。
环境
php 5.4.45(php版本可以任意)
Nginx 1.14.0
Mysql 5.7.19
xyhcms 1.5 2014年
框架ThinkPHP 3.1.3
漏洞分析
验证码绕过
先从简单的开始
1 | $verify = I('code','','md5'); |
很典型的验证码没有验证空值。
对每个请求来说,它们使用Cookie中的PHPSESSID来标识session,如果请求中的PHPSESSID和后端对不上,那么后端就会重置session,导致$_SESSION中的变量的值不存在。
而这里又用到了php的弱类型,构造null==’’的情况即可绕过验证码限制。
xyhai.php页面登录绕过
thinkphp本身是有不少问题的,尤其是涉及到数组处理的情况,在parseWhereItem函数,传入数组元素且第一个元素值是exp,
那么parseWhereItem就会拼接key和数组的第二个元素。也就是说只要调用了parseWhereItem的函数就可能出现问题。
1 | if(is_array($val)){ |
在thinkphp框架中的include/Lib/Core包中
parseWhere函数调用parseWhereItem,而又有update,delete,parseSql,还有parseThinkWhere函数调用了parseWhere。
而delete和update调用parseWhere函数的情况都比较特殊,
都需要设置$options中的where,也就是需要查询的where部分可控,然而这点在这个cms中,尤其是前台业务中是比较难做到的。因为大部分update和delete的查询参数都是直接从加密后的cookie中获取的。
1 |
|
我们把眼光转向parseSql,他的上家是buildSelectSql,而调用buildSelectSql有一个函数是select,继续跟踪发现,model.class中的find函数也会调用它。
而这个函数中的options类的保护属性,也就是说可以通过M->where(array($options))->find()/或者where()->find($options)的这种形式传入,也就是说如果我们能找到一处能传入where或者find参数的位置就可以注入了。
最后我们在后台登录处找了。核心代码如下
1 |
|
那么我们构造语句
1 | username[]=exp&username[]=%3d1%20or%20sleep(10)&password=1 |
延时10s说明执行成功,执行的语句是
1 | SELECT * FROM `xyh_admin` WHERE ( (`username` =1 or sleep(10)) ) LIMIT 1 |
观察登录逻辑,我们发现验证密码时,会将传入的密码加密后的结果和数据库中的密码比较,而这里的encrypt,password都是查询结果,而我们输入的password又是可控的,也就是说,可以进行登录绕过。
1 | http://www.xyhcms1_5.com/index.php?g=Manage&m=Login&a=login |
执行的sql语句
1 | SELECT * FROM `xyh_admin` WHERE ((`username` ='1')) union select 1,1,'15ab8357abeb6eacf1b591a1b5b1aedd',1,1,1,1,1,1,0#) ) LIMIT 1 |
我们回顾一下整个调用链参数从I()函数输入,经过where()到
model class中的find,再到select最后经过Db中的buildSelectSql,再到parseSql,然后达到parseWhereItem。
不过我写文章的时候好像看Db中的insert函数也有和parseWhereItem一样的问题。
1 | public function insert($data,$options=array(),$replace=false) { |
而这里的execute函数是提供显示错误信息的,可以使用报错注入回带处想要的数据应该说也相当有用。
get_client_ip的伪造
这里和那边的验证码是一个道理,通过伪造随机session和x-forwarded-for值来构造不同的ip。
1 | function get_client_ip($type = 0) { |
总结
这一次代码审计总共加起来看了快两天的样子,而且还没有拿到shell,深刻得认识到了自己的菜。不过还有收货了不少tricks。
- tp3 和 tp5一样都在sql语句处理数组上存在问题,碰到类似的就可以全局搜索parseWhereItem或者类型的函数。
- 网上的poc看不懂的时候直接拿在cms中找个页面写写查询试试看,跟踪跟踪函数调用,很快就会有自己的发现的。
- phpStorm的findusage很有用,但跟踪不了有些类中的函数,需要靠自己发现,注入这块主要的问题集中在Db.class里,这块还可以好好看看。
- 后台尝试过改页面,找上传漏洞等方法,不过好像没有用,这块以后需要多总结经验。