(超级有用)ThinkPHP 5漏洞分析之SQL注入

本次漏洞存在于 Builder 类的 parseData 方法中。由于程序没有对数据进行很好的过滤,将数据拼接进 SQL 语句,导致 SQL注入漏洞 的产生。漏洞影响版本: 5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5 。

环境搭建

$ composer create-project --prefer-dist topthink/think=5.0.15 v5.0.15

安装后,因为默认 framework 版本为 5.0.24 ,所以将 composer.json 文件的 require 字段设置成如下:

"require": {
    "php": ">=5.4.0",
    "topthink/framework": "5.0.15"
}

然后执行 composer update 将 framework 版本更新至 5.0.15 。进入 /application/index/controller/Index.php 修改代码如下:

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $username = request()->get('username/a');
        db('users')->insert(['username' => $username]);
        return 'Update success';
    }
}

接着本地创建数据库:

create database tpdemo;
use tpdemo;
create table users(
	id int primary key auto_increment,
	username varchar(50) not null
);

进入 /application/database.php 配置本地数据库后,在 /application/config.php 中开启 app_debug 和 app_trace

// 应用调试模式
'app_debug'              => true,
// 应用Trace
'app_trace'              => true,

漏洞复现

http://127.0.0.1:8000/?username[0]=inc&username[1]=updatexml(1,concat(0x7e,user(),0x7e),1)&username[2]=1
  • 1.

e2IF3iN.png!web

漏洞分析

首先跟进第一句: $username = request()->get(‘username/a’);

yUzqIne.png!web

通过 Request::get 方法获取 GET 传入的数据,然后调用 Request::input 方法获取 $data 数据,其中通过 array_walk_recursive 对 $data 数组中的每一个元素,都调用了 Request::filterValue 方法进行 过滤处理

VN7nqaA.png!web

当检测到字段中有如下关键字时,将会在字段末尾增加一个空格:

EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN

而我们 payload 显然是不含有这些关键字的,所以这些过滤处理并没有造成影响

接着跟进第二句,也就是数据库操作语句www.meimeitu8.com: db(‘users’)->insert([‘username’ => $username]);

eemQbiZ.png!web

首先通过 helper.php 调用 Db::connect 方法。该方法中调用 self::prarseConfig 获取数据库配置信息参数 $options 后,接着实例化 \think\db\connector\Mysql 类并返回该类的实例化对象 self::$instance[$name]

接着 helper.php 中调用 Mysql::name($name) 方法,传入的参数 $name 为表名 users

AFVzE33.png!web

因为 Mysql 类中没有 name 方法,所以调用了其父类 \think\db\Connection 类的 __call 魔术方法, __call 方法通过 getQuery 方法返回 \think\db\Query 类对象,再通过 call_user_func_array 调用 Query::name 方法。 Query::name 方法返回一个 Query 类对象

以上是 db(‘users’) 部分处理,即初始化数据库配置信息www.rsxedu.com。返回 \think\db\Query 对象。接着执行 db(‘users’)->insert([‘username’ => $username]); 。即调用 Query::insert 方法

Vbqmumy.png!web

Query::insert方法中,通过 parseExpress 方法获取配置信息后,再调用 $this->builder->insert 方法来生成 $sql 语句,此处 $this->builder 为 \think\db\builder\Mysql 类对象。最后再调用 execute 来执行生成的sql语句

所以我们最后跟进 $this->builder->insert 方法,来看看是如何生成sql语句的

22EZbu3.png!web

因为 Mysql 类继承于 Builder 类,所以最终是调用 Builder::insert 方法。该方法首先调用 parseData 方法来处理我们传入的数据,进入 switch 语句的判断,当 $val[0] 为 inc 时,调用 parseKey 方法对 $val[1] 参数,即我们的 注入payload 的存储位置进行处理,而跟进 parseKey 方法。会发现该方法只是用来解析数据,并没有对数据进行 过滤检测处理 。所以直接返回了原本的 $val[1] 然后拼接上 ‘+’ . floatval($val[2]) 。组成了参数 $result 返回给 insert 方法中的 $data 。之后就是生成 insert 语句: INSERT INTO `users` (`username`) VALUES (updatexml(1,concat(0x7e,user(),0x7e),1)+0) 。最后执行触发 报错注入 。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值