ThinkPHP5.0.9_SQL注入漏洞分析
- ThinkPHP5.0.9
- 漏洞代码
<?php
namespace app\index\controller;
class Index{
public function index(){
$id = input('id/a');
$data = db('users')->where('id','in',$id)->select();
dump($data);
}
}
- POC
/index.php?id[0,updatexml(0,concat(0x7e,user(),0x7e),0)]=1
漏洞分析
- input方法用于接收参数,然后where方法解析where条件并赋值到options数组,拿
?id=1 来分析,在经过where处理后,$this->options 变量值如下 - 跟进到select方法,同样的这里直接看到生成SQL语句的地方
- 跟进
Builder::select 方法,又是熟悉的替换,这里主要看parseWhere方法 - 跟进parseWhere方法,
$whereStr 变量是通过buildWhere方法生成的 - 跟进buildWhere方法,通过getFieldsBind方法获取表里面的所有字段,而
$where 变量值为如下 - 经过foreach之后,
$value 为一个数组,$field 为id,所以前面的那些if条件都不满足,进入else分支 - 进入else分支时,
$key 为AND,然后又调用parseWhereItem方法,跟进parseWhereItem方法,前面是分析过很多次的根据$val 是否为数组对$exp 和$value 进行赋值操作 - 然后后面对
$bindName 赋值,为where_id,当$value 非标量时,即我们传入的GET值为数组时不会进入下图的第二个if分支 - 接下来又是一系列的条件判断,最后进入的是如下分支块,
$binds 其实就是表里面的所有字段,经过foreach遍历来拼接$bindKey 变量,这里还通过了Query::isBind 进行了判断 - 跟进一下isBind方法,返回应该是false了
- 回到语句块,bindName经过拼接后为
where_id_in_0 ,那么$array[0] 就为:where_id_in_0 ,最后经过拼接$whereStr 值为id in (:where_id_in_0) ,在这一系列的拼接中,我们可以控制的是$k 变量的值,也就是GET传参的键。 - 既然传上去的id数组的键是直接拼接在SQL语句上的,这里就可以进行注入。后面就一样了,也是在
Connection::query 方法中进行预处理、参数绑定和执行SQL - 最后,说一下遗留的预处理prepare()方法然后直接到抛出异常过程的问题,这个我在parseData中有分析过,我们注入的时候是从预处理然后直接就抛出错误了,然后的执行查询execute()方法都没有执行,为什么还是会有报错呢?用网上的例子如下
<?php
$params = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
];
$db = new PDO('mysql:dbname=security;host=127.0.0.1;', 'root', 'root', $params);
try {
$link = $db->prepare('SELECT * FROM users WHERE id in (:where_id, updatexml(0,concat(0x7e,user(),0x7e),0))');
} catch (\PDOException $e) {
var_dump($e);
}
- 没有执行execute方法,抛出错误,但是也爆出错误了,原因是因为当
PDO::ATTR_EMULATE_PREPARES 设置为false时,PDO不会进行模拟预处理,参数绑定的过程是和Mysql交互进行的(当是true时,会先在PDO内部进行模拟参数绑定过程,最后再在execute的时候才发送到数据库执行)。在非模拟预处理的情况下,参数化绑定分两步
- 第一步是prepare阶段发送带有占位符的SQL语句到mysql服务器
- 第二步是多次发送占位符参数到mysql服务器进行执行
- 那么当在第一步prepare阶段时的时候SQL语句就出错的话,mysql就会直接抛出异常,不再执行第二步。预编译时SQL语句错误产生的报错只能报错出user(),database()这些数据。
写在后面
- 今天已经是21年的11月16号了,今天分析了parseArrayData、parseWhereItem和现在分析的这条链,学的东西很少,也有些慢,自从大二之后就懈怠了也不自知,好在现在明白过来,好好学!
- 总的来说吧,TP5的前面几条链都是构造数组从而不进入预编译语句块(当然这条链用了预编译,但是预编译的内容我们可控!),然后寻找其它拼接到SQL语句的地方并拼接的变量可控!因为几条链都类似,所以也审的比较快。下面就开始审TP6 的链了,审完再去把ctfshow的TP专题刷了。
|