### 简要描述: 其某处处理存在安全隐患,开发者一不小心就可能写出有漏洞的代码造成任意代码执行。 ### 详细说明: 这个问题之前看ThinkSNS就发现了: [WooYun: ThinkSNS getshell一枚](http://www.wooyun.org/bugs/wooyun-2013-043945) ThinkSNS由于使用ThinkPHP模板,造成任意代码执行。 下面分析下ThinkPHP实现细节 模板类是ThinkPHP中一个重要的类,主要实现了为模板变量赋值,模板解析和输出模板变量的逻辑,其中模板变量的赋值通过assign函数进行,模板变量的解析通过display函数,模板编译通过fetch函数,这一系列对模板的读写与编译操作导致了漏洞的产生。 先看编译,在ThinkPHP\Lib\Core\View.class.php的fetch函数中: ``` public function fetch($templateFile='',$content='',$prefix='') { … … … if('php' == strtolower(C('TMPL_ENGINE_TYPE'))) { // 使用PHP原生模板 // 模板阵列变量分解成为独立变量 extract($this->tVar, EXTR_OVERWRITE); // 直接载入PHP模板 empty($content)?include $templateFile:eval('?>'.$content); }else{ … … … } ``` 在ThinkPHP启用了原生模板的情况下,会调用extract把当前tVar导入到变量表里,然后会检测$content变量是否存在,如果存在就eval之,否则include $templateFile。 可以看到这里使用了extract函数,extract作用是从数组中把变量导入到当前的符号表中,而第二个参数表示如果有冲突,就覆盖已有变量。因此此处如果$this->tVar可控的话,那么就可以覆盖底下的$templateFile变量造成任意文件包含或覆盖$content造成任意代码执行。 漏洞的触发点就是上述的函数,而导致漏洞发生的参数可以通过模板变量赋值函数assign传入: ThinkPHP\Lib\Core\View.class.php...
### 简要描述: 其某处处理存在安全隐患,开发者一不小心就可能写出有漏洞的代码造成任意代码执行。 ### 详细说明: 这个问题之前看ThinkSNS就发现了: [WooYun: ThinkSNS getshell一枚](http://www.wooyun.org/bugs/wooyun-2013-043945) ThinkSNS由于使用ThinkPHP模板,造成任意代码执行。 下面分析下ThinkPHP实现细节 模板类是ThinkPHP中一个重要的类,主要实现了为模板变量赋值,模板解析和输出模板变量的逻辑,其中模板变量的赋值通过assign函数进行,模板变量的解析通过display函数,模板编译通过fetch函数,这一系列对模板的读写与编译操作导致了漏洞的产生。 先看编译,在ThinkPHP\Lib\Core\View.class.php的fetch函数中: ``` public function fetch($templateFile='',$content='',$prefix='') { … … … if('php' == strtolower(C('TMPL_ENGINE_TYPE'))) { // 使用PHP原生模板 // 模板阵列变量分解成为独立变量 extract($this->tVar, EXTR_OVERWRITE); // 直接载入PHP模板 empty($content)?include $templateFile:eval('?>'.$content); }else{ … … … } ``` 在ThinkPHP启用了原生模板的情况下,会调用extract把当前tVar导入到变量表里,然后会检测$content变量是否存在,如果存在就eval之,否则include $templateFile。 可以看到这里使用了extract函数,extract作用是从数组中把变量导入到当前的符号表中,而第二个参数表示如果有冲突,就覆盖已有变量。因此此处如果$this->tVar可控的话,那么就可以覆盖底下的$templateFile变量造成任意文件包含或覆盖$content造成任意代码执行。 漏洞的触发点就是上述的函数,而导致漏洞发生的参数可以通过模板变量赋值函数assign传入: ThinkPHP\Lib\Core\View.class.php ``` public function assign($name,$value=''){ if(is_array($name)) { $this->tVar = array_merge($this->tVar,$name); }else { $this->tVar[$name] = $value; } } ``` 如果传入的是数组,那么直接将数组合并和放入到tVar。 漏洞最终会通过display函数触发: ThinkPHP\Lib\Core\View.class.php ``` public function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') { G('viewStartTime'); // 视图开始标签 tag('view_begin',$templateFile); // 解析并获取模板内容 $content = $this->fetch($templateFile,$content,$prefix); // 输出模板内容 $this->render($content,$charset,$contentType); // 视图结束标签 tag('view_end'); } ``` 因此如果调用assign传入可控数组并且调用display显示模板就会触发漏洞。 ### 漏洞证明: 可看之前一个例子: [WooYun: ThinkSNS getshell一枚](http://www.wooyun.org/bugs/wooyun-2013-043945) 用ThinkPHP写一个demo测试: ``` class IndexAction extends Action { public function index(){ $this->assign($_GET); $this->display(); } } ``` 开发者通过assign函数将$_GET变量全数传入到模板示例里,然后调用display解析并输出模板,最终导致了任意代码执行: http://www.ztz.com/thinkphp/App/?content=%3C%3Fphp%20phpinfo%28%29%3B%3F%3E 我们控制content参数为<?php phpinfo(); ?> [<img src="https://images.seebug.org/upload/201402/241746337ddfdc0ec4b99fe80d8bd97dfc657e26.jpg" alt="QQ图片20140224174613.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201402/241746337ddfdc0ec4b99fe80d8bd97dfc657e26.jpg)