Author: k1n9@360 CERT

0 x00 preface

The WebLogic anti-sequence vulnerability was released last week. During the follow-up analysis, I found that a lot of Knowledge about Java deserialization was involved. Then I took this opportunity to review some knowledge points needed for the utilization and defense of Java deserialization vulnerability, and wrote this report after doing some tests and debugging. If there are any mistakes or omissions in the article, please point them out.

 

0x01 Java deserialize timing sequence

Java deserialization timing is necessary to understand the use and defense of Java deserialization, such as why gadgets are constructed from the readObject method and deserialization defense code is written in the resolveClass method. Let’s write down three related methods.

1.1 readObject

This method is used to read objects, and the readObject in question is completely different from many other methods with the same name. Note the difference between the method descriptors in the figure below and other methods with the same name.

Java. IO. ObjectInputStream classes there is mentioned in comments, if you want to in the process of serialization or deserialization do other operations can be through the implement of the three methods in a class to implement. For example, if EvilObj implements this readObject method (the method descriptor needs to be the same as the comment), the deserialization of EvilObj will call this readObject method. Example:

The call stack is as follows

Take a look at the readSerialData method and determine when reading serialized data if the class implements a readObject method, call it through reflection.

Cve-2018-3191 is a good example of how some Java deserialization constructs start with the readObject method, and then use the readObject code step by step to construct the final implementation. The gadgets used by CVE-2018-3191 will be covered later. Of course, this is just one of the ways Java deserialization can be constructed, and more commonly, see the various Gadget constructs in YsoSerial.

1.2 resolveClass and resolveProxyClass

These two methods are in a class in Java. IO. ObjectInputStream, resolveClass used according to the type of descriptor returned to the corresponding class, resolveProxyClass used to return to realize the proxy class descriptor all interfaces in the proxy class. The functionality of these two classes makes them useful for defending against Java antisequences, such as the resolveClass method that checks the class name before deciding whether to proceed with deserialization. If you want to add some operation in these two methods (such as the aforementioned do deserialization defense), the processing data stream class needs to inherit the Java. IO. ObjectInputStream, then rewrite the corresponding method below:

protected Class<? > resolveClass(ObjectStreamClass desc) protected Class<? > resolveProxyClass(String[] interfaces)Copy the code

One thing to avoid confusion here is that these two methods are overwritten in the class that handles the data stream, not in the class being deserialized.

The call stack is as follows

The same is true for resolveXyclass overrides. The important thing to remember here is that not all Java deserializations need to call both methods. Look at some of the code in the readObject0 method at the front of the callback stack:

If you serialize a String, there is no way to use the resolveClass or resolveProxyClass methods. The resolveProxyClass method is also called only when deserializing the proxy object. Looking at serialized data structures is very helpful in understanding the entire process of deserialization, and a tool for looking at serialized data structures is recommended: SerializationDumper

1.3 Deserialize the timing sequence

The sequence diagram is used to see the process of deserialization as a whole.

The deserialization process for normal objects and proxy objects is different. See the readClassDesc method:

The different flows corresponding to the step instantiated in the previous sequence diagram.

1.4 summary

This chapter mainly introduces the Java deserialization related three methods, through the code trace debugging to determine when it will be called, and then combined with deserialization sequence diagram can have a certain understanding of the whole process of deserialization. In fact, to analyze the sequence of deserialization is mainly to know two points, the first is the general process of deserialization, the second is what methods are called in this process, to understand the use and defense of Java deserialization to make some knowledge preparation.

 

0x02 WebLogi T3 deserialization and its defense mechanism

T3 from WebLogic startup to the call stack that serializes the message (from the bottom up) :

at weblogic.rjvm.InboundMsgAbbrev.readObject(InboundMsgAbbrev.java:73)
at weblogic.rjvm.InboundMsgAbbrev.read(InboundMsgAbbrev.java:45)
at weblogic.rjvm.MsgAbbrevJVMConnection.readMsgAbbrevs(MsgAbbrevJVMConnection.java:283)
at weblogic.rjvm.MsgAbbrevInputStream.init(MsgAbbrevInputStream.java:214)
at weblogic.rjvm.MsgAbbrevJVMConnection.dispatch(MsgAbbrevJVMConnection.java:498)
at weblogic.rjvm.t3.MuxableSocketT3.dispatch(MuxableSocketT3.java:348)
at weblogic.socket.BaseAbstractMuxableSocket.dispatch(BaseAbstractMuxableSocket.java:394)
at weblogic.socket.SocketMuxer.readReadySocketOnce(SocketMuxer.java:960)
at weblogic.socket.SocketMuxer.readReadySocket(SocketMuxer.java:897)
at weblogic.socket.PosixSocketMuxer.processSockets(PosixSocketMuxer.java:130)
at weblogic.socket.SocketReaderRequest.run(SocketReaderRequest.java:29)
at weblogic.socket.SocketReaderRequest.execute(SocketReaderRequest.java:42)
at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:145)
at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:117)Copy the code

