Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-33701-Unsafe-Deserialization-in-OpenTelemetry-Java-Agent-RMI-Instrumentation — Technical analysis of CVE-2026-33701, an unsafe deserialization vulnerability in OpenTelemetry Java Agent RMI instrumentation, including exploit conditions, patch details, and mitigation strategies. | Kitploit
Tools/GitHubGitHub/pl4tyz/cve-2026-33701-unsafe-deserialization-in-opentelemetry-java-agent-rmi-instrumentation
Vulnerability AnalysisExploitationWeb SecurityLearning & Education
GitHubpl4tyz/cve-2026-33701-unsafe-deserialization-in-opentelemetry-java-agent-rmi-instrumentation

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

Technical analysis of CVE-2026-33701, an unsafe deserialization vulnerability in OpenTelemetry Java Agent RMI instrumentation, including exploit conditions, patch details, and mitigation strategies.

View Repository
4 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

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

Severity: Critical (CVSS v4.0: 9.3)
Affected versions: opentelemetry-javaagent < 2.26.1
Patched in: 2.26.1 (released March 23, 2026)


Overview

OpenTelemetry is probably the most widely adopted observability framework in the Java ecosystem right now. The Java agent specifically is used by thousands of production services to automatically instrument applications for tracing, metrics and logging without requiring any code changes from the developer. You just attach it as a javaagent flag and it does everything behind the scenes.

What makes CVE-2026-33701 interesting isnt just the vulnerability itself, its the nature of how it got introduced. The agent quietly registers a custom RMI endpoint as a side effect of its RMI instrumentation. Developers dont know its there. Its not in any application code. Its not something they configured. The agent put it there automatically, and for versions below 2.26.1 that endpoint was deserializing incoming data with absolutely no filters applied.

If the application already had an RMI or JMX port exposed, an attacker with network access to that port could send a crafted serialized payload to the agents custom endpoint and potentially achieve remote code execution with the privileges of the JVM process. The catch is that RCE requires a compatible gadget chain to be present on the application classpath. More on that later.


How the Agent Instruments RMI

When the OTel Java agent attaches to a JVM it instruments RMI calls for context propagation. The idea is that when an RMI client calls a remote method, the agent needs to propagate the current trace context to the server side so spans connect correctly across service boundaries.

To do this the agent registers its own custom RMI object using a hardcoded ObjID. In ContextPropagator.java:

root@kitploit:~
public static final ObjID CONTEXT_CALL_ID =
    new ObjID("io.opentelemetry.javaagent.context-call".hashCode());

This ObjID is how the agent identifies its own endpoint within the RMI runtime. When an instrumented RMI client connects to a server, it first checks if the server has this ObjID registered. If it does, it sends a context propagation payload to it. If not, it skips context propagation and just does the regular call.

The problem is that this endpoint is registered inside the JVM RMI runtime, meaning it shares the same transport as whatever RMI or JMX port the application has open. If the application has -Dcom.sun.management.jmxremote.port set, or if it exports its own RMI services, the agents endpoint is reachable on that same port by anyone who can connect to it.


The Vulnerable Code

The deserialization happens in ContextPayload.java. In versions below 2.26.1 the read() method looked like this:

root@kitploit:~
@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;
}

The @SuppressWarnings("BanSerializableRead") annotation with the comment // fine is actually kind of telling. Someone on the team flagged this as a concern at some point, the suppression was added, and the comment was meant to justify it. Clearly it wasnt fine.

oi.readObject() with no serialization filters is the classic unsafe deserialization pattern. Java serialization will happily instantiate any class on the classpath during deserialization, and if the attacker can control the serialized stream they can choose which classes get instantiated and in what order. Thats the basis of gadget chain attacks.

The code does check instanceof Map after deserialization, but by that point the damage is already done. The gadget chain executes during the readObject() call itself, before any type checking happens. The instanceof check is completely irrelevant from a security standpoint.


The Patch

The fix in 2.26.1 completely replaces the deserialization approach. Instead of serializing a Map object and calling readObject(), the new implementation manually reads the context entries as primitive types:

root@kitploit:~
@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);
}

And the write side was updated to match:

root@kitploit:~
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());
    }
}

This approach only reads primitive types (ints and UTF strings) from the stream. There is no object instantiation happening. No gadget chain can execute through readInt() or readUTF(). The fix is correct and complete.

