前段时间打比赛的时候遇到了Think PHP3.2.x RCE的题目,这个洞是上个月爆出来的:【漏洞通报】ThinkPHP3.2.x RCE漏洞通报。虽然3已经不是主流版本了,但是考虑到还是有不少老的站在用,所以还是有一定的价值的,这里分析一下。
利用
这里用的是Think PHP3.2.5完整版,具体每个版本之间的区别可以看看参考链接
利用的条件是assign方法的第一个变量可控。因此我们可以在indexController写入demo:
|
|
payload:?m=Home&c=Index&a=index&value[_filename]=.\Application\Runtime\Logs\Home\21_06_30.log
本质上是一个文件包含,再结合报错写log最终达到rce的目的
分析
这里我们先不包含log,而是包含config.ini来分析文件包含,payload:
|
|
首先我们的可控参数通过Controller.class.php
中的assign
函数最终跳到了View.class.php
中的assign
函数:
在该函数中将其赋值给$this->tVar
变量:
赋值结束后回到IndexController
再进入到display
函数,和上面的assign
函数一样最终跳到View.class.php
的display
函数中,该函数中的fetch
函数解析并获取模板文件内容,此时模板文件路径和内容为空:
继续跟进fetch
函数,该函数中将空的templateFile
传入parseTemplate
函数:
当参数为空时,parseTemplate
函数返回默认的模板文件位置./Application/Home/View/Index/index.html
接着往下走,系统配置的默认模板引擎为think,所以程序进入else分支
将this->tVar
放入params
变量中跳入Hook::listen
,tag
变量是写死的view_parse
,因此name
变量为Behavior\ParseTemplateBehavior
,接着进入self::exec
在exec
函数中调用Behavior\ParseTemplateBehavior
的run
方法处理带有我们可控参数的$params
变量:
在run方法中经过一些判断,最终调用fetch
函数编译并加载模板文件,此时第二个参数就是我们的可控参数:array("_filename"=>“C://config.ini”);
继续跟进fetch
,获取缓存文件路径后,进入Storage的load方法中:
在load方法中,$_filename
为之前获取的缓存文件路径,$var
则为之前带有_filename=C://config.ini
的数组,利用extract方法进行变量覆盖,实现任意文件包含:
众所周知,include函数不在意包含的什么文件:
因此我们可以构造一个不存在的模块,使得tp报错,此时tp会将url写入log中,所以我们可以在url中写入恶意代码,再结合上面的LFI实现任意代码执行。
=======
title: “TP3.2.xRCE” date: 2021-08-14T16:19:19+08:00 categories:
- 漏洞复现 tags:
- TP
- web
前段时间打比赛的时候遇到了Think PHP3.2.x RCE的题目,这个洞是上个月爆出来的:【漏洞通报】ThinkPHP3.2.x RCE漏洞通报。虽然3已经不是主流版本了,但是考虑到还是有不少老的站在用,所以还是有一定的价值的,这里分析一下。
利用
这里用的是Think PHP3.2.5完整版,具体每个版本之间的区别可以看看参考链接
利用的条件是assign方法的第一个变量可控。因此我们可以在indexController写入demo:
|
|
payload:?m=Home&c=Index&a=index&value[_filename]=.\Application\Runtime\Logs\Home\21_06_30.log
本质上是一个文件包含,再结合报错写log最终达到rce的目的
分析
这里我们先不包含log,而是包含config.ini来分析文件包含,payload:
|
|
首先我们的可控参数通过Controller.class.php
中的assign
函数最终跳到了View.class.php
中的assign
函数:
在该函数中将其赋值给$this->tVar
变量:
赋值结束后回到IndexController
再进入到display
函数,和上面的assign
函数一样最终跳到View.class.php
的display
函数中,该函数中的fetch
函数解析并获取模板文件内容,此时模板文件路径和内容为空:
继续跟进fetch
函数,该函数中将空的templateFile
传入parseTemplate
函数:
当参数为空时,parseTemplate
函数返回默认的模板文件位置./Application/Home/View/Index/index.html
接着往下走,系统配置的默认模板引擎为think,所以程序进入else分支
将this->tVar
放入params
变量中跳入Hook::listen
,tag
变量是写死的view_parse
,因此name
变量为Behavior\ParseTemplateBehavior
,接着进入self::exec
在exec
函数中调用Behavior\ParseTemplateBehavior
的run
方法处理带有我们可控参数的$params
变量:
在run方法中经过一些判断,最终调用fetch
函数编译并加载模板文件,此时第二个参数就是我们的可控参数:array("_filename"=>“C://config.ini”);
继续跟进fetch
,获取缓存文件路径后,进入Storage的load方法中:
在load方法中,$_filename
为之前获取的缓存文件路径,$var
则为之前带有_filename=C://config.ini
的数组,利用extract方法进行变量覆盖,实现任意文件包含:
众所周知,include函数不在意包含的什么文件:
因此我们可以构造一个不存在的模块,使得tp报错,此时tp会将url写入log中,所以我们可以在url中写入恶意代码,再结合上面的LFI实现任意代码执行。
ef5418316bb4efc68bd96772d45afc77caf7808c