这是一个服务端模板注入漏洞。
Application/Home/Controller/MController.class.php
```
<?php
namespace Home\Controller;
use Common\Controller\FrontendController;
class MController extends FrontendController{
public function index(){
if(!I('get.org','','trim') && C('PLATFORM') == 'mobile' && $this->apply['Mobile']){
redirect(build_mobile_url());
}
$type = I('get.type','android','trim');
$android_download_url = C('qscms_android_download')?C('qscms_android_download'):'';
$ios_download_url = C('qscms_ios_download')?C('qscms_ios_download'):'';
$this->assign('android_download_url',$android_download_url);
$this->assign('ios_download_url',$ios_download_url);
$this->assign('type',$type);
$this->display('M/'.$type);
}
}
?>
```
可见这里将`$type`传入display函数,display函数是ThinkPHP中展示模板的函数。跟进了几个函数,进入了View类的display函数:
```
<?php
public function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {
G('viewStartTime');
// 视图开始标签
Hook::listen('view_begin',$templateFile);
// 解析并获取模板内容
$content = $this->fetch($templateFile,$content,$prefix);
// 输出模板内容
$this->render($content,$charset,$contentType);
// 视图结束标签
Hook::listen('view_end');
}
```
可见,这里拿到了`$templateFile`并传入fetch函数,将fetch的结果交给render函数。这两个函数,fetch是将文件内容获取到,render是使用ThinkPHP的模板引擎渲染之。
拿favicon.ico一下,已经成功包含:
```
http://enshijob.com/index.php?m=&c=M&a=index&type=../favicon.ico
http://118.178.58.235/index.php? ... type=../favicon.ico
```
![](http://mmbiz.qpic.cn/mmbiz_jpg/shVkAyu04IHwib3KVN3VBPOeSHnPkrtLDaxymYwrSMriburQ2R21v0KtibKNLndCguCtJ2sp8fCbUWovk8e0mnyjw/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1)
### 漏洞利用
那么,这个漏洞如何利用?因为type的值是作为display函数的参数传入的,所以实际上这个漏洞可以理解为“模板引擎注入”,我们现在控制了模板的文件目录,现在只需要上传一个内容符合ThinkPHP模板格式的文件,再作为type的值即可。
查看ThinkPHP文档:
![](http://mmbiz.qpic.cn/mmbiz_jpg/shVkAyu04IHwib3KVN3VBPOeSHnPkrtLDaKibJcqzkyYn8YWbBJcaFGmsz6c3RwjMOIuekaiabyXbcVqujOzv7j2g/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1)
可知,要使用原生PHP代码的话,只需要将代码包含在`<php>`标签内即可。
74cms里,个人用户创建简历后支持上传docx格式的简历。我们上传一个包含`<php>...</php>`的docx文件,将其作为模板。数据包如下:
![](http://mmbiz.qpic.cn/mmbiz_png/shVkAyu04IHwib3KVN3VBPOeSHnPkrtLDwv8CBY5kZ0PPHuxOo7ibzf2s1Z1RjjFbwdx6GpY31qvH0nz0bG1N5HQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
![](http://mmbiz.qpic.cn/mmbiz_png/shVkAyu04IHwib3KVN3VBPOeSHnPkrtLDwv8CBY5kZ0PPHuxOo7ibzf2s1Z1RjjFbwdx6GpY31qvH0nz0bG1N5HQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
pid是简历id,word_resume就是包含模板的文件。上传成功,获取文件名:
![](http://mmbiz.qpic.cn/mmbiz_jpg/shVkAyu04IHwib3KVN3VBPOeSHnPkrtLDXfv9Fibgicn2UNCoyWezXx3w0wicND1raE7VQ7IZW46GMHwBVtic6oECGA/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1)
再将这个文件名作为type的值,成功执行代码:
```
http://vm.cn/74cms/index.php?m=& ... /58a442fca3f70.docx
```
![](http://mmbiz.qpic.cn/mmbiz_jpg/shVkAyu04IHwib3KVN3VBPOeSHnPkrtLDsJaiaZibZUvSyqARENyXzF3vEhOLs2890ibzjMkDsFAG3qHaWx5KfuCNg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1)
最后说一下,这个文件判断存在的时候,linux下可能会有点问题,因为is_file这个函数遇到"M/../../../etc/passwd"这样的文件路径,只要M不存在就会返回false,没细研究具体的内容,有心的同学可以研究一下发出来。
全部评论 (3)