Java代码审计之Struts2-003、005(三)

本系列文章将详细分析 Java 流行框架 Struts2 的历史漏洞,今后关于 Struts2 新的漏洞分析,也将更新于 Struts2-Vuln 项目上。该系列仅是笔者初学 Java代码审计 的一些记录,也希望能够帮助到想学习 Java代码审计 的朋友 。如有任何问题,欢迎 issue 。分析文章均来自 个人博客 ,转载请注明出处。

漏洞概要

Struts2-003是一个远程代码执行漏洞,Struts2-005为Struts2-003补丁绕过。

影响版本: Struts 2.0.0 - Struts 2.1.8.1 。更多详情可参考官方通告:https://cwiki.apache.org/confluence/display/WW/S2-003

漏洞环境

Struts2-003:Apache Tomcat/6.0.10+struts-2.0.8 Struts2-005:Apache Tomcat/6.0.10+struts-2.0.12

下载地址:http://archive.apache.org/dist/struts/binaries/struts-2.0.8-all.zip

https://archive.apache.org/dist/tomcat/tomcat-6/v6.0.10/bin/apache-tomcat-6.0.10.zip

漏洞分析

本次漏洞是通过绕过 ParameterInterceptor 拦截器的过滤,进而执行 OGNL 表达式,所以我们先在 ParameterInterceptor:doIntercept() 方法中下断点。 此时,存放 HTTP 数据的 parameters 变量将通过 setParameters 方法设置到值栈中,我们跟进该方法。

1

setParameters 方法会调用 acceptableName 方法判断参数是否含有非法字符,可以发现非法字符中包含 # 号,所以我们的 payload 需要将 # 号编码成 \u0023 。通过校验后,会调用 setValue 方法将值添加进值栈,我们继续跟进该方法。

2

setValue 方法中,会调用 compile 方法对字符串进行解析。

3

在解析字符串时,程序会对 \u 字符进行解码,具体处理代码在 JavaCharStream:readChar() 中。

4

经过 compile 方法解码后的字符串,又会回到 Ognl.setValue() ,表达式的解析执行也正是在此处。

5

我们来尝试弹个计算器:

1
login.action?('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('\u0023myret\u003d@java.lang.Runtime@getRuntime().exec(\'deepin-calculator\')')(bla)(bla)

6

漏洞修复

下图右边为官方修复后的代码(左图struts-2.0.8,右图为struts-2.0.12)。

7

8

我们会发现,修复代码主要引入控制静态方法调用开关 allowStaticMethodAccess 变量,以及用于控制成员变量的访问权限的 SecurityMemberAccess 类对象。然而通过 OGNL 表达式,我们完全可以控制这两个变量的值,这也导致 Struts2-003 补丁可以被绕过,即后来的 Struts2-005 漏洞。

1
2
3
# struts-005 poc

login.action?('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('\u0023_memberAccess.allowStaticMethodAccess\u003dtrue')(bla)(bla)&('\u0023_memberAccess.excludeProperties\u003d@java.util.Collections@EMPTY_SET')(bla)(bla)&('\u0023myret\u003d@java.lang.Runtime@getRuntime().exec(\'deepin-calculator\')')(bla)(bla)

9

这里有个小小的疑问,在 Struts2-005poc 中有这么一个设置:#_memberAccess.excludeProperties=@java.util.Collections@EMPTY_SET ,在调试代码时会发现 #_memberAccess.excludeProperties 的默认值为 dojo\..* (如下图)。

10

而这个正则一般不会匹配到我们的 poc ,也就是说其实我这里不设置#_memberAccess.excludeProperties=@java.util.Collections@EMPTY_SET 也可以。然而实际测试结果是,没有设置这个,后面的 java.lang.Runtime@getRuntime().exec 就不能执行成功,这里不懂是什么原因。

最后再来看下 Struts2-005 的修复代码,下图右边为官方修复后的代码(左图struts-2.0.12,右图为struts-2.2.1)。修复代码最终使用了更加严格的正则 [a-zA-Z0-9\\.\\]\\[\\(\\)_'\\s]+ 来校验参数名的合法性。

11

参考

Struts2基于OGNL的RCE漏洞全解析
191204-Ognl 使用实例手册
【Struts2-命令-代码执行漏洞分析系列】S2-003和S3-005
Struts2漏洞分析与研究之S2-005漏洞分析
Struts2漏洞系列之【S2-005】 参数名过滤不严谨导致代码执行

文章作者: Mochazz
文章链接: https://mochazz.github.io/2020/06/28/Java代码审计之Struts2-003/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Mochazz's blog