赞
踩
TP5手册:https://www.kancloud.cn/manual/thinkphp5/118044
在分析 tp5 漏洞之前,先来看一看 tp5 在查询时的流程,与 tp3 有什么异同,写一个控制器
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$name = Input("name/d");
$data = db("test")->where("name",$name)->find();
dump($data);
}
}
tp5 输入输入使用 Input 函数,代替 tp3 中使用的 I 函数,Input 函数有一个功能可以使用变量修饰符对输入数据进行转换
ThinkPHP5.0版本默认的变量修饰符是/s
,如果需要传入字符串之外的变量可以使用下面的修饰符,包括:
修饰符 | 作用 |
---|---|
s | 强制转换为字符串类型 |
d | 强制转换为整型类型 |
b | 强制转换为布尔类型 |
a | 强制转换为数组类型 |
f | 强制转换为浮点类型 |
接下来看看 Sql 查询分析,tp5 使用了 pdo 预编译,基本流程如下,有效防止 SQL 注入
find 函数中有整个 pdo 查询流程
生成查询 SQL
获取参数绑定
执行查询
5.0.9 及以下版本如果开启了 debug 都存在这个漏洞,危害较低,不能子查询,只能泄露一些信息,如 user()、database() 等
控制器
class Index
{
public function index()
{
$name = Input("name/a");
$data = db("test")->where("name","in",$name)->select();
dump($data);
}
}
payload
?name[0,updatexml(0,concat(0xa,database()),0),0]
database 同时还会泄露数据库配置信息
基础的调试流程
where() -> select -> parseWhere() -> buildWhere()
这里直接进入 buildWhere()
if 都不进入,到最后
进入 parseWhereItem()直接看重点
因为 $exp 是 in,满足条件进入 if,foreach 遍历拼接 $bindKey
$bindKey = $bindName . '_in_' . $k;
$bindKey = where_name_in_0,updatexml(0,concat(0xa,database()),0),0
最终 return wherestr
$whereStr = `name` IN (:where_name_in_0,updatexml(0,concat(0xa,database()),0),0)
完整的 sql 语句
:where_id_in_
的后面,直接拼接传入的 id 的键,并没有进行过滤,可以造成 SQL 注入
主要问题是 tp5 使用了 pdo,正常来说不会出现注入,但是这里缺可以报错注入,而且正常 pdo 执行流程为三步(上边介绍了),但是这里在 prepare 截断就执行了 SQL 查询
原因如下:
PDO::ATTR_EMULATE_PREPARES => false
这个选项涉及到 PDO 的“预处理”机制:
因为不是所有数据库驱动都支持 SQL 预编译,所以 PDO 存在“模拟预处理机制”。如果说开启了模拟预处理,那么 PDO 内部会模拟参数绑定的过程,SQL语句是在最后 execute() 的时候才发送给数据库执行
如果关闭即为 false 的话,PDO不会模拟预处理,参数化绑定的整个过程都是和Mysql交互进行的
非模拟预处理的情况下,参数化绑定过程分两步:
但是,如果你将user()改成一个子查询语句,那么结果又会爆出Invalid parameter number: parameter was not defined的错误。因为没有过多研究,说一下我猜测:预编译的确是mysql服务端进行的,但是预编译的过程是不接触数据的 ,也就是说不会从表中将真实数据取出来,所以使用子查询的情况下不会触发报错;虽然预编译的过程不接触数据,但类似user()这样的数据库函数的值还是将会编译进SQL语句,所以这里执行并爆了出来
在 tp5 新版本中,改成对值得处理,而且拼接用得时$i
这个计数,最后拼接得预处理也只能是:where_id_in_1
,:where_id_in_1
这样的了
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。