ThinkPHP 3.2.3 find/select/delete注入

前言

select()、find()、delete()三个函数可以传入数组数据导致sql注入

环境

\Application\Home\Controller\IndexController.class.php

配置好数据库

分析

先给出payload

/?id[where]=1 and updatexml(1,concat(0x7e,version(),0x7e),1)

跟进 I() 函数

获取了传进去的内容

M函数就不看了,进入find函数

因为这里$options是一个数组所以这里跳过第一个if判断

这个getPk函数是查找mysql主键的函数

紧接着一个if判断:

因为这里的$pk不是一个数组,所以直接跳出

进入一个_parseOptions()函数:

跟进:

使用array_merge函数将 $options 与options合并,合并结果还是 $option 因为 $this->option 是一个空数组

获取到了表名等

这里到了重点:

这里判断 $options[where] 不是一个数组,所以没有进入if判断直接跳出

进入 _options_filter 函数,是空的

从此跳出了 _parseOptions() 函数返回了 find 函数

紧接着进入select函数:

跟进:

重点跟进 buildSelectSql 函数:

跟进parseSql函数:

重点看这个parseWhere:

这里判断为字符串直接返回:

parseSql函数返回sql语句:

进行查询:


更换payload为:

/?id=1 and updatexml(1,concat(0x7e,version(),0x7e),1)

跟进到_parseOptions这里时出现了不同:


_parseType 函数对where进行处理


跟进函数:

这里经过intval处理后,消除了注入语句

总结

关键在于find函数第一步的处理这里

_parseOptions :

如果传进去的是 $id[where] 那么 $options['where'] 为字符串不进行操作,如果传进去是 $id 那么 $options['where'] 为数组,$options['where'][id] 才是字符串,所以进行了处理

public function find($options=array()) {                  //$options = String
if(is_numeric($options) || is_string($options)) {
$where[$this->getPk()] = $options; //$where['id'] = String
$options = array();
$options['where'] = $where; //$options['where'] = {"id":String}
}

此外不仅仅 parseWhere()可以触发,与之类似拼接的 parseGroup()parseHaving()parseOrder()parseTable()也能触发,可以用id[where]id[alias]等参数触发

修复:

在_parseOptions函数处理时不传入$options,这样经过_parseOptions处理过后,$option始终为空.