S2-066漏洞分析與復現(CVE-2023-50164)

合天网安实验室發表於2024-03-20

Foreword

自struts2官方紕漏S2-066漏洞已經有一段時間,期間斷斷續續地寫,直到最近才完成。羞愧地回顧一下官方通告:

image.png

2023.12.9釋出,編號CVE-2023-50164,主要影響版本是 2.5.0-2.5.32 以及 6.0.0-6.3.0,描述中提到了檔案上傳漏洞和目錄穿越漏洞。開始以為這是個組合漏洞,其實不是,這是一個漏洞,看了幾篇大佬的文章,有的把它稱為“檔案上傳目錄穿越漏洞”,也有道理。

Prepare

準備工作就是搭建專案,用Tomcat跑,除錯好斷點,回顧下struts2的結構。篇幅有限,這裡只貼一張struts2自身的配置檔案:

image.png

struts2有眾多的Filter和Intercepter,它的配置邏輯是,除檔案中定義的class、package以外,其餘全部攔截。對於S2-066,處理一個請求要經過的幾個關鍵類包括Dispatcher、Interceptor、HttpParameters以及UploadAction。使用下面的poc:

cda10716c5804ad293daf6ae5c7ed9a.png

Dispatcher

請求首先會進入著名的Dispatcher。multi引數對應的就是body資料,包含upload、fileName、contentType三個變數,無誤:

image.png

request中還有一個引數,uploadFileName=../../z127.txt,這個是汙染引數,檔案上傳的目的地,也是利用這個漏洞的目標。

image.png

走到這裡Dispatcher只是簡單處理一下請求然後交給Interceptor,無異常。

【----幫助網安學習,以下所有學習資料免費領!加vx:dctintin,備註 “部落格園” 獲取!】

 ① 網安學習成長路徑思維導圖
 ② 60+網安經典常用工具包
 ③ 100+SRC漏洞分析報告
 ④ 150+網安攻防實戰技術電子書
 ⑤ 最權威CISSP 認證考試指南+題庫
 ⑥ 超1800頁CTF實戰技巧手冊
 ⑦ 最新網安大廠面試題合集(含答案)
 ⑧ APP客戶端安全檢測指南(安卓+IOS)

FileUploadInterceptor

攔截器先是把request包裝了一下,型別是MultiPartRequestWrapper。

image.png

這裡與Dispatcher一樣,請求引數還是multi和request兩部分,也無異常。

image.png

並且遍歷只有一次 ,因為真正的body只有一個,就是那個multi。

image.png

在遍歷過程中struts2還出現了硬編碼現象,要求檔名引數必須以FileName結尾,且拼接完成的檔名字首就是body中的{upload}名稱,這就給exp帶來了一定限制:

image.png

此外,注意這裡的279行:

// get the name of the file from the input tag
String[] fileName = multiWrapper.getFileNames(inputName);

使用的是MultiPartRequest介面的方法,而這個介面在S2-066中是由JakartaMultiPartRequest實現。使用下面這個poc進行目錄穿越並斷點檢測一下:

image.png

發現目錄穿越失敗,檔案沒有放在指定目錄下。分析原始碼:

image.png

引數覆蓋原本想用../../z126.txt,方法的輸入引數確實也是這樣接收的,但在這個方法中struts2會對檔名進行截斷,最終輸出的檔名會變為 z126.txt,檔案也就不會出現目錄穿越的現象。因此目錄穿越不是發生在這裡,讓struts2自己背這個鍋多少有點冤。body中的資料組裝完畢是下面這樣,size等於3,依舊無誤:

image.png

HttpParameters

來到HttpParameters檢視接收的引數,還是upload、contentType、fileName三個:

image.png

但是引數接收完後就不正常了,除了原本的UploadFileName(注意首字母是大寫),還多了一個uploadFileName(注意首字母是小寫),size也變成了4。這就是Struts2官方所解釋的大小寫敏感,即對大寫的Upload和小寫的upload分別做處理。從這裡開始,S2-066才露出真正面目:

image.png

HttpParameters實現了Map介面,所以本質上它還是一個map,這也是組裝引數最常用的方式。但不管是HashMap還是TreeMap,自己不會出現覆蓋的問題。用一個小實驗證明:

image.png

走到這裡,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:

image.png

而setUploadFileName第一遍是z106.txt:

image.png

第二遍是../../z127.txt:

image-20240319143952102

例項化完成後,uploadFileName屬性已被覆蓋:

image.png

檢視物理路徑,上傳成功:

image.png

POCs

引數不是filename結尾,失敗:

5e69d76ba4a0c6e60e5fd410a53f9a1.png

引數不符合FileName大小寫要求,失敗:

674bb93cd91ae101fbb3cf77c13629c.png

大寫覆蓋小寫失敗:

ab7ee0ad28107ef1a8569beca2091a2.png

大寫覆蓋大寫失敗:

31b60ab46f1b93332c0cd5ecc3ce11f.png

小寫覆蓋小寫失敗:

9a5feb70097b8fd64c922676ab5cc17.png

小寫覆蓋大寫成功,文章開頭所用。另外覆蓋也可以放在body中:

61f40e5da8a7488a8156c1e8897df3f.png

驗證:

8da6d33b7bf1dc08e644433d86e0dde.png

利用條件多少有點苛刻,但殺傷力不輸struts2過去那一堆,CVSS3.0評分9.8,CRITICAL。

capture_20240114164504492.bmp

更多網安技能的線上實操練習,請點選這裡>>

相關文章