文件上傳bypass安全狗
- 2021 年 5 月 6 日
- 筆記
0x00 前言
本文首發於先知社區://xz.aliyun.com/t/9507
我們知道WAF分為軟WAF,如某狗,某盾等等;雲WAF,如阿里雲CDN,百度雲CDN等等;硬WAF,如天融信,安恆等等,無論是軟WAF、雲WAF還是硬WAF,總體上繞過的思路都是讓WAF無法獲取到文件名或者其他方式無法判斷我們上傳的木馬(PHP、JSP、ASP、ASPX等等)。
這裡總結下關於軟waf中那些繞過文件上傳的姿勢和嘗試思路,這裡選擇繞過的軟waf為某狗4.0,可能其他軟waf在攔截關鍵字方面可能會有差異,但繞過軟waf的大體思想都是相同的,如果文章中有錯誤,歡迎師傅們斧正。
0x01 初探原理
寫這篇文章時想過一個問題,如何總結哪些屬於文件上傳Bypass的範疇?打個比方:
上傳正常.jpg的圖片 #成功
上傳正常.php #攔截
繞過.php文件的filename後進行上傳 #成功
使用繞過了filename的姿勢上傳惡意.php #攔截
以上這麼個邏輯通常來講是waf檢測到了正文的惡意內容。再繼續寫的話就屬於免殺的範疇了,過於模糊並且跑題了,並不是真正意義上的文件上傳Bypass,那是寫不完的。
上傳文件時waf會檢查哪裡?
請求的url
Boundary邊界
MIME類型
文件擴展名
文件內容
常見擴展名黑名單:
asp|asa|cer|cdx|aspx|ashx|ascx|asax php|php2|php3|php4|php5|asis|htaccess htm|html|shtml|pwml|phtml|phtm|js|jsp vbs|asis|sh|reg|cgi|exe|dll|com|bat|pl|cfc|cfm|ini
個人寫的「稍微」全一點,實際上waf的黑名單就不一定這麼全了。
測試時的準備工作:
- 什麼語言?什麼容器?什麼系統?都什麼版本?
- 上傳文件都可以上傳什麼格式的文件?還是允許上傳任意類型?
- 上傳的文件會不會被重命名或者二次渲染?
0x02 環境介紹
實驗環境:mysql + apache +php
waf:某狗4.0
這裡用了一個簡單的上傳頁面判斷,觀察程式碼可以發現只允許上傳Content-Type為image/gif、image/jpeg、image/pjpeg三種形式的文件
<html> <body> <form action="upload.php" method="post" enctype="multipart/form-data"> <label for="file">Filename:</label> <input type="file" name="file" id="file" /> <br /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html> <?php error_reporting(0); if ((($_FILES["file"]["type"] == "image/gif") || ($_FILES["file"]["type"] == "image/jpeg") || ($_FILES["file"]["type"] == "image/pjpeg"))) //&& ($_FILES["file"]["size"] < 20000)) { if ($_FILES["file"]["error"] > 0) { echo "Return Code: " . $_FILES["file"]["error"] . "<br />"; } else { echo "Upload: " . $_FILES["file"]["name"] . "<br />"; echo "Type: " . $_FILES["file"]["type"] . "<br />"; echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />"; echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />"; if (file_exists("upload/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " already exists. "; } else { move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "Stored in: " . "upload/" . $_FILES["file"]["name"]; } } } else { echo "Invalid file"; } ?>
0x03 實驗bypass
先上傳一個asp,看一下返回值
這裡看到了404xxxdog的頁面,那應該是攔截了,我這裡先放過去看看,果然是某狗攔截了
開始嘗試繞waf
這裡我先把Content-Type改成image/gif通用圖片類型
0x03.1 增大文件大小
測試發現 waf對於Content-Disposition欄位的 長度驗證不是很準確,因為我們可以想到它進行攔截的規則肯定是基於正則,那麼我們想辦法讓安全狗攔截的正則匹配不到即可
這裡附一個對Content-Disposition欄位的解釋
在常規的 HTTP 應答中,Content-Disposition 響應頭指示回復的內容該以何種形式展示,是以內聯的形式(即網頁或者頁面的一部分),還是以附件的形式下載並保存到本地。
在 multipart/form-data 類型的應答消息體中,Content-Disposition 消息頭可以被用在 multipart 消息體的子部分中,用來給出其對應欄位的相關資訊。各個子部分由在Content-Type 中定義的分隔符分隔。用在消息體自身則無實際意義。 Content-Disposition 消息頭最初是在 MIME 標準中定義的,HTTP 表單及 POST 請求只用到了其所有參數的一個子集。只有 form-data 以及可選的 name 和 filename 三個參數可以應用在HTTP場景中。
這裡對這個欄位的長度進行篡改,繞過成功
0x03.2 對文件名修改(卒)
我們在上傳時候通常會把文件名後綴和解析漏洞,那麼waf對於filename參數後的值的文件名後綴肯定是要正則去匹配的 這樣正常上傳肯定不行
那麼繞過之前我們猜想,第一個它可能是對filename這樣的鍵值對進行匹配,例如”ket = val”這樣的鍵值對 那麼這裡我們就是filename=「shell.php」
那這裡把雙引號去除,擾亂匹配,發現不行
那麼我們可不可以多一個filename,因為文件在接收上傳文件名時取的是最後一個filename,那麼我們在最後一個filename參數前加一些干擾的filename參數試試
發現還是不行 那麼這裡就知道他是對所有filename參數進行檢測 那麼我們能不能把前面的filename參數去掉值呢
Content-Disposition: form-data; name="file"; filename= ; filename="shell.php"
結果對文件名進行修改全卒,在之前版本的某狗在filename= ;是可以進行繞過的,4.0版本文件名修改全卒
0x03.3 修改文件名後綴
經典的apache解析漏洞嘗試,攔截
可以在文件名中間加符號擾亂某狗匹配,經測試 “;” ” ” ” ‘ ” 均可
0x03.4 對filename動手腳
這裡可以讓waf對filename這個字元串匹配不到,但是伺服器又可以接收,加入換行這類的干擾
先測試單個字元進行換行,都失敗
切斷filename= 和 之後的值,則可以繞過
文件名換行,即hex加入0a,也可以繞過
0x03.5 修改匹配欄位(卒)
我們的filename參數是在post包中的 Content-Disposition 欄位,那麼waf也是先匹配到這個http頭在對內容進行檢測,我們可以嘗試對這個頭的特徵進行修改
我們嘗試去掉這個form-data (form-data;的意思是內容描述,form-data的意思是來自表單的數據,但是即使不寫form-data,apache也接受。)
Content-Disposition: name=”file”; filename=”shell.php”
發現失敗,之前3.0版本可以繞,4.0卒
對Content-Disposition進行參數污染,攔截
對Content-Disposition進行大小寫混淆,攔截
加上額外的Content-Type進行干擾,攔截
加上filename進行參數污染,攔截
加一個額外的Content-Length頭,攔截
0x03.6 多個等號
經測試兩個=或者三個=都可以達到繞過的效果
0x03.7 %00截斷
%00截斷產生的原因是0x00為十六進位的表示方法,ASCII碼里就為0,而有些函數在進行處理的時候會把這個當作結束符
這裡直接嘗試在文件名後面加上%00形成00截斷,成功繞過
0x04 後記
某狗4.0版本對於3.0版本又有了一定的改進,對於之前的文件名修改和修改匹配欄位已經不能夠繞過waf,但是對於繞waf的思想總結起來可以有如下幾點:
大小寫轉換、干擾字元污染、字元編碼、拼湊法