CVE-2020-2551: Unauthenticated Remote Code Execution in IIOP protocol via Malicious JNDI Lookup

Boik Su
5 min readMar 15, 2020

--

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.

exp.java

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.

Taken from http://blog.neoit.my/java/java-rmi-simple-server-example-using-terminal/

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.

This figure shows the relationships for objects that use IIOP between a client and WebLogic Server

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.

Follow all the way down…
Just like how the CORBA initiates the ORB and gets the ref of Naming Service

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)
...
Step 1 ~ 3
Step 4 ~ 6
Step 7 ~ 8
Step 9: JNDI Injection

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.

Operation timed out due to internal IP

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.

Change the socket’s address to the one accessible from the outside

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.trustURLCodebasehas 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.

Reference

--

--