Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-33701-Unsafe-Deserialization-in-OpenTelemetry-Java-Agent-RMI-Instrumentation — Технический анализ CVE-2026-33701 — уязвимости небезопасной десериализации в RMI-инструментации OpenTelemetry Java Agent, включая условия эксплуатации, детали исправления и стратегии смягчения последствий. | Kitploit
Инструменты/GitHubGitHub/pl4tyz/cve-2026-33701-unsafe-deserialization-in-opentelemetry-java-agent-rmi-instrumentation
Анализ уязвимостейЭксплуатацияВеб-безопасностьОбучение и Образование
GitHubpl4tyz/cve-2026-33701-unsafe-deserialization-in-opentelemetry-java-agent-rmi-instrumentation

CVE-2026-33701-Unsafe-Deserialization-in-OpenTelemetry-Java-Agent-RMI-Instrumentation

Технический анализ CVE-2026-33701 — уязвимости небезопасной десериализации в RMI-инструментации OpenTelemetry Java Agent, включая условия эксплуатации, детали исправления и стратегии смягчения последствий.

Репозиторий
106 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2026-33701 — Небезопасная десериализация в RMI-инструментации Java-агента OpenTelemetry

Серьёзность: Критическая (CVSS v4.0: 9.3)
Затронутые версии: opentelemetry-javaagent < 2.26.1
Исправлено в: 2.26.1 (выпущено 23 марта 2026 года)


Обзор

OpenTelemetry — пожалуй, самый широко используемый фреймворк наблюдаемости в экосистеме Java на данный момент. Java-агент, в частности, используется тысячами продакшн-сервисов для автоматической инструментации приложений для трассировки, метрик и логирования без необходимости вносить какие-либо изменения в код со стороны разработчика. Вы просто подключаете его как флаг javaagent, и он делает всё за кулисами.

Что делает CVE-2026-33701 интересным — это не только сама уязвимость, но и природа того, как она была внедрена. Агент незаметно регистрирует собственную пользовательскую RMI-конечную точку как побочный эффект своей RMI-инструментации. Разработчики не знают, что она там есть. Её нет ни в каком коде приложения. Это не то, что они настраивали. Агент разместил её автоматически, и в версиях ниже 2.26.1 эта конечная точка десериализовала входящие данные без каких-либо применённых фильтров.

Если приложение уже имело открытый RMI- или JMX-порт, злоумышленник, имеющий сетевой доступ к этому порту, мог отправить специально сформированную сериализованную полезную нагрузку на пользовательскую конечную точку агента и потенциально добиться удалённого выполнения кода с привилегиями процесса JVM. Загвоздка в том, что RCE требует наличия совместимой цепочки гаджетов в classpath приложения. Подробнее об этом позже.


Как агент инструментирует RMI

Когда Java-агент OTel подключается к JVM, он инструментирует RMI-вызовы для распространения контекста. Идея в том, что когда RMI-клиент вызывает удалённый метод, агенту необходимо распространить текущий контекст трассировки на серверную сторону, чтобы спаны корректно связывались через границы сервисов.

Для этого агент регистрирует собственный пользовательский RMI-объект, используя жёстко заданный ObjID. В ContextPropagator.java:

public static final ObjID CONTEXT_CALL_ID =
    new ObjID("io.opentelemetry.javaagent.context-call".hashCode());

Этот ObjID — то, как агент идентифицирует свою собственную конечную точку в среде выполнения RMI. Когда инструментированный RMI-клиент подключается к серверу, он сначала проверяет, зарегистрирован ли этот ObjID на сервере. Если да, он отправляет на него полезную нагрузку распространения контекста. Если нет, он пропускает распространение контекста и просто выполняет обычный вызов.

Проблема в том, что эта конечная точка регистрируется внутри среды выполнения RMI JVM, а значит, использует тот же транспорт, что и любой RMI- или JMX-порт, открытый приложением. Если приложение имеет настроенный -Dcom.sun.management.jmxremote.port, или если оно экспортирует собственные RMI-сервисы, конечная точка агента доступна на том же порту для любого, кто может к нему подключиться.


Уязвимый код

Десериализация происходит в ContextPayload.java. В версиях ниже 2.26.1 метод read() выглядел так:

