Translated ['src/pentesting-web/deserialization/basic-java-deserializati

This commit is contained in:
Translator 2025-07-28 18:12:58 +00:00
parent 7d8a9504c1
commit 7c267dfe3e

View File

@ -1,15 +1,27 @@
# Basic Java Deserialization with ObjectInputStream readObject
{{#include ../../banners/hacktricks-training.md}}
У цьому POST буде пояснено приклад використання `java.io.Serializable`.
У цьому POST буде пояснено приклад використання `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).
### Нагадування: Які методи імпліцитно викликаються під час десеріалізації?
1. `readObject()` специфічна для класу логіка читання (якщо реалізована та *приватна*).
2. `readResolve()` може замінити десеріалізований об'єкт на інший.
3. `validateObject()` через зворотні виклики `ObjectInputValidation`.
4. `readExternal()` для класів, що реалізують `Externalizable`.
5. Конструктори **не** виконуються тому ланцюги гаджетів покладаються виключно на попередні зворотні виклики.
Будь-який метод у цьому ланцюзі, який в кінцевому підсумку викликає дані, контрольовані атакуючим (виконання команд, запити JNDI, рефлексія тощо), перетворює рутину десеріалізації на гаджет RCE.
Давайте розглянемо приклад з **класом Person**, який є **серіалізованим**. Цей клас **перезаписує функцію readObject**, тому коли **будь-який об'єкт** цього **класу** буде **десеріалізований**, ця **функція** буде **виконана**.\
У прикладі **функція readObject** класу Person викликає функцію `eat()` його домашньої тварини, а функція `eat()` собаки (з якоїсь причини) викликає **calc.exe**. **Ми побачимо, як серіалізувати та десеріалізувати об'єкт Person, щоб виконати цей калькулятор:**
У прикладі функція **readObject** класу Person викликає функцію `eat()` його домашньої тварини, а функція `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)**
**Наступний приклад взято з <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()** **викликає інший код, контрольований атакуючим**. У реальних ланцюгах гаджетів тисячі класів, що містяться в зовнішніх бібліотеках (Commons-Collections, Spring, Groovy, Rome, SnakeYAML тощо), можуть бути зловживані атакуючому потрібен лише *один* досяжний гаджет для отримання виконання коду.
---
## 2023-2025: Що нового в атаках десеріалізації Java?
* 2023 CVE-2023-34040: десеріалізація заголовків записів помилок Spring-Kafka, коли увімкнені прапорці `checkDeserExWhen*`, дозволила довільне створення гаджетів з тем, опублікованих атакуючими. Виправлено в 3.0.10 / 2.9.11. ¹
* 2023 CVE-2023-36480: порушено припущення про довірений сервер Java-клієнта Aerospike зловмисні відповіді сервера містили серіалізовані корисні навантаження, які були десеріалізовані клієнтом → RCE. ²
* 2023 CVE-2023-25581: парсинг атрибутів профілю користувача `pac4j-core` приймав Base64 блохи з префіксом `{#sb64}` і десеріалізував їх, незважаючи на `RestrictedObjectInputStream`. Оновлення ≥ 4.0.0.
* 2023 CVE-2023-4528: JSCAPE MFT Manager Service (порт 10880) приймав XML-кодовані Java-об'єкти, що призводило до RCE як root/SYSTEM.
* 2024 до 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-inspector` або `serialpwn-cli` у вашій CI, щоб зупинити збірку, якщо небезпечний гаджет стає досяжним.
## Оновлений чек-лист інструментів (2024)
* `ysoserial-plus.jar` спільний форк з > 130 ланцюгами гаджетів:
```bash
java -jar ysoserial-plus.jar CommonsCollections6 'calc' | base64 -w0
```
* `marshalsec` все ще еталон для генерації гаджетів JNDI (LDAP/RMI).
* `gadget-probe` швидке виявлення гаджетів чорного ящика проти мережевих сервісів.
* `SerialSniffer` агент JVMTI, який друкує кожен клас, прочитаний `ObjectInputStream` (корисно для створення фільтрів).
* **Порада з виявлення** увімкніть `-Djdk.serialDebug=true` (JDK 22+), щоб записувати рішення фільтра та відхилені класи.
## Швидкий чек-лист для безпечних реалізацій `readObject()`
1. Зробіть метод `private` і додайте анотацію `@Serial` (допомагає статичному аналізу).
2. Ніколи не викликайте методи, надані користувачем, або не виконуйте I/O у методі лише читайте поля.
3. Якщо потрібна валідація, виконуйте її **після** десеріалізації, поза `readObject()`.
4. Віддавайте перевагу реалізації `Externalizable` і виконуйте явні читання полів замість стандартної серіалізації.
5. Зареєструйте посилений `ObjectInputFilter` навіть для внутрішніх сервісів (дизайн, стійкий до компрометацій).
## Посилання
1. Консультація з безпеки Spring CVE-2023-34040 Десеріалізація Java в Spring-Kafka (серпень 2023)
2. GitHub Security Lab GHSL-2023-044: Небезпечна десеріалізація в Java-клієнті Aerospike (липень 2023)
{{#include ../../banners/hacktricks-training.md}}