WAF (Web Application Firewall)即應用防火牆。主要是用於HTTP(S)協議的校驗、攔截惡意(攻擊)請求,放行正常的業務請求。WAF的類型分為三類:網絡層、應用層、雲WAF。本次分享主要是研究應用層的WAF繞過,對於應用層的WAF是指WAF解析的是NGINX或者Apache處理過的HTTP數據包,也就是中間件初步處理後轉發給WAF處理。要想繞過WAF的過濾,那麼就得研究NGINX解析數據包與後端PHP或者其他語言解析的數據包有何不同。我們通過他們差異繞過這些WAF的過濾。2.1 form-data
multipart/form-data會將表單的數據處理為一條消息,以標籤為單元,用分隔符分開。既上傳鍵值對,也可以上傳文件。當上傳的字段是文件時,會有Content-Type來表名文件類型;content-disposition,用來說明字段的一些信息;由於有boundary隔離,所以multipart/form-data既可以上傳文件,也可以上傳鍵值對,它採用了鍵值對的方式,所以可以上傳多個文件。

2.2 x-www- form-urlencoded
application/x-www-from-urlencoded,會將表單內的數據轉換為鍵值對,&分隔。當form的action為get時,瀏覽器用x-www-form-urlencoded的編碼方式,將表單數據編碼為(name1=value1&name2=value2…),然後把這個字符串append到url後面,用?分隔,跳轉 到這個新的url。3.1文件上傳繞過
我們以ngx_lua_waf源碼來學習WAF是如何判斷上傳文件後綴。waf.lua 45-49 line.
我們可以看到WAF是通過這麼一個正則來匹配到文件後綴的,這個正則的缺陷在於如果我們在,Content-Disposition中引入垃圾數據,會導致正則匹配失敗。但是在PHP這種引入垃圾數據的 Content-Disposition是可以解析成功的。這也是單一型Bypass繞過的常見技巧。3.2雙寫boundary繞過
WAF為了降低誤報率並不會上傳文件的內容進行過濾,那麼我們能不能讓WAF以為我們是在上傳文件但是實際我們傳輸的是普通的POST數據包呢?答案是可以的,這是我們Bypass思想的核心所在。例如靶機:gqleung/bypass_waf_1,我們雙寫了boundary,但是WAF會認為boundary=a才是真正的消息,而boundary=b僅僅是boundary=a的內容,但是PHP會根據 Content-Type中的boundary來確定是哪個boundary消息的。這樣在上傳文件包不被檢查的時候可以導致POST包的過濾可以被Bypass。例如下面的HTTP數據包,真實的數據包就是b從而繞過WAF的攔截。如圖所示:
3.3.構造假boundary繞過
如果數據包中存在兩個相同的boundary塊頭PHP則會以第一個boundary頭為主,內容則以第二個boundary塊為主。但是某些WAF會以第第二個為主,即便WAF所有boundary塊都檢查,如果第二個是上傳包根據我們前面的前提(WAF為了降低業務誤報率,不檢查上傳的內容)也可以繞過WAF的檢查。我們再次以靶機:gqleung/bypass_waf_1靶機為例子構造如下數據包:
3.4Content-Disposition引號閉合繞過
我們看到下面的數據包,第一個Content-Disposition: form-data; name='fla; filename="exp.fw",我們在name中刪掉了一個引號,這樣的話再PHP中解析就會閉合到最後一個引號。但是在WAF中仍然以為是上傳文件。當然用雙引號也是可以的,但是閉合會閉合到下一個雙引號。
3.5參數溢出繞過
我們使用的WAF是基於lua-nginx-module-0.10.9rc7 開發,該模塊存在的問題是最大能夠獲取的參數數量是100個,也就是說該WAF最大的檢查數量是100個參數,如果是101個參數那麼地101個起不會檢查直接放行。這就是所謂的最大參數溢出繞過。以靶機gqleung/bypass_waf_3 為例,我們如果直接讀取/etc/passwd會直接被WAF攔截,如果前面加一百個參數就可以繞過。3.6skip_upload
在PHP中存在一個叫skip_upload的變量來控制是否是上傳。下面看一段PHP源碼(php- 5.3.3/main/rfc1867.c line 991)從代碼我們可以得知,只需要c小於0即可使得skip_upload=1,也就是跳過上傳。在前面幾個分支我們可以知道只要參數中存在]就會使得c--。根據上面的理論分析,我們只需要構造兩個Content-Disposition,其中一個是上傳的,在他name中加入]符號讓上傳被忽略,另外Content-Disposition是正常的POST參數,這樣我們在通過WAF時候WAF會認為我們的是上傳文件而非POST數據,但是PHP中卻跳過了上傳最終導致了WAF的檢查被繞過。在PHP源碼中 rfc1867.c line 909 可以看到如果skip_upload=1的情況有一種是Maximum number of allowable file uploads has been exceeded ,也就是上傳的文檔數量超過了最大允許範圍就會跳過超過數量的上傳。那麼這個最大文檔數量是多少呢?他是由php.ini中max_file_uploads決定的默認值就是20.也就是說我們的上傳文檔數量超過20就會被忽略。
我們同樣以靶機gqleung/bypass_waf_3為例根據前面的理論分析我們構造上傳包如下,第二十個我們可以使用正常的POST就可以收到參數。