@SuppressWarnings("BanSerializableRead") // fine
public static ContextPayload read(ObjectInput oi) throws IOException {
    try {
        Object object = oi.readObject();
        if (object instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, String> map = (Map<String, String>) object;
            return new ContextPayload(map);
        }
    } catch (ClassCastException | ClassNotFoundException ex) {
        logger.log(FINE, "Error reading object", ex);
    }
    return null;
}

Аннотация @SuppressWarnings("BanSerializableRead") с комментарием // fine на самом деле довольно показательна. Кто-то в команде в какой-то момент отметил это как проблему, было добавлено подавление, и комментарий должен был это оправдать. Очевидно, что это не было нормально.

oi.readObject() без фильтров сериализации — это классический паттерн небезопасной десериализации. Сериализация Java с радостью создаст экземпляр любого класса в classpath во время десериализации, и если злоумышленник может контролировать сериализованный поток, он может выбрать, какие классы будут созданы и в каком порядке. Это основа атак с цепочками гаджетов.

Код действительно проверяет instanceof Map после десериализации, но к этому моменту ущерб уже нанесён. Цепочка гаджетов выполняется во время самого вызова readObject(), до того как происходит какая-либо проверка типов. Проверка instanceof полностью нерелевантна с точки зрения безопасности.


Исправление

Исправление в 2.26.1 полностью заменяет подход к десериализации. Вместо сериализации объекта Map и вызова readObject() новая реализация вручную считывает записи контекста как примитивные типы:

@Nullable
public static ContextPayload read(ObjectInput oi) throws IOException {
    int size = oi.readInt();
    if (size > MAX_CONTEXT_ENTRIES) {
        logger.log(
            FINE,
            "RMI context propagation payload size {0} exceeds maximum allowed of {1}, skipping context propagation.",
            new Object[] {size, MAX_CONTEXT_ENTRIES});
        return null;
    }
    Map<String, String> map = new HashMap<>();
    for (int i = 0; i < size; i++) {
        String key = oi.readUTF();
        String value = oi.readUTF();
        map.put(key, value);
    }
    return new ContextPayload(map);
}

И сторона записи была обновлена соответствующим образом:

public void write(ObjectOutput out) throws IOException {
    int size = context.size();
    if (size > MAX_CONTEXT_ENTRIES) {
        out.writeInt(0);
        return;
    }
    out.writeInt(size);
    for (Map.Entry<String, String> entry : context.entrySet()) {
        out.writeUTF(entry.getKey());
        out.writeUTF(entry.getValue());
    }
}

Этот подход считывает из потока только примитивные типы (целые числа и UTF-строки). Никакого создания экземпляров объектов не происходит. Ни одна цепочка гаджетов не может выполниться через readInt() или readUTF(). Исправление корректно и полно.

Есть также второе изменение, заслуживающее внимания. ObjID, используемый для идентификации пользовательской конечной точки агента, был версионирован:

// До (2.26.0)
new ObjID("io.opentelemetry.javaagent.context-call".hashCode());

// После (2.26.1)
new ObjID("io.opentelemetry.javaagent.context-call-v2".hashCode());

Это означает, что исправленный агент вообще не будет отвечать на старый ObjID. Злоумышленник, нацеленный на старую конечную точку, не получит ответа от исправленного сервера. Это хорошая дополнительная мера усиления защиты поверх исправления десериализации — она фактически ломает любой эксплойт, созданный против конечной точки v1, даже если бы десериализация каким-то образом всё ещё была доступна.


Условия эксплуатации

Для эксплуатации должны одновременно выполняться все три следующих условия:

1. Java-агент OpenTelemetry подключён на JDK 16 или ниже

Это самое важное ограничение. JDK 17 внёс значительные изменения во внутренности RMI и обеспечивает гораздо более строгую инкапсуляцию модулей через Java Platform Module System. Большинство цепочек гаджетов, работающих против десериализации Java, полагаются на рефлексию для доступа к внутренним классам JDK или для вызова методов, которые обычно недоступны. JDK 17 ломает эти пути рефлексии по умолчанию из-за строгой инкапсуляции, что означает, что большинство известных цепочек гаджетов просто не работают на JDK 17 и выше.

Однако на JDK 8, 11 и 16 доступ через рефлексию гораздо более разрешителен, и цепочки гаджетов работают как ожидается. Эти версии JDK по-прежнему представляют значительную часть продакшн-развёртываний Java, особенно в старых корпоративных средах.

2. RMI- или JMX-порт доступен злоумышленнику по сети

Скачать инструмент