CVE-2020-2551: Unauthenticated Remote Code Execution in IIOP protocol via Malicious JNDI Lookup
TL;DR
According to the advisory, this bug is triggered via the processing of IIOP protocol. Therefore, thanks to some preliminary analyses (listed below under References), the PoC is easy to build and the RCE is pretty straightforward.
Background
Before stepping into the culprit, we first need to have a brief understanding of what RMI, CORBA, IIOP, and RMI-IIOP are.
RMI
Remote Method Invocation: Starting with JDK 1.1, this API allows developers to invoke a method from a remote server like it’s on the local side. There’re some prerequisites to be fulfilled on both sides.
CORBA
Common Object Request Broker Architecture: A standard defined by the Object Management Group (OMG) designed to facilitate the communication of systems that are deployed on diverse platforms.
CORBA uses an interface definition language (IDL) to specify the interfaces that an object present to the outer world. CORBA then specifies a mapping from IDL to a specific implementation language like C++ or Java.
IIOP
IIOP is CORBA’s communication protocol using TCP/IP as the transport. It specifies a standard for client and server communication.
RMI-IIOP
Previously Java programmers had to choose between RMI and CORBA/IIOP (Java IDL) for distributed programming solutions. However, RMI-IIOP combines RMI-style ease of use with CORBA cross-language interoperability.
Analysis
weblogic.jndi.WLInitialContextFactory
Due to the culprit of this bug, the Context Factory is really the key point so as to initiate a normal IIOP request containing an adversarial data and trigger the bug.
If we go all the way down along the initialization of this Factory, we will know that it’s actually doing almost the same things as using CORBA library.
As a result, we have a way to trigger this vulnerability right now. Then…What does JtaTransactionManager
do here?
com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager
Actually, it’s another key point regarding this vulnerability. Do you remember that the description says that the RCE is achieved via Malicious JNDI Lookup? That’s the point! JtaTransactionManager
plays the role of a JNDI Injection trigger in CVE-2018-3191, and we use it here again since WebLogic doesn’t blacklist it! (Further readings: )
Deserialization Process
The stack frame:
lookupUserTransaction:565, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
initUserTransactionAndTransactionManager:444, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
readObject:1198, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta) <-- JtaTransactionManager#readObject
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
readObject:314, ObjectStreamClass (weblogic.utils.io) <-- ObjectStreamClass#readObject
readValueData:281, ValueHandlerImpl (weblogic.corba.utils)
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:2128, IIOPInputStream (weblogic.iiop) <-- JtaTransactionManager
read_value:1936, IIOPInputStream (weblogic.iiop)
...
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:2128, IIOPInputStream (weblogic.iiop) <-- Proxy
read_value:1936, IIOPInputStream (weblogic.iiop)
read_value_internal:220, AnyImpl (weblogic.corba.idl)
read_value:115, AnyImpl (weblogic.corba.idl)
read_any:1648, IIOPInputStream (weblogic.iiop) <-- bind_any()
read_any:1641, IIOPInputStream (weblogic.iiop)
_invoke:58, _NamingContextAnyImplBase (weblogic.corba.cos.naming)
invoke:249, CorbaServerRef (weblogic.corba.idl)
invoke:230, ClusterableServerRef (weblogic.rmi.cluster)
run:522, BasicServerRef$1 (weblogic.rmi.internal)
doAs:363, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:146, SecurityManager (weblogic.security.service)
handleRequest:518, BasicServerRef (weblogic.rmi.internal)
...
Q & A
NAT Problem
It’s common that the internal IP assigned by NAT is not accessible from the external side. However, WebLogic servers are often installed and run behind the NAT firewall. If we try to exploit them with the provided PoC, the timeout will be thrown out.
Therefore, amending responding IP is a necessary step for making the PoC work expectedly. One can modify it by modifying the raw packet or intercept and amend it in the library.
JDK Version
The payload heavily depends on what version of JDK or WebLogic is running with. Basically, using LDAP protocol is the best gadget for exploiting JNDI Injection before October 2018. However, starting with Oracle JDK 11.0.1, 8u191, 7u201, and 6u211, things have changed. One of the properties com.sun.jndi.ldap.object.trustURLCodebase
has defaulted to false, and this bypass has been assigned to CVE-2018–3149.
Therefore, the first trial still goes to LDAP, undoubtedly. If we bump into a newer version of JDK, we could try to exploit it with tricks listed here, however.