ThinkPHP3.2.x RCE复现
漏洞描述
该漏洞是在受影响的版本中,业务代码中如果模板赋值方法assign的第一个参数可控,则可导致模板文件路径变量被覆盖为携带攻击代码的文件路径,造成任意文件包含,执行任意代码。个人感觉这个漏洞虽然不如thinkphp本体的一些RCE漏洞影响范围大,但也算是提供了一个思路,对基于某些框架二次开发的系统,寻找其不规范的方法调用的思路还是值得学习的。
环境搭建
ThinkPHP3.2.3 Phpstudy8.1.1 PHP-5.6.9 Apache Windows10(tp3手册)
如果是linux可以用composer拉取环境( composer create-project topthink/thinkphp=3.2.3 tp3)
先访问http://xxx.xx/index.php 生成项目文件
然后在\Application\Home\Controller\IndexController.class.php
写入demo代码
1 |
|
因为该漏洞利用的assign函数需要模板渲染,所以需要对应的模板文件,与demo相对应的文件路径:
1 | \Application\Home\View\Index\index.html |
漏洞分析
借用玄甲安全实验室的一张图,整体看一下程序的执行流程
接下来分析具体的代码:
Application/Home/Controller/IndexController.class.php
assign方法中第一个变量可控
跟进assign函数
ThinkPHP/Library/Think/Controller.class.php
调用的是ThinkPHP/Library/Think/View.class.php
中的assign函数,此时进入else分支,我们传进去的$value
被赋值给$this->tVar
返回后进入display函数,注意这是一个无参调用
调用的是ThinkPHP/Library/Think/View.class.php
的display函数,开始解析并获取模板文件内容,此时模板文件路径和内容为空
进入ThinkPHP/Library/Think/View.class.php
的fetch函数后,会先判断模板存不存在,这也是为什么我们要创建模板文件的原因
接着判断是不是php类型模板,不是进入else分支。接着$params 被赋值,var即为为我们传进去的日志路径,file为模板文件的路径。
接着进入ThinkPHP/Library/Think/Hook.class.php
的listen函数,经过一些判断,进入exec函数
接着exec函数把$params带进ThinkPHP/Library/Behavior/ParseTemplateBehavior.class.php
的run函数处理
进入ThinkPHP/Library/Think/Template.class.php
的fetch函数
进入最后的load函数,$var不为空则使用extract方法的EXTR_OVERWRITE默认描述对变量值进行覆盖,之后include该日志文件路径,造成文件包含。
漏洞利用
日志包含
日志在
Application/Runtime/Logs/Home
目录下开启debug
1
2请求不报错 日志文件在 Application/Runtime/Logs/Home下
请求会报错 日志文件在 Application/Runtime/Logs/Common下关闭debug
1
2请求不会报错 不会记录日志
请求会报错 日志文件在 Application/Runtime/Logs/ 下
上传入口
利用系统自带的上传功能,上传包含shell的文件
1 | http://x.x.x.x/index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Common/21_07_14.log |