mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/pentesting-web/deserialization/basic-java-deserializati
This commit is contained in:
parent
2daddbb8e5
commit
e7b7c0a463
@ -1,15 +1,27 @@
|
||||
# Basic Java Deserialization with ObjectInputStream readObject
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
在这篇文章中,将解释一个使用 `java.io.Serializable` 的示例。
|
||||
在这篇文章中,将解释一个使用 `java.io.Serializable` 的示例**以及为什么重写 `readObject()` 在输入流由攻击者控制时可能极其危险**。
|
||||
|
||||
# Serializable
|
||||
## Serializable
|
||||
|
||||
Java `Serializable` 接口(`java.io.Serializable` 是一个标记接口,您的类必须实现它才能被 **序列化** 和 **反序列化**。Java 对象序列化(写入)是通过 [ObjectOutputStream](http://tutorials.jenkov.com/java-io/objectoutputstream.html) 完成的,反序列化(读取)是通过 [ObjectInputStream](http://tutorials.jenkov.com/java-io/objectinputstream.html) 完成的。
|
||||
Java `Serializable` 接口 (`java.io.Serializable`) 是一个标记接口,您的类必须实现它才能被 **序列化** 和 **反序列化**。Java 对象序列化(写入)是通过 [`ObjectOutputStream`](http://tutorials.jenkov.com/java-io/objectoutputstream.html) 完成的,反序列化(读取)是通过 [`ObjectInputStream`](http://tutorials.jenkov.com/java-io/objectinputstream.html) 完成的。
|
||||
|
||||
让我们看一个 **可序列化的类 Person** 的示例。这个类 **重写了 readObject** 函数,因此当 **这个类的任何对象** 被 **反序列化** 时,这个 **函数** 将被 **执行**。\
|
||||
在这个示例中,类 Person 的 **readObject 函数** 调用了它的宠物的 `eat()` 函数,而 Dog 的 `eat()` 函数(出于某种原因)调用了 **calc.exe**。 **我们将看到如何序列化和反序列化一个 Person 对象以执行这个计算器:**
|
||||
### 提醒:在反序列化过程中哪些方法会被隐式调用?
|
||||
|
||||
**以下示例来自 [https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649](https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649)**
|
||||
1. `readObject()` – 类特定的读取逻辑(如果实现且为 *private*)。
|
||||
2. `readResolve()` – 可以用另一个对象替换反序列化的对象。
|
||||
3. `validateObject()` – 通过 `ObjectInputValidation` 回调。
|
||||
4. `readExternal()` – 对于实现 `Externalizable` 的类。
|
||||
5. 构造函数 **不会** 被执行 – 因此小工具链完全依赖于之前的回调。
|
||||
|
||||
在该链中的任何方法如果最终调用了攻击者控制的数据(命令执行、JNDI 查找、反射等),将使反序列化过程变成 RCE 小工具。
|
||||
|
||||
让我们看一个 **Person 类** 的示例,该类是 **可序列化的**。这个类 **重写了 readObject** 函数,因此当 **该类的任何对象** 被 **反序列化** 时,这个 **函数** 将被 **执行**。\
|
||||
在这个示例中,Person 类的 **readObject** 函数调用了它的宠物的 `eat()` 函数,而 Dog 的 `eat()` 函数(出于某种原因)调用了 **calc.exe**。 **我们将看到如何序列化和反序列化一个 Person 对象以执行这个计算器:**
|
||||
|
||||
**以下示例来自 <https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649>**
|
||||
```java
|
||||
import java.io.Serializable;
|
||||
import java.io.*;
|
||||
@ -80,8 +92,63 @@ payloadTest("test.ser");
|
||||
}
|
||||
}
|
||||
```
|
||||
## 结论
|
||||
### 结论(经典场景)
|
||||
|
||||
正如您在这个非常基本的示例中所看到的,这里的“漏洞”出现是因为 **readObject** 函数 **调用了其他易受攻击的函数**。
|
||||
正如您在这个非常基本的例子中所看到的,这里的“漏洞”出现是因为 **readObject()** 方法 **调用了其他攻击者控制的代码**。在现实世界的 gadget 链中,成千上万的类包含在外部库中(Commons-Collections、Spring、Groovy、Rome、SnakeYAML 等)可以被滥用——攻击者只需要 *一个* 可达的 gadget 就能获得代码执行。
|
||||
|
||||
---
|
||||
|
||||
## 2023-2025:Java 反序列化攻击的新动态
|
||||
|
||||
* 2023 – CVE-2023-34040:当 `checkDeserExWhen*` 标志启用时,Spring-Kafka 反序列化错误记录头允许从攻击者发布的主题构造任意 gadget。已在 3.0.10 / 2.9.11 中修复。¹
|
||||
* 2023 – CVE-2023-36480:Aerospike Java 客户端的受信任服务器假设被破坏——恶意服务器回复包含被客户端 反序列化 的序列化有效负载 → RCE。²
|
||||
* 2023 – CVE-2023-25581:`pac4j-core` 用户配置文件属性解析接受 `{#sb64}` 前缀的 Base64 blob,并在 `RestrictedObjectInputStream` 的情况下反序列化它们。升级 ≥ 4.0.0。
|
||||
* 2023 – CVE-2023-4528:JSCAPE MFT Manager Service(端口 10880)接受 XML 编码的 Java 对象,导致以 root/SYSTEM 身份的 RCE。
|
||||
* 2024 – 多个新的 gadget 链被添加到 ysoserial-plus(mod),包括 Hibernate5、TomcatEmbed 和 SnakeYAML 2.x 类,绕过了一些旧的过滤器。
|
||||
|
||||
## 现代缓解措施
|
||||
|
||||
1. **JEP 290 / 序列化过滤(Java 9+)**
|
||||
*添加类的允许列表或拒绝列表:*
|
||||
```bash
|
||||
# 仅接受您的 DTO 和 java.base,拒绝其他所有内容
|
||||
-Djdk.serialFilter="com.example.dto.*;java.base/*;!*"
|
||||
```
|
||||
编程示例:
|
||||
```java
|
||||
var filter = ObjectInputFilter.Config.createFilter("com.example.dto.*;java.base/*;!*" );
|
||||
ObjectInputFilter.Config.setSerialFilter(filter);
|
||||
```
|
||||
2. **JEP 415(Java 17+)上下文特定过滤器工厂** – 使用 `BinaryOperator<ObjectInputFilter>` 根据执行上下文(例如,每个 RMI 调用、每个消息队列消费者)应用不同的过滤器。
|
||||
3. **不要通过网络暴露原始 `ObjectInputStream`** – 更倾向于没有代码执行语义的 JSON/二进制编码(Jackson 在禁用 `DefaultTyping` 后,Protobuf、Avro 等)。
|
||||
4. **深度防御限制** – 设置最大数组长度、深度、引用:
|
||||
```bash
|
||||
-Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000"
|
||||
```
|
||||
5. **持续的 gadget 扫描** – 在您的 CI 中运行工具,如 `gadget-inspector` 或 `serialpwn-cli`,如果危险的 gadget 变得可达则失败构建。
|
||||
|
||||
## 更新的工具备忘单(2024)
|
||||
|
||||
* `ysoserial-plus.jar` – 社区分支,包含 > 130 个 gadget 链:
|
||||
```bash
|
||||
java -jar ysoserial-plus.jar CommonsCollections6 'calc' | base64 -w0
|
||||
```
|
||||
* `marshalsec` – 仍然是 JNDI gadget 生成的参考(LDAP/RMI)。
|
||||
* `gadget-probe` – 针对网络服务的快速黑盒 gadget 发现。
|
||||
* `SerialSniffer` – JVMTI 代理,打印每个被 `ObjectInputStream` 读取的类(有助于制作过滤器)。
|
||||
* **检测提示** – 启用 `-Djdk.serialDebug=true`(JDK 22+)以记录过滤器决策和被拒绝的类。
|
||||
|
||||
## 安全 `readObject()` 实现的快速检查清单
|
||||
|
||||
1. 将方法设为 `private` 并添加 `@Serial` 注解(有助于静态分析)。
|
||||
2. 切勿在方法中调用用户提供的方法或执行 I/O – 仅读取字段。
|
||||
3. 如果需要验证,请在反序列化 **后** 执行,位于 `readObject()` 之外。
|
||||
4. 更倾向于实现 `Externalizable` 并显式读取字段,而不是默认序列化。
|
||||
5. 即使对于内部服务,也要注册一个强化的 `ObjectInputFilter`(抗妥协设计)。
|
||||
|
||||
## 参考文献
|
||||
|
||||
1. Spring Security Advisory – CVE-2023-34040 Java 反序列化在 Spring-Kafka 中(2023年8月)
|
||||
2. GitHub Security Lab – GHSL-2023-044:Aerospike Java 客户端中的不安全反序列化(2023年7月)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user