ThinkPHP 5.0.x 版本远程代码执行漏洞分析

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

内容简介:1月11日,公告:补丁:

0x01 概述

1月11日, ThinkPHP 官方发布新版本5.0.24,修复了一个安全问题,该问题可能导致 GetShell ,这是 ThinkPHP 近期的第二个高危漏洞,两个漏洞均是无需登录即可远程触发,危害极大。

公告: https://blog.thinkphp.cn/910675

补丁: https://github.com/top-think/framework/commit/4a4b5e64fa4c46f851b4004005bff5f3196de003

0x02 影响版本

5.0.x ~ 5.0.23

0x03 环境搭建

选择 5.0.22 版本进行复现分析

0x04 漏洞分析

我们知道可以通过 http://127.0.0.1/public/index.php?s=captcha 的方式通过 s 参数传递具体的路由,具体调用如下

index.php

require __DIR__ . '/../thinkphp/start.php';

start.php

App::run()->send();

跟进 run() 方法

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

可以看到在进入 self::exec($dispatch, $config) 前, $dispatch 的值是通过 $dispatch = self::routeCheck($request, $config) 设置的,先进入 exec() 方法看一下:

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

exec() 方法根据 $dispatch 的值选择进入不同的分支,当进入 method 分支时,调用 Request::instance()->param() 方法,跟进 param() ,看到调用了 Request 类的 method() 方法 :

$method = $this->method(true);

其中 method() 方法就是补丁修改的位置,在这个方法中,如果 method 等于 true ,则调用 $this->server() 方法::

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

server() 方法中调用 $this->input 方法:

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

接着调用了 filterValue() 方法:

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

filterValue() 则调用了 call_user_func() 方法,如果两个参数均可控,则会造成命令执行:

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

回头看一下 $filter$value 参数从哪里来:

  • $filter
$filter = $this->getFilter($filter, $default);

getFilter() 中设置了 $filter 值:

$filter = $filter ?: $this->filter;

也即由 $this->filter 决定

  • $value

$value 为第一个参数 $data ,即为传入数组的值,由 $this->server 决定

所以最终的问题就是如何从请求中传入 $this->filter$this->server 这两个值,构造 call_user_func() 的参数触发漏洞。

回到最开始的 run() 方法,其中:

$dispatch = self::routeCheck($request, $config);

$dispatch 的值通过 routeCheck() 方法设置,根据routeCheck()方法:

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

调用了 check() 方法:

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

check() 方法中根据不同的 $rules 值返回不同的结果,而 $rules 的值由 $method 决定, $method 则由 $request->method() 返回值取小写获得,所以再次回到 $request->method() 方法,这次没有参数

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

如果 $method 不等于 true ,则会取配置选项 var_method ,该值为 _method

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

然后调用 $this->{$this->method}($_POST); 语句,此时假设我们控制了 $method 的值,也就意味着可以调用 Request 类的任意方法,而当调用构造方法 __construct() 时,就可以覆盖 Request 类的任意成员变量,也就是上面分析的 $this->filter$this->server 两个值,同时也可以覆盖 $this->method ,直接指定了 check() 方法中的 $method 值。

0x05 构造PoC

首先要主动触发 Request 类的构造函数,通过参数 _method=__construct 传入,进入到 __construct 方法,该方法把参数遍历并设置值:

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

所以我们可以传入 filter=system 来设置 $this->filter 的值

此处 filter 不是数组也可以,因为在 getFilter() 中虽然对 filter 是字符串的情况进行了按 , 分割,但是传入一个值的情况下不影响最终的返回值

再看 $this->server ,在调用 $this->server('REQUEST_METHOD') 时指定了键值,所以通过传入 server 数组即可

server[REQUEST_METHOD]=id

然后我们注意到上面 check() 方法,

$rules = isset(self::$rules[$method]) ? self::$rules[$method] : [];

它的返回值由 $rules 决定,而 $rules 的值取决于键值 $method ,当我们指定 $methodget 时,可以正确获取到路由信息,从而通过 checkRoute() 检查,此时我们通过指定 method=get 覆盖 $this->method 的值即可

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

最终的 PoC

_method=__construct&filter=system&method=get&server[REQUEST_METHOD]=id

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

调用栈如下图所示

ThinkPHP 5.0.x 版本远程代码执行漏洞分析

0x06 总结

这个漏洞本质上是一个覆盖漏洞,通过 _method 覆盖了配置文件的 _method ,导致可以访问 Request 类的任意函数,而在 Request 的构造函数中又创建了恶意的成员变量,导致后面的命令执行,整个漏洞利用可以说是非常巧妙了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

Visual C++网络通信协议分析与应用实现

Visual C++网络通信协议分析与应用实现

汪晓平、钟军 / 人民邮电出版社 / 2003-2-1 / 60.00元

本书介绍了如何利用Visual一起来看看 《Visual C++网络通信协议分析与应用实现》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试