There is also a second change worth noting. The ObjID used to identify the agents custom endpoint was versioned:

root@kitploit:~
// Before (2.26.0)
new ObjID("io.opentelemetry.javaagent.context-call".hashCode());

// After (2.26.1)
new ObjID("io.opentelemetry.javaagent.context-call-v2".hashCode());

This means a patched agent will not respond to the old ObjID at all. An attacker targeting the old endpoint would get no response from a patched server. This is a nice additional hardening measure on top of the deserialization fix, it effectively breaks any exploit that was built against the v1 endpoint even if somehow the deserialization was still reachable.


Exploit Conditions

All three of the following must be true simultaneously for this to be exploitable:

1. OpenTelemetry Java agent attached on JDK 16 or lower

This is the most important constraint. JDK 17 introduced significant changes to the RMI internals and enforces much stricter module encapsulation via the Java Platform Module System. Most gadget chains that work against Java deserialization rely on reflection to access internal JDK classes or to invoke methods that wouldnt normally be accessible. JDK 17 breaks those reflection paths by default because of strong encapsulation, which means the majority of known gadget chains simply dont work on JDK 17 and above.

On JDK 8, 11, and 16 however, reflection access is much more permissive and gadget chains work as expected. These JDK versions still represent a substantial portion of production Java deployments, especially in older enterprise environments.

2. An RMI or JMX port is network-reachable by the attacker

The application needs to have something like -Dcom.sun.management.jmxremote.port=9010 configured, or it needs to export its own RMI services. The OTel agents endpoint piggybacks on whatever RMI transport is already in use. If theres no RMI port open, the endpoint isnt reachable.

3. A gadget-chain-compatible library is present on the application classpath

The agent jar itself does not contain any gadget-chain-compatible classes. We confirmed this by inspecting the class list extracted from the 2.26.0 agent jar. The agent contains OTel instrumentation code, shaded API classes, semconv definitions and bootstrap infrastructure. None of the commonly exploited gadget sources like Commons Collections, Commons BeanUtils, or Spring Framework classes are bundled inside the agent.

This means the gadget chain has to come from the application. The developer has to have included a library that contains exploitable serialization gadgets, which means the exploitability varies depending on what the application depends on.


Why This Still Matters in Practice

The three conditions above might sound restrictive but they are actually quite common together in real enterprise Java environments. Consider a typical production scenario: a backend service running on JDK 11 or JDK 17 (with --add-opens flags, which are common in containerized deployments and can partially re-enable gadget chains), instrumented with the OTel agent for observability, with JMX enabled for operational monitoring, and a dependency tree rich enough to contain gadget-compatible classes.

The key insight is that developers trust observability agents as passive infrastructure. They expect the agent to observe, not to open new network-reachable endpoints. The attack surface created here is invisible from the application developers perspective. They didnt write any RMI code. They didnt configure any RMI endpoint. The agent did it silently as a consequence of instrumentation.


Gadget Chain Dependency

For further research on the gadget chain requirement, the ysoserial project (publicly available, widely referenced in academic and professional security research) documents several Java deserialization gadget chains that are relevant to this class of vulnerability. Chains like CommonsCollections, Spring1, and Spring2 are well documented examples of how existing library classes can be chained to achieve code execution through unsafe deserialization. Which specific chains are applicable in a given environment depends entirely on what libraries that application has on its classpath.


Remediation

Upgrade to opentelemetry-javaagent version 2.26.1 or later. This is the only complete fix.

If immediate upgrade isnt possible, the RMI instrumentation can be disabled entirely by adding the following system property to the JVM startup flags:

root@kitploit:~
-Dotel.instrumentation.rmi.enabled=false

This disables the instrumentation that registers the vulnerable endpoint, removing the attack surface entirely. Context propagation across RMI calls will not work while this flag is set, but that is acceptable as a temporary mitigation.

Independently of this CVE, if JMX is exposed on a network-reachable port without authentication it should be treated as a critical misconfiguration regardless of the OTel agent being present or not.


References

  • GitHub Security Advisory: GHSA-xw7x-h9fj-p2c7
  • Patch commit: open-telemetry/opentelemetry-java-instrumentation@9cf4fba
  • NVD entry: CVE-2026-33701
  • ysoserial (public gadget chain reference): https://github.com/frohoff/ysoserial
Download Tool