S2-032 遠端程式碼執行漏洞檢測與利用
什麼是STRUTS 2漏洞?
Struts 是Apache軟體基金會(ASF)贊助的一個開源專案,通過採用JavaServlet/JSP技術,實現基於Java EEWeb應用的MVC設計模式的應用框架,Struts 2是Struts的下一代產品,是在 Struts 1和WebWork的技術基礎上進行了合併的全新的Struts 2框架。
Struts2框架廣泛應用於政府、公安、交通、金融行業和運營商的網站建設,作為網站開發的底層模板使用。Struts2漏洞,主要指的是J2EE開源框架struts2出現的命令執行漏洞,危害巨大,可導致遠端執行任意系統命令,進而獲取系統控制權,資料庫控制權,導致資訊洩露。所有使用struts2框架開發的系統都會受到影響。
STRUTS 2攻防對抗歷史簡要回顧。
Struts2的程式碼執行問題最早要追溯到2010年,當時來自Google Security Team的Meder Kydyraliev發現可以通過用unicode編碼的形式繞過引數攔截器對特殊字元“#”的過濾,造成程式碼執行問題,官方漏洞編號S2-003,這也是最早的存在記錄的一個struts2遠端程式碼執行漏洞。對於第一個出現的struts2遠端程式碼執行漏洞官方當時並沒有意識到這已經打開了潘多拉的魔盒,通過傳遞非法引數繞過過濾呼叫OGNL表示式,這也就是後來多數的struts遠端程式碼執行漏洞的利用流程。對於S2-003官方只是簡單的用正則將含有”\u0023”的請求全部過濾掉,由於”\u0023”在傳遞的過程中被轉義為”\\u0023”所以正則根本沒有匹配上。導致漏洞的第一次修補實際上失敗了。
OGNL表示式可以呼叫java的靜態方法,開發者後來也意識到命令執行的危害。後來OGNL上下文中一些名稱空間中的屬性,比如將#_memberAccess.allowStaticMethodAccess設定為true,#context["xwork.MethodAccessor.denyMethodExecution"]設定為false。但是通過unicde編碼繞過過濾規則的問題依然存在。比如將”\u0023”換成八進位制的“\43”,即可繞過官方當時的修復。
後來官方終於意識到了問題的嚴重性在過濾的時候更加嚴謹的改寫了正則,過濾掉了出現\, @等字元的請求內容,官方修改的正則表示式,如圖所示。
修補後的正則雖然更為嚴謹但是問題依然存在。大概是在11年Google Security Team的一位成員又提出了新的利用思路 (CVE-2011-3923),藉助action例項中的私有變數的set方法執行OGNL呼叫java靜態方法執行任意命令。對於這個CVE的修復官方依然是通過正則過濾的方式來修復此問題,官方修復如圖所示。
由於struts2框架底層是利用OGNL表示式實現的。官方為了防止在OGNL表示式中直接呼叫java靜態方法,它在OGNL上下文中內建了幾個命名物件。例如,#_memberAccess["allowStaticMethodAccess"]預設被設定為false,#context["xwork.MethodAccessor.denyMethodExecution"]預設被設定為true。
但是上面提到的這幾個屬性的值可以利用執行OGNL進行修改,修改相關屬性之後,又可以直接呼叫java靜態方法。
- 13年的時候,在S2-013修補中,#_memberAccess["allowStaticMethodAccess"]的屬性,被設定為沒有許可權被修改。這樣看似從根本上解決了問題。但是利用java反射類來訪問私有成員變數的方式依然可以繞過達到直接修改前面那兩個屬性。
- 此外java.lang.ProcessBuilder這個類,new一個例項然後呼叫start()方法,便達到命令執行的目的。也可以繞過apache設定的限制。
- 此後出現了S2-016,這個漏洞是DefaultActionMapper類支援以”action:”、”redirect:”、”redirectAction:”作為導航或是重定向字首,但是這些字首後面同時可以跟OGNL表示式,由於struts2沒有對這些字首做過濾,又導致命令執行。
此後官方對於S2-020的修復依然是頭痛醫頭腳痛醫腳,使用正則表示式來過濾使用者請求。依然導致了大量的繞過。
Struts2歷屆的漏洞補丁頁面可以從這個頁面檢視到。
https://struts.apache.org/docs/security-bulletins.html
s2-032漏洞技術分析
此次漏洞存在於struts2的動態方法引用功能。只要在struts2配置檔案中開啟該功能,就可能被利用。
<constant name=”struts.enable.DynamicMethodInvocation” value=”true” />
如果我們請求http://localhost/index.action?method:OGNL的情況下,請求的OGNL表示式會被執行,造成命令執行。
method後面跟的方法名會被struts2進行解析,程式碼位於DefaultActionMapper.java中。
可以看到
mapping.setMethod(key.substring(METHOD_PREFIX.length()));
將我們傳入的方法加入到map中。然後在DefaultActionInvocation.java中,被invokeAction引用。
invokeAction首先獲取方法字串,然後呼叫ognlUtil.getValue來執行方法並獲取方法結果。問題就出在傳給ognl的methodName沒有進行嚴格的過濾,尤其是沒有過濾ognl關鍵字,從而造成命令執行。
通過對比2.3.28和2.3.28.1,可以發現修補的位置:
在進行方法新增的時候,對傳進來的方法進行了過濾,呼叫了cleanupActionName。cleanupActionName的定義如下:
其中allowdActionNames定義是
protected Pattern allowedActionNames = Pattern.compile(“[a-zA-Z0-9._!/\\-]*”);
所以程式碼的意思是去除一切不在上述範圍的字元。
最後的漏洞利用exp格式舉例
還可以用如下的指令碼程式碼進行網址漏洞的批量檢測。