前言:
接觸過網絡安全行業的人都知道什么是WAF,這是每一個安全人員都會面臨的難題,面對市面上各種waf,繞過waf的手段也層出不窮,自己在實戰的過程中也經常面臨遇到waf但是缺乏繞waf的思路和方法導致滲透測試不能繼續下去,在本文中我將會記錄自己在學習sql注入繞waf的過程,如有不足之處歡迎各位大佬補充(#^.^#).
正文:
WAF(Web Application Firewall)的中文名稱叫做“Web應用防火牆”,市面上的waf主要分為三大類,第一類是硬件類(綠盟、天融信、安恆的硬件waf)、第二類是軟件類(安全狗、雲鎖、寶塔等軟件waf)、第三類是基於雲的waf(阿里雲、騰訊雲、創宇盾等雲waf),而所謂的繞waf就是指繞過防火牆的攔截規則從而達到攻擊的目的。
我之前已經知道的sql注入繞waf手段有五種,分別是大寫繞過(OrDEr bY 1)、雙寫繞過(ororderder bbyy 1)、編碼解碼、內聯注釋繞過(/**/,/*!*/)、內存溢出繞過(就是加一些很長很長的字符串超出waf檢測范圍從而繞過),不過這里有些繞過手段對於很老的waf才有用,后來還了解到了http參數污染繞過和Content-Type繞過、分塊傳輸繞過等,這些我會放在后面的一篇文章里寫出來。
回歸主題,先來了解一下三大數據庫的注釋、空白字符和語法特性
| mysql | oracle | sql server | |
|---|---|---|---|
| 注釋符 | /**/、/*!*/、#、-- -、-- 、-- + | /**/、-- +、-- 、-- - | /**/、-- +、--%0A-、-- - |
| 空白字符 | %20%0A%0B%0C%0D%20 | %00%20%0A%0B%0C%0D | %0A%00%20 |
SQL語句select、union、from關鍵字前后可以拼接一些特殊字符,這些關鍵字可以讓我們進行FUZZ
Mysql
[ + ]


[ ! ]


[ - ]


[ @ ]


[ ' ]


[ " ]


[ ~ ]


[ { ]


[ \N ]

可以看出select后面拼接+號可以解析user,花括號貌似是放正則匹配的,但是我們只要在關鍵字加上反引號或者括號是可以解析關鍵字的,from關鍵字后面也可以加上花括號,通過以上特性可以幫助我們FUZZ從而繞過waf,例如

除了mysql,Oracle和sql server也有這種特性,但是不同的是它們並不是完全支持mysql所擁有的特性,接下來我會分析Oracle和sql server擁有哪些特性。
Oracle
[ + ]

|----------------------------------------------------分割線----------------------------------------------------------------------|

[ - ]

|----------------------------------------------------分割線----------------------------------------------------------------------|

[ ! ]

|----------------------------------------------------分割線----------------------------------------------------------------------|

[ @ ]

|----------------------------------------------------分割線----------------------------------------------------------------------|

[ ~ ]

|----------------------------------------------------分割線----------------------------------------------------------------------|

[ ' ]

|----------------------------------------------------分割線----------------------------------------------------------------------|

[ " ]

|----------------------------------------------------分割線----------------------------------------------------------------------|

[ { ]

|----------------------------------------------------分割線----------------------------------------------------------------------|

[ \N ]

從上面的結果和我自己另外測試的結果可以得出
-
Oracle的select關鍵字能添加的特殊字符有限,例如+、'、"能全面支持,-不能作用於select后面的關鍵字,!、@、~、{}、\N不能在select和from后面使用;
-
反引號不會解析關鍵字,括號跟mysql一樣可以解析關鍵字但是括號里不能沒有東西;
-
union前加數字要加上and或or才能執行,不能加xor會報錯;
Sql Server
[ + ]


[ - ]


[ ! ]


[ @ ]


[ ~ ]


[ ' ]


[ " ]


[ { ]


[ \N ]

從上面的結果和我自己本地測試可以得出:
-
MSSQL的union前可以添加數字,但是要加上=(0=1.1)還要有and或者or,不能加xor,否則會報錯;
-
select后不能加!,@,~,{}
-
反引號不會解析關鍵字,括號跟mysql一樣可以解析關鍵字
-
MSSQL可以使用在from關鍵字前使用\N
MSSQL這里放個小技巧,當top限制輸出不能使用的時候我們可以給前面的SQL語句加個分號結束它,然后寫上set rowcount 1;就限制輸出一行了,並且是全局作用范圍也就是說下面所有的語句都只輸出一行,要想設置回來就把1改為零即可,接着把set rowcount 1;語句刪除再寫上我們構造的SQL語句就可以直接限制輸出了.
select a from test where id='1';set rowcount 1;-- +'
實戰
直接放圖吧
Mysql

Sql server

Oracle

我的sql注入bypass思路
我的測試流程是先看看waf過濾了什么,例如union select被過濾那么就看看是union被過濾還是select被過濾或者union select連起來被過濾,然后在被攔截的關鍵字前后隨便填充些字符看看是否還攔截,如果不攔截就開始進行fuzz,我一般習慣在目標站點進行fuzz到不攔截再去本地數據庫上測試出一條可以執行的語句然后放給目標站點執行,假如說填充了字符還是攔截的話那么久很可能函數或關鍵字被過濾了,這樣的話我才考慮等價替換或者利用web容器特性繞過,比如說apache的HDD參數污染、IIS的%分割繞過,往后再考慮分塊傳輸、Content-Type繞過等繞過方式。
等價替換
對於關鍵字或者函數過濾,除了使用內聯注釋分割我們還可以通過等價替換繞過,這里我就放mysql的等價替換了,對於其他數據庫思路其實是一樣的,先通過官方文檔獲取所有api函數,然后index.php?id=1 xor user()用burpsuite來爆破出沒有被過濾的函數,接着去看沒有被過濾的函數的用法再本地測試看看是否能組合利用,在目標站點測試的時候最好配合本地測試。
截取函數:
mid(string,1,1) <=> mid(string from 1 for 1)

|----------------------------------------------------分割線----------------------------------------------------------------------|
strsub(string,1,1) <=> strsub(string from 1 for 1)

|----------------------------------------------------分割線----------------------------------------------------------------------|
lpad(string,1,1) ldap用法:lpad(string,從string取指定數量的字符,如果取的數量超出了string內容的長度那么剩下的則用這里的替換掉)

|----------------------------------------------------分割線----------------------------------------------------------------------|
repacle(LPAD(user(),1,1),LPAD(user(),2,1),"") replace用法:replace(在這里進行匹配,要匹配什么,匹配到后替換成什么)

|----------------------------------------------------分割線----------------------------------------------------------------------|
LPAD(reverse(trim(LPAD(user(),1,space(1)))),1,space(1)) 用法:space(生成指定數量的空格字符);trim(去除前后空格字符);reverse(把值進行反轉 asp=>psa)

|----------------------------------------------------分割線----------------------------------------------------------------------|
編碼解碼函數:
當ascii()和ord()函數被過濾時可以用conv函數替代:
conv(hex(LPAD(user(),1,1)),16,10) conv用法: conv(這里放進制值,第一位值是什么進制,要轉換成什么進制)

|----------------------------------------------------分割線----------------------------------------------------------------------|
,逗號被過濾
,逗號在sql注入中是很重要得,多個關鍵字和函數都要用到,這里就說一下不用逗號的關鍵字和函數
if <=> case when

|----------------------------------------------------分割線----------------------------------------------------------------------|
union select 1,2,3 <=> union select * from (select 1)a join (select 2)b join (select 3)c

這里報錯是因為我聯合查詢的字段數超過了前面那張表里的字段數所有報了個超出字段數的錯誤,實際上是可以用的。
|----------------------------------------------------分割線----------------------------------------------------------------------|
limit 0,1 <=> limit 1 offset 0

|----------------------------------------------------分割線----------------------------------------------------------------------|
比較表達式
if(abs(strcmp(ascii(substr(user(),1,1)),114))-1,1,0) 用法:strcmp()比較兩個值的大小,第一個值比第二個值大就返回1,小返回-1,等於返回0;abs()求絕對值再減去一,結果為true就返回1,false返回0;

|----------------------------------------------------分割線----------------------------------------------------------------------|
select find_in_set(ord(substr(user(),1,1)),114) 函數用法:find_in_set(在這里進行查找,要查找的內容) 找到就返回1否則返回0

|----------------------------------------------------分割線----------------------------------------------------------------------|
select ord('r') regexp 114 用法:跟find_in_set原理差不多,regexp會去前面的結果查找指定的內容,找到就返回1否則返回0

|----------------------------------------------------分割線----------------------------------------------------------------------|
least(ord('r'),118) 函數用法:least會比較兩個數值誰最小並輸出出來

|----------------------------------------------------分割線----------------------------------------------------------------------|
greatest(ord('r'),100) 函數用法:greatest會比較兩個數值誰最大並輸出出來

|----------------------------------------------------分割線----------------------------------------------------------------------|
between x and y 用法:between設定一個區間然后去判斷前面的結果是否在這個區間里,在則返回1否返回0

總結
寫這篇文章的時候比較急,所以寫的比較亂,各位大佬要是覺得有不足之處歡迎點評更正,總的來說繞waf也是一個比較深入的領域,除了這里的方法之外還有許多其他的奇淫巧技,但只要有思路甚至可以琢磨出自己的一套繞waf方式再加上市面上大部分的waf都是正則匹配可以通過像本文寫的那樣FUzz一下多花點時間基本都能繞過,還可以逆向軟件waf的匹配庫研究一下它們的匹配規則來繞過也是可行的。
文章參考資料:先知社區
