ThinkPHP3.2.x RCE漏洞分析
环境搭建
修改控制器
/Application/Home/Controller/IndexController.class.php
1 |
|
新建文件,内容随意
/Application/Home/View/Index/index.html
漏洞复现
利用条件:assign方法的第一个变量可控。
向日志文件写入攻击代码
1 |
|
包含日志文件,日志文件的命名规则为年_月_日.log
1 |
|
漏洞分析
先进入assign方法
把assign方法的第一个参数赋值给$this->tVar
ThinkPHP/Library/Think/View.class.php
assign方法结束后进入display
ThinkPHP/Library/Think/View.class.php#fetch
进入fetch方法,因为模板文件名为空,所以先调用parseTemplate获取默认模板文件(./Application/Home/View/Index/index.html),也就是之前新建的文件。
然后构造了params数组,包含了传入的value数组和默认模板文件名
/ThinkPHP/Library/Think/Hook.class.php
进入exec
/ThinkPHP/Library/Think/Hook.class.php
实际调用Behavior\ParseTemplateBehavior->run,把params数组作为参数
/ThinkPHP/Library/Behavior/ParseTemplateBehavior.class.php
调用fetch
/ThinkPHP/Library/Think/Template.class.php
获取缓存文件路径,然后进入load方法
/ThinkPHP/Library/Think/Storage/Driver/File.class.php
调用extract,将$vars数组作为参数进行变量覆盖。把$_filename覆盖为日志文件名,然后包含日志文件
再回来看看payload,实际上只是传入了一个包含日志文件名的value数组,只需要关注它的流向就可以了
1 |
|
传入的value数组的流向:
在assign()方法中,传入的value数组被赋值给$this->tVar
在
ThinkPHP/Library/Think/View.class.php#fetch
中被赋值给params数组,然后作为第二个参数传给listen在
/ThinkPHP/Library/Think/Hook.class.php#listen
中作为第三个参数传给exec在
/ThinkPHP/Library/Think/Hook.class.php#exec
中调用Behavior\ParseTemplateBehavior->run,把params数组作为参数在
/ThinkPHP/Library/Behavior/ParseTemplateBehavior.class.php#run
中,params[‘value’]也就是value数组被作为第二个参数传入fetch在
/ThinkPHP/Library/Think/Template.class.php#fetch
中又继续作为第二个参数传入load在
/ThinkPHP/Library/Think/Storage/Driver/File.class.php#load
中进行变量覆盖,最终包含传入value数组中的_filename
,执行了向日志文件中写入的代码,实现RCE
总结
在代码审计知识星球看到了这个新出的漏洞,分析后感觉更适合作为权限维持的后门,实际业务代码中应该很少有这种情况
参考链接
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!