Here is not to analyze the specific implementation of T3 protocol, catch stopWeblogic. sh in the execution process of data packets:

The first packet is the handshake packet, and then the second packet is found with serialized data. The first four bytes of the packet are the length of the packet. Replacing the serialized data part and then doing packet replay makes the data deserialized by T3 protocol its own construct.

2.1 WebLogic deserialization defense mechanism

The readObject method of the InboundMsgAbbrev class tells you where the deserialization was done from the call stack:

Notice that the readObject method descriptor is not the same as the one in the previous chapter. This means that if an InboundMsgAbbrev object is deserialized, the readObject method is not called. The readObject here is only used in the code flow of T3 protocol processing messages.

As you can see, the class that handles the input data stream is ServerChannelInputStream. As you know from the previous section, the input stream can be controlled. Next, instantiate the ServerChannelInputStream object and deserialize it. Take a look at the ServerChannelInputStream class:

The ServerChannelInputStream class inheritance diagram:

The ServerChannelInputStream class inherits from ObjectInputstream and overwrites the resolveClass and resolveProxyClass methods. As you can see from the previous chapter, these two methods in the ServerChannelInputStream class are called when deserializing different serialized data, It’s easy to understand why WebLogic chose to add filtering code to these two methods (as did previous defenses against deserialization, Override the resolveClass method in ObjectInputstream or override an ObjectInputstream directly.

ResolveProxyClass said at first why this method with the interface name and “. Java rmi. Registry. The registry “, this is the patch loopholes in CVE – 2017-3248. JRMPClient CVE – 2017-3248 using the vulnerability of the used this Gadget, the JRMPClient ysoserial USES dynamic agent, the agent’s interface is “.. Java rmi registry. The registry “. For this there is a lot of bypass method, such as in an interface in Java. The rmi. Activation. The Activator, or not use proxy can be directly. This is the construction of the JRMPClient Gadget, but it is not the content of this article. If you want to know more about this Gadget, please refer to the construction of the ysoSerial Gadget.

Method of checkLegacyBlacklistIfNeeded resolveClass method is used to do filtering for the name of the class and package names.

Enter WebLogicObjectInputFilter class from checkLegacyBlacklistIfNeeded method with in until checkLegacyBlacklistIfNeeded method:

If the Jre filter (JEP290) is unavailable, an exception Unauthorized deserialization attempt is thrown. Take a look at the isBlacklistedLegacy method:

You can see that if the first character of the class name is [(array in the field descriptor) or one of primitiveTypes (some underlying data types), it is not checked.

There are two detection places, one is the class name and one is the package name. Whenever one of them appears in LEGACY_BLACKLIST, it will throw an exception as seen earlier. Let’s see where the LEGACY_BLACKLIST value comes from.

See WebLogicObjectInputFilter an initialization method:

If the Jre filter is not available, the value of LEGACY_BLACKLIST will be set, and the getLegacyBlacklist method will be used:

The value comes from the member variable BLACKLIST of the WebLogicFilterConfig class. The value of a BLACKLIST is generated by the constructLegacyBlacklist method:

The parameters var1, var2 and var3 correspond to

That is to say, you can also control whether to add or remove some blacklists dynamically by starting parameters. By default, the blacklist comes from DEFAULT_BLACKLIST_PACKAGES and DEFAULT_BLACKLIST_CLASSES in WebLogicFilterConfig.

The blacklist after the October patch is as follows:

private static final String[] DEFAULT_BLACKLIST_PACKAGES = new String[]{"org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist", "java.rmi.activation", "sun.rmi.server"};
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", "java.rmi.server.UnicastRemoteObject", "java.rmi.server.RemoteObjectInvocationHandler", "com.bea.core.repackaged.springframework.transaction.support.AbstractPlatformTransactionManager", "java.rmi.server.RemoteObject"};Copy the code

2.2 WebLogic Uses JEP290 for filtering

JEP290 is a new addition to Java9 that enables detection of serialized data. Later versions 8U121, 7U131 and 6U141 were also supported. This feature can be used to limit the maximum number of bytes, depth, array size, and number of references of serialized data, as well as class detection. Use this method to implement the ObjectInputFilter interface (older JDKS only have this class in the Sun.misc package, Java9 is in the java.io package, and Oracle currently discontinues support for both Java9 and Java10. Java11, most recently), and override the checkInput method to check the serialized data. RMI is used to do this filtering in older JDK versions 8U152.

WebLogic is through reflection to access to Java. IO. ObjectInputFilter or sun. Misc. ObjectInputFilter various methods to realize a JreFilterApiProxy objects:

DetermineJreFilterSupportLevel method:

The following process is roughly as follows: assign the member variable serialFilter in the WebLogicFilterConfig object to the values of DEFAULT_BLACKLIST_PACKAGES and DEFAULT_BLACKLIST_CLASSES, The value of serialFilter is a format used by JEP290 to test serialized data (it contains the default values to be tested, separated by semicolons). The package name must be followed by an asterisk (asterisk). If the package name or class name is preceded by an exclamation mark (!), the package name indicates a blacklist. If the package name or class name is not preceded by an exclamation mark (!), the package name indicates a whitelist. You can see all of this in the ObjectInputFilter interface methods). The reflection then calls the setObjectInputFilter method to assign the value of serialFilter to the serialFilter in ObjectInputStream (if an ObjectInputStream object) SerialFilter null is not tested for serialized data. Take a look at WebLogic’s serialFilter:

Looking at the ObjectInputStream side, you can see the call stack from deserialization to incoming detection in the bottom left of the diagram:

Follow in the checkInput method:

Some of the previous routine checks, circled in red, are for formats in serialFilter, using the Function<T, U> interface and lambda syntax. Take a look at the code block for the Global inner class in the ObjectInputFilter interface to see how this check is done.

See that it is a string comparison (class name and package name). Back below the filterCheck method block in ObjectInputStream:

If the state returned is null or REJECTED, an exception will be thrown to end the anti-sequence process. Other return states are logged only.

2.3 summary

As you can see in this chapter, WebLogic has two defenses against deserialization, one when JEP290 is unavailable and the other when JEP290 is available. The code logic of JEP290 is quite long, so I didn’t write the details of each step in the analysis. In fact, they are not impossible to whitelist, I think whitelist should be easy to affect the function of the program, because Java various interface and class encapsulation will make it unclear which interface or class will be used in deserialization. So it’s not easy to write code that determines a whitelist like this. At present, such filtering can only be circumvented if new gadgets are found. On the other hand, such filtering has always been a problem, but the problem has not been discovered.

 

0x03 WebLogic Remote Debugging and Patch Fixed bug in October

3.1 WebLogic Remote Debugging

Modify the domain/bin/setDomainEnv. Sh, set debugFlag to true

In this way, 8453 will be listened to as a debug port during startup, and then use an IDE such as Idea to set up a remote debug configuration to connect to the port. The WebLogic JAR package needs to be added to the project. Because WebLogic has no source code, the debugging code is decomcompiled, so there are problems of not monitoring variables or executing on the wrong line of code.

3.2 CVE – 2018-3245

This hole is a bypass caused by the incomplete repair of the CVE-2018-2893 patch in July. It involves the construction of the JRMPClient Gadget. Please refer to the Weblogic JRMP deserialization Vulnerability review for details

It is important to note that the class name added to the blacklist is not the class name of the object directly serialized, but the name of its parent class. The reason for this filtering effect is that the parent class name is carried in the serialized data.

3.3 CVE – 2018-3191

This Gadget isn’t new, only in com. Bea. Core. Repackaged. Springframework and related classes in this package.

Combined with the readObject Gadget mentioned in Chapter 1, this Gadget is pretty straightforward, but you still need to know how to use JNDI to make full use of it.

Com. Bea. Core. Repackaged. Springframework. Transaction. The jta. JtaTransactionManager this class will trigger when deserialize JNDI lookup, Combined with JNDI utilization, code execution can be achieved.

JtaTransactionManager class readObject method:

Enter the initUserTransactionAndTransactionManager method:

Go to the lookupUserTransaction method and follow along quickly to see the JDNI query method lookup:

One advantage of JNDI is that the lookup method’s parameters are controllable, that is, the value of name can be passed into an RMI or LDAP absolute path. As you can see from the previous code, the name value here comes from the transactionManagerName member variable in the JtaTransactionManager class, So as long as you set transactionManagerName to a controllable RMI address, The JtaTransactionManager object is then serialized and transmitted to WebLogic through T3 protocol, which can be utilized when DATA is deserialized by T3 protocol.

Use demo:

JDK version 8U121 or 7U131 is required by default to make use of JDNI (trustURLCodebase is set to false by default to prevent remote class loading). The server must be able to connect to the Internet.

 

0x04 Reference link

  1. Combating Java Deserialization Vulnerabilities with Look-Ahead Object Input Streams (LAOIS)
  2. Analysis of the Weblogic CVE – 2018-3191