审完这个暂时就先不审了,去刷下ctfshow的tp题先试试手
影响版本
5.0.0<=ThinkPHP5<=5.0.18 、5.1.0<=ThinkPHP<=5.1.10
环境
我这里有之前安好的5.0.12就直接用了
\application\index\controller\index.php
<?php
namespace app\index\controller;
use think\Controller;
class Index extends Controller
{
public function index()
{
$this->assign(request()->get());
return $this->fetch(); // 当前模块/默认视图目录/当前控制器(小写)/当前操作(小写).html
}
}
application/index/view/index/index.html
内容任意(空文件亦可),主要是作为控制器中index方法的模板
分析
payload
?cacheFile=1.php #我在public下写了一个1.php便于文件包含
首先是进入get()
,其实get()
就是获取我们get传参的值,内容也比较简单所以就粗略的跟进下。
$this->get
不为空,$name
是字符串,所以没进入if,直接进入input()
(圈出来的是各参数值)
input的前两个if都绕开了,getFilter()
给$filter多赋了个null值,之后进入array_walk_recursive()
回调filterValue()
(这里的$data就是上一步的$get值)
跟进后其实也没执行啥,就执行了最后的filterExp()
,对用户传参进行过滤,但这个基本上是针对sql的所以对我们的payload没影响最终返回$data
的值,其实$data
的值自始至终没发生过改变
在就跟进assign()
了,$name
就是get()方法retrun的值,也就是$data的值
protected function assign($name, $value = '')
{
$this->view->assign($name, $value);
}
再跟进$this->view->assign($name, $value);
的assign()
通过array_merge()
,将$this->data
和$name
的值合并传给$data,但$this->data
默认为null,所以也就相当于把$name
给了它,retrun返回$this
回到我们自己定义的控制器$this->assign(request()->get());
就执行完了,再跟进下一行的fetch()
protected function fetch($template = '', $vars = [], $replace = [], $config = [])
{
return $this->view->fetch($template, $vars, $replace, $config);
}
又是套娃继续跟进(四个参数默认为空)
还是array_merge()
合并,1和3参数为空,所以$vars=$data;也就是我们get传参的值,$method三目运算,$renderContent=false
所以$method=fetch
160行就相当于又执行了fetch()
跟进$template
默认为空,parseTemplate()
获取我们模板文件的路径(看下底下的值就明白了),之后又又进入了fetch()
,可以看下三个参数的值
跟进,前边三个if只有第一个执行了,然后进入parseTemplateFile()
,里边其实就执行了一个记录模板文件的更新时间
的操作,不看了
下边if,对$cacheFile
赋值缓存的一个路径,之后直接进入read()
跟进,进入if进行了个变量覆盖在,EXTR_OVERWRITE标记的情况下,如果有冲突,覆盖已有的变量。所以就将原有的缓存路径替换成了1.php最后文件包含
不得不说TP非RCE的链真的很友好!!!!