[2021-05-05 11:38:39 更正了一處錯誤說法]
寫在前面
在做一道CTF題是涉及到了文件上傳,然后突然發現頁面沒有上傳文件的按鈕。

就像這樣可以瀏覽本地並選定本地文件再點擊上傳的按鈕竟然沒有。

正文
先看一下正常上傳POST變量和文件的數據包。
上傳POST變量
輸入123並上傳,通過Brupsuite中可以看到:
這是作為表單的格式上傳的POST變量,但POST變量可以用一種更加簡潔的格式上傳,我們用GET數據包修改那當然是修改起來越簡單越好,而且這兩種格式在結果上並沒有差別(說法錯誤),所以這里只將說明如何修改成更簡單的后者來上傳。
#更正:實際上Content-Type的值application/x-www-form-urlencoded和multipart/form-data在結果是也是有區別的,前者告知了服務器提交的參數使用了url編碼,故服務器對其解析時會使用URL來解碼。具體效果如下:
[2021-05-27 08:55:08 突然發現在這里進行更正的時候使用了另外一個PHP文件,但沒有說明。簡單說這個PHP文件如圖所示,先輸出了一句"---the $_POST---"和換行,接着用了print_r輸出POST數組中的鍵值]
關於POST共有四種常用提交數據方式,具體參考如下博客(博主不懂其他兩個Orz):四種常見的 POST 提交數據方式(application/x-www-form-urlencoded,multipart/form-data,application/json,text/xml)_stay hungry ! stay foolish!-CSDN博客
BrupSuite提供了在這兩種格式間轉換的一鍵式操作,只需要在BrupSuite的請求消息框(注意保證在框中右鍵點擊)中右鍵選擇"Body類編碼改變即可",如果攜帶的數據是文件時選擇這種轉換將按照文件中的name為變量名而文件內容為變量值的規則獲得一個POST變量,這就會導致文件的filename在這一轉換中丟失,但如果是POST變量則不用擔心。
下面來說如何將一個GET數據包修改成如上圖的攜帶POST變量的POST數據包,首先訪問目標PHP文件(可以在上傳的頁面的源碼中尋找<form>標簽中的action屬性來確定),然后用BrupSuite把數據包攔下來。
進行如下操作就可以修改成一個攜帶POST變量的POST數據包:
- 請求方式修改為POST
- 消息頭中加上Content-Type:application/x-www-form-urlencoded
- 在消息主體中加上需要上傳的POST數據
注:Content-Length並不是必須的
上傳文件
先觀察正常上傳文件的數據包:
與上傳一個POST變量的區別在於,與文件相關的部分存放在消息主體並且被一層字符首尾包裹,這串字符在消息頭中的Content-Type中的boundary被規定,這個boundary起着一個分隔符的作用,此處先單獨講一下關於boundary的規則(根據服務器軟件種類不同這個規則可能會有細微區別)。
boundary的值在Content-Type中是可以被自由規定的(甚至可以為空,但必須要保證在Content-Type中存在boundary=這串字符),獨占一行的--加上boundary組成一個非尾部的分割符,后面接着的是與文件相關的部分,在其末端的下一行則又是--加上boundary組成的分隔符,但需要注意的是如果這行分隔符是尾部(后面沒有其他文件數據)則應該在這行分割符的后面再加上--(即--加上boundart加上--)並換行。
關於文件數據主要有三部分組成:
文件信息中,filename為必要的,其余的form-data、name和Content-Type的值為不必要的(刪掉后兩者僅會導致目標PHP$_FILES數組中對應文件的對應數據為空,如果目標PHP存在文件校正則應補上,至於前者...好像沒影響?)。此外空行是必要的,文件數據可以為空但必須獨占一行。
下面來說如何將一個GET數據包修改成如上圖可以上傳文件,同樣先直接訪問目標PHP文件。
接着向請求包中做出如下更改:
- 請求方式修改為POST方式
- 消息頭中加上Content-Type:multipart/form-data;boundary=XXX (XXX是你中意的字符,可以為空)
- 在消息主體中加上一個由boundary和--構成的分隔符(非尾部)
- 在填入分隔符的下一行填入Content-Disposition:(form-data;) (name="thefile";) filename="test.txt" (在一點中()包裹的為可選內容)
- (可選)在Content-Disposition下一行填入Content-Type:XXX (XXX為文件類型)
- 在上述內容的下一行填入文件數據 (不可見字符請以手動逐個字節添加,直接復制黏貼或導入文件會出現多余字符)
- 視情況在文件數據下一行添加boundary和--構成的分隔符(非尾部)或boundary和--構成的分隔符(尾部,在非尾部的部分再加上--並換行,表示此前的文件數據是最后一份)
- (可選)如果是多個文件請按照第4點往下執行
最后說一句
關於消息頭Content-Length這個參數可以不用加上,因為Brupsuite在發包時會自動加上(用wireshark查看就能確認這一點)。此外傳入GET值,POST值,和FILE文件這三者並不互相干擾,完全可以三個願望一次滿足(不是
文章難免有不足,歡迎各位師傅斧正。