Foreword
自struts2官方紕漏S2-066漏洞已經有一段時間,期間斷斷續續地寫,直到最近才完成。羞愧地回顧一下官方通告:
2023.12.9釋出,編號CVE-2023-50164,主要影響版本是 2.5.0-2.5.32 以及 6.0.0-6.3.0,描述中提到了檔案上傳漏洞和目錄穿越漏洞。開始以為這是個組合漏洞,其實不是,這是一個漏洞,看了幾篇大佬的文章,有的把它稱為“檔案上傳目錄穿越漏洞”,也有道理。
Prepare
準備工作就是搭建專案,用Tomcat跑,除錯好斷點,回顧下struts2的結構。篇幅有限,這裡只貼一張struts2自身的配置檔案:
struts2有眾多的Filter和Intercepter,它的配置邏輯是,除檔案中定義的class、package以外,其餘全部攔截。對於S2-066,處理一個請求要經過的幾個關鍵類包括Dispatcher、Interceptor、HttpParameters以及UploadAction。使用下面的poc:
Dispatcher
請求首先會進入著名的Dispatcher。multi引數對應的就是body資料,包含upload、fileName、contentType三個變數,無誤:
request中還有一個引數,uploadFileName=../../z127.txt,這個是汙染引數,檔案上傳的目的地,也是利用這個漏洞的目標。
走到這裡Dispatcher只是簡單處理一下請求然後交給Interceptor,無異常。
【----幫助網安學習,以下所有學習資料免費領!加vx:dctintin,備註 “部落格園” 獲取!】
① 網安學習成長路徑思維導圖
② 60+網安經典常用工具包
③ 100+SRC漏洞分析報告
④ 150+網安攻防實戰技術電子書
⑤ 最權威CISSP 認證考試指南+題庫
⑥ 超1800頁CTF實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP客戶端安全檢測指南(安卓+IOS)
FileUploadInterceptor
攔截器先是把request包裝了一下,型別是MultiPartRequestWrapper。
這裡與Dispatcher一樣,請求引數還是multi和request兩部分,也無異常。
並且遍歷只有一次 ,因為真正的body只有一個,就是那個multi。
在遍歷過程中struts2還出現了硬編碼現象,要求檔名引數必須以FileName結尾,且拼接完成的檔名字首就是body中的{upload}名稱,這就給exp帶來了一定限制:
此外,注意這裡的279行:
// get the name of the file from the input tag
String[] fileName = multiWrapper.getFileNames(inputName);
使用的是MultiPartRequest介面的方法,而這個介面在S2-066中是由JakartaMultiPartRequest實現。使用下面這個poc進行目錄穿越並斷點檢測一下:
發現目錄穿越失敗,檔案沒有放在指定目錄下。分析原始碼:
引數覆蓋原本想用../../z126.txt,方法的輸入引數確實也是這樣接收的,但在這個方法中struts2會對檔名進行截斷,最終輸出的檔名會變為 z126.txt,檔案也就不會出現目錄穿越的現象。因此目錄穿越不是發生在這裡,讓struts2自己背這個鍋多少有點冤。body中的資料組裝完畢是下面這樣,size等於3,依舊無誤:
HttpParameters
來到HttpParameters檢視接收的引數,還是upload、contentType、fileName三個:
但是引數接收完後就不正常了,除了原本的UploadFileName(注意首字母是大寫),還多了一個uploadFileName(注意首字母是小寫),size也變成了4。這就是Struts2官方所解釋的大小寫敏感,即對大寫的Upload和小寫的upload分別做處理。從這裡開始,S2-066才露出真正面目:
HttpParameters實現了Map介面,所以本質上它還是一個map,這也是組裝引數最常用的方式。但不管是HashMap還是TreeMap,自己不會出現覆蓋的問題。用一個小實驗證明:
走到這裡,HttpParameters對引數的處理開始出現異常,但依然沒有發生覆蓋。
UploadAction
終於到Action了。引用一段struts2官方的描述:
An attacker can manipulate file upload params to enable paths traversal and under some circumstances this can lead to uploading a malicious file which can be used to perform Remote Code Execution.
透過操控上傳引數,駭客能夠出發目錄穿越漏洞,這樣一來,在某些情況下可以上傳惡意檔案,從而進行RCE。換種說法,S2-066是框架自身、軟體工程師、Java反射機制共同作用的結果。走到這裡,為了簡化程式碼,UploadAction即是Action又是Entity。而在Entity的例項化過程中,必然是透過setXX屬性來賦值。所以就有了setUploadFileName(注意首字母大寫)和setuploadFileName(注意首字母小寫)的需求 。而在Entity的setter與getter中,這兩種需求都會被當做一種,即setUploadFileName,因此覆蓋也就發生了。正常情況下例項化方法只走一遍,如contentType:
而setUploadFileName第一遍是z106.txt:
第二遍是../../z127.txt:
例項化完成後,uploadFileName屬性已被覆蓋:
檢視物理路徑,上傳成功:
POCs
引數不是filename結尾,失敗:
引數不符合FileName大小寫要求,失敗:
大寫覆蓋小寫失敗:
大寫覆蓋大寫失敗:
小寫覆蓋小寫失敗:
小寫覆蓋大寫成功,文章開頭所用。另外覆蓋也可以放在body中:
驗證:
利用條件多少有點苛刻,但殺傷力不輸struts2過去那一堆,CVSS3.0評分9.8,CRITICAL。
更多網安技能的線上實操練習,請點選這裡>>