Thursday, October 8, 2009 At 5:52PM
Hello folks, Ron Gutierrez here. Recently I chose to dissect the cryptic serialized HTTP requests used by Google Web Toolkit RPC to better understand which fields are actually “fuzzable”. If you were looking to find a flaw in the implementation of GWT RPC then every single value in the request would be fuzzible. In this case, I’m looking for the fields that the web application will actually be processing.
First, a little bit about Google Web Toolkit (GWT). GWT is a Java framework that is used to create AJAX applications. Rather than having to write complex JavaScript code, GWT makes it easy to build AJAX enable Java web applications by allowing developers to build the components in Java and then compiling that code into optimized JavaScript for browsers to run. One of the added bonuses that GWT provides is the ability to reuse code. GWT-RPC provides the ability to send native and custom Java objects from the client-side Javascript code over to the Java server-side backend. GWT-RPC sends a serialized stream containing the Class name, Method name, Parameters through the wire whenever an AJAX call is made. This serialized stream is what we need to dissect in order to understand which fields can be fuzzed without invalidating the request.
The serialized steam is in plaintext and delimited by pipes. The calls are sent through the browser as a typical HTTP POST Request with the serialized method call in the body of the request. Here is an example of a very simple GWT RPC request to a method called greetServer that takes in two strings as parameters.
5|0|7|http://localhost:8080/testproject/
|29F4EA1240F157649C12466F01F46F60|
com.test.client.GreetingService|greetServer|java.lang.String|
myInput1|myInput2|1|2|3|4|2|5|5|6|7|
The stream can be split into three different parts (keep in mind that I am using Google’s naming convention for these parts as seen through the code that implements the serialization).
Header
1) Contains the SERIALIZATION_STREAM_VERSION value which at present will always be set to 5. If this value is changed, it will cause an error on the server side. Therefore, this value is not fuzzable.
2) The next value is a flag value. I have yet to seen somewhere where this value is set to anything other than 0. Modifying this value does not break a request.
5|0|7|http://localhost:8080/testproject/
|29F4EA1240F157649C12466F01F46F60|
com.test.client.GreetingService|greetServer|java.lang.String|
myInput1|myInput2|1|2|3|4|2|5|5|6|7|
String Table
1) The first value is an integer that tells you how many pipe delimited values to read in to fill the String Table. In this example, the next seven values will be the String Table contents. Consider this data structure to be similar to that of an array in that each value has an index that corresponds to the values in the Payload (discussed next).
5|0|7|http://localhost:8080/testproject/
|29F4EA1240F157649C12466F01F46F60|
com.test.client.GreetingService|greetServer|java.lang.String|
myInput1|myInput2|1|2|3|4|2|5|5|6|7|
Payload
1) Consists of numeric values used to reconstruct the method call and parameters in the String Table. For example, 1 refers to the second element (http://localhost:8080/testproject), 2 refers to the third element (29F…), etc.
2) These values for the most part should not be fuzzed. Since the payload values are used to reconstruct the method call on the server changing them to anything that will create an invalid call which will result in a request error.
3) There are a few caveats which I’ll get into more detail as we progress.
5|0|7|http://localhost:8080/testproject/
|29F4EA1240F157649C12466F01F46F60|
com.test.client.GreetingService|greetServer|java.lang.String|
myInput1|myInput2|1|2|3|4|2|5|5|6|7|
The first four values of the payload can be expected to appear in every GWT RPC request. The first two values consist of the directory containing all the Javascript that was generated during the GWT compiliation and what looks to be an identifier for the Service. Regardless of their meanings, their contents should not be fuzzed as the values are not taken into consideration by the server. The next values “com.test.client.GreetingServer” and “greetServer” corresponds to the server side service name and method name that will be called. Altering these values to an incorrect class or method will cause an exception to be thrown and returned in the response if the exception is not properly handled by the server.
5|0|7|http://localhost:8080/testproject/
|29F4EA1240F157649C12466F01F46F60|
com.test.client.GreetingService|greetServer|java.lang.String|
myInput1|myInput2|1|2|3|4|2|5|5|6|7|
This is where the Payload gets a little more interesting … The fifth payload value (2) actually tells you how many method parameters are being sent. In this example the user is sending two parameters; since two parameters are being sent, there are two subsequent payload values (5|5 in this case) that refer to the indices of the String Table containing the data types of the parameters. In this example, The Java data type of both method parameters is java.lang.String.
5|0|7|http://localhost:8080/testproject/
|29F4EA1240F157649C12466F01F46F60|
com.test.client.GreetingService|greetServer|java.lang.String|
myInput1|myInput2|1|2|3|4|2|5|5|6|7|
The final two values refer to the indices of the String Table containing the actual string values that are being passed in the method. These are the primary subjects for application-level fuzzing. It’s worth noting that integer values that are sent as parameters will be appended to the payload rather than being stored in the String Table. Integer values can be modified, however since Java will be expecting an integer value you will be limited to only numeric values.
5|0|7|http://localhost:8080/testproject/
|29F4EA1240F157649C12466F01F46F60|
com.test.client.GreetingService|greetServer|java.lang.String|
myInput1|myInput2|1|2|3|4|2|5|5|6|7|
Keep in mind that this was a walkthrough of a request with primitive data types. In reality, developers can be sending custom objects or more complex common Java objects such as Vectors, Linked Lists, etc. These data types will follow a different protocol for reading values from the String Table and Payload. To properly de-serialize these types, I found it easiest to capture sample requests and script the de-serialization. I am planning to share this research and my script for automating the fuzzing of GWT RPC applications in an upcoming blog post.
Author: Ron Gutierrez
©Aon plc 2023