ThinkPHP5.1.17_parseArrayDate函数导致的SQL注入漏洞分析
- ThinkPHP5.1.16-ThinkPHP5.1.17(非最新版的5.1.18版本也可利用
- 漏洞代码
<?php
namespace app\index\controller;
class Index{
public function index(){
$username = request()->get('username/a');
db('users')->where(['id'=>10])->update(['username' => $username]);
}
}
- POC
?username[0]=point&username[1]=0x7e,database(),0x7e),'1&username[2]=updatexml&username[3]=1',concat
漏洞分析
- 这里先把漏洞代码的强制转换为数组去掉,进行分析,get方法就不分析了,直接分析where方法,where方法先获取where方法里的参数,然后进行删除,这里不用管,没啥用,然后调用parseWhereExp方法返回
- parseWhereExp方法就是解析where方法里面参数的,我们不在where里面构造SQL语句,直接过,跟进到
Query::update
方法,这里把我们传入的GET参数和options['data']
合并然后又赋值给options['data']
,然后再调用Connection::update
,而parseOptions方法前面也看过了,就是对options进行操作的,无大影响。 - 跟进到
Connection::update
方法,前面的很多代码也是没用的,直接跟进到生成SQL语句的地方,也就是Builder::update
, - 跟进
Builder::update
方法,可以看到这里也是熟悉的parseData方法然后再进行替换 - 跟进parseData方法,和parseData造成的注入一样,如果没用强制转换为数组,则会进行预编译
- 然后回到
Connection::update
方法,执行execute方法进行执行SQL语句 - 跟进execute方法,这里跟之前的那条链一样,都是预处理然后执行SQL语句,这样就无法注入了
- 回到parseData方法,之前那条链是通过让
$val[0]
为INC或者DEC来控制SQK语句,但是现在就算是INC或者EXP也无法控制SQL语句,但是多了一个default分支,里面调用了parseArrayData方法 - 跟进parseArrayData方法,只要
$data[0]
为point即可进入point分支,然后进行一系列赋值和拼接, - 这个
$result
变量的结构很像updatexml函数报错的格式,此时SQL语句是直接拼接的,且拼接的内容可控,导致了SQL注入漏洞的产生。
写在后面
- 这条链其实跟parseData造成的注入链差不多,都是绕过预编译,这个版本中把parseData造成注入的地方修复了,但是由于增加了parseArrayData漏洞方法,所以导致注入再次产生。
- 注意的是,我在用composer和在Github上下载的下来的TP都没有default分支和
Mysql::parseArrayData
方法,所以这个要自己在Mysql这个类和Connection这个类加上漏洞代码