Java Deserialization Exploits: Registry Whitelist Bypass

Java Deserialization Exploits: Registry Whitelist Bypass

maio 25, 2020 | Adeline Zhang

In 2019, An Trinh discovered two vulnerabilities, CVE-2019-9670 (XXE/SSRF) and CVE-2019-6980 (deserialization vulnerability), in Zimbra.

As usual, An Trinh did not disclose any details.

Luckily, Hans Martin Munch is more generous than An Trinh and has shared many interesting ideas. For example, he once advised using YouDebug to fix the CVE-2017-3241 vulnerability.

ysoserial.payloads.JRMPClient is designed to trick a victim into accessing a malicious DGC server as a DGC client. When the victim deserialization comes from a malicious object of the DGC server, a filter is configured by default. For details, see the implementation of sun.rmi.transport.DGCImpl.checkInput().

A new idea proposed by An Trinh is to trick a victim into accessing a malicious RMI Registry server as an RMI Registry client. In this case, there is no filter involved if the victim deserialization comes from a malicious object of the RMI Registry server. No default filter is configured on JEP 290 for this scenario.

In this document, OpenJDK 8u232 is used for demo purposes.

(1) RMIRegistryServer.java

(2) EvilRMIRegistryClientWithUnicastRemoteObjectFail.java

Run the following command to start the malicious service:

Run the following command to start the victim’s machine:

Run the following command to start the attacker’s machine:

This attack fails to achieve the desired objective.

a. Failure Cause

References:

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/registry/RegistryImpl_Stub.java
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/server/MarshalOutputStream.java
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/transport/ObjectTable.java
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/transport/Target.java

If UnicastRemoteObject.exportObject() has been called, when the ObjectOutputStream.writeObject() serialization is called to output the UnicastRemoteObject instance, MarshalOutputStream.replaceObject() will be triggered to replace the UnicastRemoteObject instance with another object instance, thereby compromising the attack chain.

To avoid such replacement, reflection can be used to change the value of ObjectOutputStream.enableReplace from “true” to “false”. This is Hans Martin Munch’s advice.

b. Using YouDebug

According to endnote [3], YouDebug allows the execution of automatic debugging. Breakpoints and actions upon a breakpoint hit can be defined in the script in advance.

Edit ModifyRebind.ydb as follows:

The intention of the script is simply to block ObjectOutputStream.writeObject(). If the type of the object is UnicastRemoteObject, the value of ObjectOutputStream.enableReplace will be changed from “true” to “false”.

Run the following command to start the malicious service:

Run the following command to start the victim’s machine:

Run the following command to start the attacker’s machine:

(3) Customizing RegistryImpl_Stub.rebind()

Hans Martin Munch suggests the customization of RegistryImpl_Stub.rebind(), namely, modification of ObjectOutputStream.enableReplace via reflection before invoking writeObject(). He left this as homework, without providing the answer directly.

(4) Simplified Calling Relationships After a Successful Attack

References:

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/registry/RegistryImpl_Skel.java
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/server/UnicastRef.java
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/transport/tcp/TCPChannel.java
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/transport/StreamRemoteCall.java

Local attackers can successfully launch an attack against OpenJDK 8u232. However, remote attackers, when launching an attack against OpenJDK 8u232, cannot pass the source IP address check predefined in RegistryImpl_Skel:142.

As indicated on page 20 of endnote 1, the stack top of the invoked stack is as follows:

sun.rmi.server.UnicastRef.unmarshalValue()

sun.rmi.transport.tcp.TCPChannel.newConnection()

sun.rmi.server.UnicastRef.invoke()

In my opinion, this is a forged stack provided by An Trinh. TCPChannel.newConnection() has nothing to do with the attack chain. UnicastRef.unmarshalValue() can be exploited, but StreamRemoteCall.executeCall() has been exploited for attack, as shown in the preceding figure.

(5) Hans Martin Munch’s Misjudgment

Hans Martin Munch made a detour by changing the value of ObjectOutputStream.enableReplace, there is a misjudgment. If he had studied ysoserial.payloads.JRMPListener provided by Matthias Kaiser, he could be more direct.

According to Hans Martin Munch’s solution, the call stack trace is as follows:

[1] sun.rmi.transport.ObjectTable.putTarget (ObjectTable.java:171), pc = 0
[2] sun.rmi.transport.Transport.exportObject (Transport.java:106), pc = 6
[3] sun.rmi.transport.tcp.TCPTransport.exportObject (TCPTransport.java:265), pc = 32
[4] sun.rmi.transport.tcp.TCPEndpoint.exportObject (TCPEndpoint.java:411), pc = 5
[5] sun.rmi.transport.LiveRef.exportObject (LiveRef.java:147), pc = 5
[6] sun.rmi.server.UnicastServerRef.exportObject (UnicastServerRef.java:237), pc = 78
[7] java.rmi.server.UnicastRemoteObject.exportObject (UnicastRemoteObject.java:383), pc = 19
[8] java.rmi.server.UnicastRemoteObject.exportObject (UnicastRemoteObject.java:320), pc = 9
[9] java.rmi.server.UnicastRemoteObject. (UnicastRemoteObject.java:198), pc = 26
[10] java.rmi.server.UnicastRemoteObject. (UnicastRemoteObject.java:180), pc = 2
[11] sun.reflect.NativeConstructorAccessorImpl.newInstance0 (native method)
[12] sun.reflect.NativeConstructorAccessorImpl.newInstance (NativeConstructorAccessorImpl.java:62), pc = 85
[13] sun.reflect.DelegatingConstructorAccessorImpl.newInstance (DelegatingConstructorAccessorImpl.java:45), pc = 5
[14] java.lang.reflect.Constructor.newInstance (Constructor.java:423), pc = 79

UnicastRemoteObject.exportObject() will trigger ObjectTable.putTarget(),

while the ObjectOutputStream.writeObject() has to go through the following functions during deserialization of the ObjectOutputStream.writeObject() instance:

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/server/MarshalOutputStream.java

After the attack chain is broken, the malicious object will not be sent to the victim.

The new whitelist bypass technique is to generate a UnicastRemoteObject instance like ysoserial.payloads.JRMPListener, so as to prevent UnicastRemoteObject.exportObject() from being triggered on the client side to invoke ObjectTable.putTarget(). In this way, the value of “target” returned by MarshalOutputStream:81 is “null” and no replacement occurs.

(6) Not Bypassing Source IP Address Check

The new technique put forward by An Trinh can only be used to launch local attacks. If rmiregistry is started by a root user and a local attacker only has common user privileges, the method provided in this document can help the local attacker to obtain root privileges illegally. However, it does not work for remote attackers.

In OpenJDK 8u232, the code at RegistryImpl_Skel:142 is:

RegistryImpl.checkAccess("Registry.rebind")

It checks whether the source IP address of rebind() is the IP address of the local device. If not, the readObject() function will not be invoked. It is said that OpenJDK 8u232 has been preconfigured with source IP address check.

From the perspective of prevention, it is advisable to use the latest version of Java where conditions permit.

References:

[1] Far Sides of Java Remote Protocols – An Trinh [2019-12-04]

https://www.blackhat.com/eu-19/briefings.html
http://i.blackhat.com/eu-19/Wednesday/eu-19-An-Far-Sides-Of-Java-Remote-Protocols.pdf

[2] An Trinhs RMI Registry Bypass – Hans Martin Munch [2020-02]

https://mogwailabs.de/blog/2020/02/an-trinhs-rmi-registry-bypass/

[3] YouDebug

https://github.com/kohsuke/youdebug