Apache Tomcat RCE by deserialization (CVE-2020-9484) – write-up and exploit

A few days ago, a new remote code execution vulnerability was disclosed for Apache Tomcat. Affected versions are:

  • Apache Tomcat 10.x < 10.0.0-M5
  • Apache Tomcat 9.x < 9.0.35
  • Apache Tomcat 8.x < 8.5.55
  • Apache Tomcat 7.x < 7.0.104

In other words, all versions of tomcat 7, 8, 9 and 10 released before April 2020. This most certainly means you have to update your instance of tomcat in order not to be vulnerable.

Prerequisites

There are a number of prerequisites for this vulnerability to be exploitable.

  1. The PersistentManager is enabled and it’s using a FileStore
  2. The attacker is able to upload a file with arbitrary content, has control over the filename and knows the location where it is uploaded
  3. There are gadgets in the classpath that can be used for a Java deserialization attack

Tomcat PersistentManager

First some words about the PersistentManager. Tomcat uses the word “Manager” to describe the component that does session management. Sessions are used to preserve state between client requests, and there are multiple decisions to be made about how to do that. For example:

  • Where is the session information stored? In memory or on disk?
  • In which form is it stored? JSON, serialized object, etc.
  • How are sessions IDs generated?
  • Which sessions attributes do we want to preserve?

Tomcat provides two implementations that can be used:

  • org.apache.catalina.session.StandardManager (default)
  • org.apache.catalina.session.PersistentManager

The StandardManager will keep sessions in memory. If tomcat is gracefully closed, it will store the sessions in a serialized object on disk (named “SESSIONS.ser” by default).

The PersistentManager does the same thing, but with a little extra: swapping out idle sessions. If a session has been idle for x seconds, it will be swapped out to disk. It’s a way to reduce memory usage.

You can specify where and how you want swapped sessions to be stored. Tomcat provides two options:

  • FileStore: specify a directory on disk, where each swapped session will be stored as a file with the name based on the session ID
  • JDBCStore: specify a table in the database, where each swapped session will be stored as individual row

Configuration

By default, tomcat will run with the StandardManager enabled. An administrator can configure to use the PersistentManager instead, by modifying conf/context.xml:

<Manager className="org.apache.catalina.session.PersistentManager"
         maxIdleSwap="15">  
  <Store className="org.apache.catalina.session.FileStore"
         directory="./session/" />
</Manager>  

When there is no Manager tag written in context.xml, the StandardManager will be used.

The exploit

When Tomcat receives a HTTP request with a JSESSIONID cookie, it will ask the Manager to check if this session already exists. Because the attacker can control the value sent in the request, what would happen if he put something like “../../../../../../tmp/12345“?

  1. Tomcat requests the Manager to check if a session with session ID “../../../../../../tmp/12345” exists
  2. It will first check if it has that session in memory.
  3. It does not. But the currently running Manager is a PersistentManager, so it will also check if it has the session on disk.
  4. It will check at location directory + sessionid + ".session", which evaluates to “./session/../../../../../../tmp/12345.session
  5. If the file exists, it will deserialize it and parse the session information from it
The web application will return HTTP 500 error upon exploitation, because it encounters a malicious serialized object instead of one that contains session information as it expects.

From the line in the stacktrace of the above image marked in red we see that the PersistentManager tries to load the session from the FileStore. The blue line then shows that it tries to deserialize the object. The error is thrown after deserialization succeeded but when it tried to interpret the object as a session (which it is not). The malicious code has already been executed at that point.

Of course, all that is left to exploit the vulnerability is for the attacker to put a malicious serialized object (i.e. generated by ysoserial) at location /tmp/12345.session

It doesn’t make much sense to create an exploit as it’s just one HTTP request. There is however a very quick and convenient PoC written by masahiro311, see this GitHub page.

Conclusion

This attack can have high impact (RCE), but the conditions that need to be met make the likelihood of exploitation low.

  • PersistentManager needs to be enabled manually by the tomcat administrator. This is likely to happen only on websites with high traffic loads (but not too high, as it will be more likely that a JDBC Store is used instead of a File Store)
  • The attacker has to find a separate file upload vulnerability to place the malicious serialized file on the server.
  • There have to be libraries on the classpath which are vulnerable to be exploited by a Java deserialization attack (e.g. gadgets).

However, a large range of versions of tomcat are affected.

To learn more about advanced web application attacks like these, register for our virtual course at BlackHat USA 2020: Practical Web Application Hacking Advanced.

And don’t forget to follow us on twitter: @redtimmysec

References

Exploiting JD bugs in crypto contexts to achieve RCE and tampering with Java applets

A little over two months ago at Red Timmy Security we have released EnumJavaLibs, a tool allowing the discovery of which libraries are available in the classpath of a remote Java application.

The original idea of developing such a kind of tool is very interesting and we think would be a shame not to talk about it. It came from an assessment that was conducted a year and a half ago and led to the discovery of a Java Deserialization vulnerability on a critical application.

As we are under NDA for that project, there is not any possibility for us to divulge the name of the vendor and the product affected. However, we can describe generically the architecture and give you something juicy to learn from.

General architecture

The solution to analyse was a web application written with GWT that could be accessed only with previous TLS mutual authentication by means of a HSM (Hardware Security Module) device. The authentication process required a Java Applet.

The Google Web Toolkit Framework (GWT) was used for both server and client components. Separate from the GWT implementation, as already said, there was a Java Applet. It was downloaded by the user’s browser during the interaction with the front-end web application before authentication. It was utilized solely for communicating with the hardware token, client side, where a private key was stored.

Basically the GWT-server application and the GWT-client component were in direct communication. Also the GWT-client component spoke to the Java applet which in turn talked to the hardware token to perform operations such as data signing, needed during the authentication process. This information was finally transmitted to the server by the GWT-Client. The following diagram should clarify the generic architecture.

But let’s start from scratch.

The authentication process

When the hardware token is plugged in, a PIN is typed in to unlock the device and the login can start. At this point the authentication process is initiated and a Login request to the server is issued (see screenshot below).

The Login request is very interesting because contains an ASN.1 DER encoded stream (starting with “MII[..]” in the POST body) wrapped around another layer of encoding done with Base64. Why do not we decode it with the “openssl” binary and look inside?

Line 62 reveals the presence of a Java serialized object (sequence “ACED0005”) containing what seems a random number, a session ID and a timestamp. May be it will be deserialized on server side? To explore our hypothesis we decide to change the object at line 62 with something else (another payload generated with “ysoserial”) and submit it to the server. However, the procedure does not seem to work properly. But why? The answer we are looking for arrives after we decide to decompile and analyse the Java applet too.

The analysis pinpointed that yes we were right, at line 62 there is a Java serialized object but line 1591 is containing a RSA-SHA256 digital signature which protects the Java object itself from tampering. Now is clear why the server is not collaborating: in order to inject our own Java serialized payload at line 62 we need a way to bypass that digital signature. One approach might be to find a way to get the private key out of the hardware token, but this is probably going to keep us busy for long time. We opted instead for the “collaborative” approach. The Java applet cannot access the private key directly, however it can ask the HSM (through an interface specially exposed) to sign an arbitrary value and return it back to the applet. The Java applet could be modified as just described and act as our proxy, but there is another problem: it is digitally signed too and with the latest versions of JRE, Java refuses to run it even though we set all the security checks to the minimum possible.

Bypassing the Java checks and opening our way to JD

This is how we bypassed it in nine steps and managed to inject an arbitrary serialized payload:

  • The Java serialized object that holds the attacker payload is generated with “ysoserial” or similar tools.
  • The applet is downloaded from the web server, decompiled and its code modified in order to read from disk the custom Java object generated at point 1, just before it is digitally signed.
  • All the signature files inside “appletfile.jar/META-INF” are deleted. This will make the applet unsigned.
  • The Java applet is now signed again, but this time with a self-signed certificate:

      $ keytool -genkey -keyalg RSA -alias myKey -keystore myKeystore -validity 360

      $ jarsigner -keystore myKeystore -verbose appletfile.jar myKey

  • The security settings in the Java configuration are set to “High” (lowest value). Also the target website is added to the exception list and the certificate generated at step 4 is imported as “Secure Site CA”. This actually decreases and circumvents the security control of the local Java installation. However, there are multiple ways to bypass it, other than this. So feel free to follow any procedure you are comfortable with.
  • First time, we browse the target website as usual. The “appletfile.jar” is downloaded and stored in a cache by Java.
  • Then we search and find the jar file inside the browser cache directory and replace it with the modified and re-signed applet.
  • Now we close the browser and open it again, visiting the target website for the second time. Java will complain about the modified applet but all the security warning messages popping up can be safely ignored.
  • Upon performing the Log-in, our custom serialized Java object is signed and sent to the server.

Logging in of course fails, but the Java deserialization attack should be done at this point. In our specific case we noticed that the server’s reply would change based on what we store inside our object. For example if we serialize an empty “String” object, the reply shows the code XXXX5:

HTTP/1.1 200 OK
Connection: close

//EX[4,0,3,'WdZtoTv',2,0,1,["com.xxxxxxxxxxxxxxxx.xxxxClientException/1437829146","java.util.Date/3385151746","XXXX5","Your operation failed due to an internal error. "],0,7]

If instead we serialize an inexistent object (one not in the classpath of the Java remote application) a similar text message is received in the reply but with “XXXX9” code instead. In simple terms the aplication is behaving as an oracle! From here the idea to create EnumJavaLibs! As the server responds with two different error codes, this fact can be leveraged to find out what libraries it has in the classpath and customize the attack, instead of blindly generating payloads.

Due to the limited amount of time to close the investigation and in order to confirm whether or not what we have discovered is actually a Java deserialization vulnerability or something else, we have asked the vendor the permission to look at the server-side code. Finally our request has been accepted, and this what we have found.

On server side, in the part of the code handling the incoming login request, inside “pkcs7.getContentStream()” there is the call to “readObject()” performing deserialization. This confirms the criticality of the vulnerability.

As can be seen from the above two code snippets, exception “XXXX9” is thrown when “readObject()” fails, in case anything goes wrong during deserialization. Exception “XXXX5” is thrown instead when the object that is read is not an instance of LoginRequest, which actually occurs after deserialization and does not prevent the attack to be placed. Of course bad things can be done only if a valid gadget chain is provided (there was no lookahead protection implemented by the way), but it is not the goal of this blog post to talk about that.

Conclusion

Nice experience! As a general recommendation, do not trust user input when performing deserialization and implement look-ahead protection to prevent an unexpected object from being deserialized.

From an attacker standpoint, be aware that Java serialized objects can be hidden inside other data structures, such as certificates. Also, digital signatures on Java applets only protect against attackers who MITM an applet with a malicious one, so the client will get warnings. It will NOT protect against attackers tampering with the applet running on their own systems: they can just ignore the warnings.

Finally, if you have an application acting as an oracle when sending (malformed) serialized objects, try EnumJavaLibs. Below some references in case you want to do a deep dive into the topic. And don’t forget to follow us on twitter: https://twitter.com/redtimmysec

References:

https://www.owasp.org/index.php/Top_10-2017_A8-Insecure_Deserialization

https://www.ibm.com/developerworks/library/se-lookahead/index.html

Hey! Red Timmy Security will be at Blackhat Las Vegas this year too! Check out our trainings!

Practical Web Application Hacking Advanced 1-2 August and 3-4 August 2020.

Richsploit: One tool to exploit all versions of RichFaces ever released

We are proud to release Richsploit, a tool we wrote to exploit multiple versions of RichFaces. This infamous Java library is included with many JSF web applications for providing advanced UI elements beyond the (very limited) set that is built-in with the framework. Therefore, many websites using JSF are vulnerable to exploitation.

How to detect RichFaces

When a website is using RichFaces, it will load resources from specific URLs which you can see when you monitor outgoing requests with tools like Burp or within the browser’s development tools.

  • Richfaces 3.x: http://example.com/app_name/a4j/g/..
  • Richfaces 4.x: http://example.com/app_name/rfRes/org.richfaces…

Tools like Wappalyzer can be used as well.

Vulnerable versions

All versions of RichFaces released since September 2007 (>= 3.1.0) are vulnerable to at least one exploit. See below an overview of which version is vulnerable to which exploit:

RichFaces 3
3.1.0 ≤ 3.3.3 CVE-2013-2165
3.1.0 ≤ 3.3.4 CVE-2018-12533
3.1.0 ≤ 3.3.4 CVE-2018-14667
RichFaces 4
4.0.0 ≤ 4.3.2 CVE-2013-2165
4.0.0 ≤ 4.5.4 CVE-2015-0279
4.5.3 ≤ 4.5.17 CVE-2018-12532

Exploits

What follows is a brief overview of how each vulnerability works and can be exploited. For more background information, I will refer to some external articles which describe the technicalities in more details.

CVE-2013-2165

This vulnerability was the first one identified on RichFaces, and affects both version 3.x and 4.x. It’s a classic deserialization vulnerability, which allows deserialization of arbitrary Java serialized object streams. To exploit it, the application has to be linked with vulnerable libraries in the classpath, to be able to build a gadget chain.

Because there is no lookahead implemented, every class on the classpath is a potential candidate. One example would be the Apache Commons class, which is often linked with JSF applications.

You can prepare a payload with a tool like ysoserial, save it to a file, and fetch it to Richsploit via the “-p” parameter.

More information: https://codewhitesec.blogspot.com/2018/05/poor-richfaces.html

CVE-2015-0279

This second vulnerability is harder to exploit, but potentially more rewarding. It’s an EL injection vulnerability, that can lead to remote code execution. The idea is to send a proper serialized object this time, but inside the object we put malicious EL code.

EL stands for Expression Language, which is a limited form of Java, used to attach small pieces of logic to UI listeners (e.g. clicking a button would invoke a function call). Even though the capabilities are very limited, we can use a series of tricks to achieve remote code execution.

Richsploit will inject the following EL string when executed: #{"".getClass().forName("java.lang.ProcessBuilder").getConstructors()[1].newInstance("/bin/sh~-c~"+COMMAND+".split("~")).start()}

Let’s take a step-by-step look at how this works.

Expression Language restricts us to a one liner, and we can only start with a variable which is in EL scope:

  • Lambda parameters
  • Literals
  • Managed beans
  • Implicit objects
  • Variables defined in the xhtml

In our example, we start with an empty string, which is a literal

Now we need a way to transfer from a String class to a ProcessBuilder class. We do that by first transforming to the generic Class<> type. Class is an object in Java that represents a Java class. It has methods like getConstructor(), getMethod(), etc.

From there we transform our object to a ProcessBuilder type, using the forName() function from Class. We use ProcessBuilder instead of the more famous Runtime, because

  • ProcessBuilder allows to set the command via the constructor
  • Runtime needs a call to Runtime.getRuntime() but static calls are not allowed in EL (yet another restriction)

To use this class, we have to instantiate it. The getConstructors() function returns an array of all constructors of the class. Unfortunately the order of them appearing in the array changes each time the application is launched. Therefore, this method could require some trial and error, until it is at index 1.

Luckily, ProcessBuilder only has two constructors, so we have a 50% chance of success.

Java doesn’t allow you to create a static array, so we use the split function to transform the String to a String[].

Finally, the start() function is required by ProcessBuilder to invoke execution.

More information: https://issues.redhat.com/browse/RF-13977

CVE-2018-12532

This vulnerability is a response on the fix of the previous one (CVE-2015-0279). As the patch to CVE-2015-0279 (introduced in 4.5.4) disallowed the use of parenthesis in the EL method expression of the contentProducer, it seemed like a dead end.

But if you are fimilar with EL internals, you would know that they can have custom function mappers and variable mappers, which are used by the ELResolver to resolve functions (i. e., name in ${prefix:name()}) and variables (i. e., var in ${var.property}) to Method and ValueExpression instances respectively. Fortunately, various VariableMapper implementations were added to the whitelist (since version 4.5.3).

So to exploit this, all that is needed is to use a variable in the contentProducer method expression like ${dummy.toString} and add an appropriate VariableMapper to the method expression that maps dummy to a ValueExpression, containing the EL we want to inject (same as with CVE-2015-0279).

CVE-2018-12533

This CVE is similar to the CVE-2015-0279, but it uses a different class. Instead of org.resource.MediaResourceOutput, it is using org.richfaces.renderkit.html.Paint2DResource$ImageData.

The trick is that ImageData extends org.ajax4jsf.resource.SerializableResource, which is in the whitelist that was introduced in 3.3.4 to fix the Java deserialization vulnerability (CVE-2013-2165).

More information: https://codewhitesec.blogspot.com/2018/05/poor-richfaces.html

CVE-2018-14667

This final CVE is the most elaborate one and requires some extra detailed explanation because there are no other write-ups available on the internet except this slideshare.

It’s again an EL injection, similar to CVE-2015-0279. The general idea is as follows:

  1. Visit a page that contains <a4j:mediaOutput> tag. This will register UserResource for the session
  2. Prepare serialized payload, containing the EL injection string
  3. Visit UserResource URL and attach encoded serialized payload object in URL after /DATA/
  4. The UserResource class will be instantiated with data from the payload, leading to EL execution

To see this in more detail, this is the call chain from step 3:

/richfaces-demo-jsf2-3.3.3.Final-tomcat6/a4j/s/3_3_3.Finalorg.ajax4jsf.resource.UserResource/n/s/-1487394660/DATA/eAFtUD1LAzEYfi0WP6q[...]zrk29wt4uKHd.jsf

When this URL is provided, the filter defined in web.xml sends it to BaseFilter, which will send it to:

InternetResourceService.java:
The getResourceDataForKey() function reads the /DATA/ object and returns it as Java Object.
Object resourceDataForKey = getResourceBuilder().getResourceDataForKey(resourceKey);

InternetResourceService.java:
It is then saved in resourceContext
resourceContext.setResourceData(resourceDataForKey);

InternetResourceService.java:
The ResourceLifeCycle.send() method is called, which will call sendResource()
getLifecycle().send(resourceContext, resource);

ResourceLifeCycle.java:
Here, the send() method is called DIRECTLY ON THE RESOURCE. So this roughly translates to UserResource.send(context_containing_el)
resource.send(resourceContext);

This is the send function of the vulnerable class UserResource.java of which I provide the code here:

 public void send(ResourceContext context) throws IOException {
   UriData data = (UriData) restoreData(context);
   FacesContext facesContext = FacesContext.getCurrentInstance();
   if (null != data && null != facesContext ) {
     // Send headers
     ELContext elContext = facesContext.getELContext();
     // Send content
     OutputStream out = context.getOutputStream();
     MethodExpression send = (MethodExpression)       
     UIComponentBase.restoreAttachedState(facesContext,data.createContent);
     send.invoke(elContext,new Object[]{out,data.value});
   }
 }

The send.invoke() will execute the EL coming from the context.
So the only way to reach this vulnerable method is by supplying UserResource as the resource in the URL, otherwise resource.send() will never lead to the send() method of UserResource.

Now for the payload, the object we supply is in the ‘context’ variable. It will need to survive:

  • restoreData(context)
  • A cast to UriData
  • A dereference to .createContent, which has to contain a MethodExpression

This means the gadget chain looks as follows:

org.ajax4jsf.resource.UserResource.UriData 
createContent:
javax.faces.component.StateHolderSaver
savedState:
org.jboss.el.MethodExpressionImpl
exp: "${Expression Language}"

See the source code of Richsploit for how this particular serialized object is created. Some tricks with reflection are required because UriData has private fields, and there would have been no way to set the value of createContent directly.

Download

Richsploit can be found on our GitHub page. And remember to join us at Blackhat Las Vegas this year with our Practical Web Application Hacking Advanced Course on 1-2 August and 3-4 August!

Interactive modification of Java Serialized Objects with SerialTweaker

Today we release another Java tool from the Serially toolkit. This tool can be used for advanced Java Deserialization attacks, when existing gadget chains don’t work or when there is a whitelist mechanism in place (like LookAheadDeserialization). In that case we have to work with the classes that are in the whitelist and thus accepted by the application. Instead of sending a gadget chain containing classes not familiar in the application, the idea is to modify the existing serialized objects that are used by the application during normal operations.

WARNING! This tool will deserialize input that it is given. It is therefore vulnerable to deserialization attacks by definition. Please make sure the input you use is not malicious, and/or use the tool in an isolated sandboxed environment.

Example usage

The probability to achieve RCE this way is pretty small, however in this kind of attack something like an authorization bypass is much more likely. Let’s discuss an example on how to perform the Java serialized object modifcation. Imagine an object that contains information about a user. It may contain values like ID, username and references to an object that defines roles and permissions. If we spot this object in serialized form on the wire, we can feed it to SerialTweaker in the following way:

We feed the Base64-encoded version of the serialized object directly on the command line as argument of ‘-b’. SerialTweaker will decode it to a raw serialized object and analyze it via a customized implementation of ObjectInputStream. This customized version captures all classes which are inside the serialized object, so we can create a list of classes that are needed to perform deserialization. Note that in order to deserialize the object locally, the Java runtime must have the required classes in the classpath. Therefore we use the same technique as with EnumJavaLibs: we keep a local repository of jar files and dynamically load what is needed.

So once the analysis finished, SerialTweaker preloads the required libraries. The next step is to attempt deserialization. If it encounters a class that is present in multiple jar files, it will prompt the user to choose which library to load from. In our case, the classes are directly traceable to the jar file “UserDb.jar”, so no prompt is shown. The big integers following the class names are the SerialVersionUID’s of the class.

Modifying variables

When deserialized, the contents of class variables are printed. These are values which are normally encoded within the serialized object, and thus values that we can modify. Keep in mind that static and transient variables are not serialized by default. SerialTweaker will print them and allow you to modify them, because there can be implemented a writeObject() method in the class that does serialize them. But in the default case it will not work to modify these values because they are not serialized. A warning “(not serialized by default)” will be printed after the variable output to remind the user of this behavior.

In our example, the 3rd field of the User class is a reference to a Roles object. SerialTweaker recognizes references and will print nested classes. The Roles class contains a boolean variable, which we would like to change to true.

Next, the user is prompted if he wants to perform any modifications. We type the id of the field we want to change (3), and the new value for it (T, for true). SerialTweaker prints the modified version of the object to confirm the modification was successful. If we’re done making changes, the modified object is serialized and the Base64 output is printed to the user.

Modifying ViewState

An interesting subject of serialized object modification might be JSF viewstates. When configured to store state client side, JSF websites will ship back- and forward a serialized object with every request. It’s usually a large object containing information about the state of UI elements. Changing these values might give an attacker opportunity to bypass certain restrictions.

SerialTweaker has a special ‘viewstate mode’, which will grab and decrypt (with ‘-k’) the viewstate from a given URL. Use the ‘-v’ flag to supply the URL.

Evaluation

There are already existing tools out there that can modify serialized objects. The difference is that they work by modifying the serialized object directly, working on a much lower level. This method is error-prone, because the serialized object contains various length fields and references, which need to be updated accordingly. SerialTweaker can make much more advanced modifications, but it comes with a price. You need to have the classes in your classpath in order to be able to deserialize them. Modifying values inside custom classes is therefore not possible with this approach.

The local repository of jar files is expected in ~/.serially/jars and should be indexed by using JavaClassDB.py from the EnumJavaLibs project.

Download

The tool is released on our GitHub page.

Remote Java classpath enumeration with EnumJavaLibs

To discover a deserialization vulnerability is often pretty straightforward. When source code is available, it comes down to finding calls to readObject() and finding a way for user input to reach that function. In case we don’t have source code available, we can spot serialized objects on the wire by looking for binary blobs or base64 encoded objects (recognized by ‘rO0..’). The hard part comes with exploitation. Sure you can throw all exploits from ysoserial at it and hope for the best, but if it doesn’t work there are not much other things to try.

The big piece of information which is missing at this point, is information about the classpath of the remote application. If we know what libraries are loaded, we might be able to construct a gadget chain (or adjust the existing ysoserial exploit to match the version of the library on the remote application, for example). That’s where the idea of EnumJavaLibs comes from: just let it deserialize arbitrary objects from different (popular) 3rd party Java libraries. More specifically:

  1. Create a local database of the most common Java libraries
  2. For each of these libraries, find a class that is serializable
  3. Create an instance of this object, serialize it, and send it to the remote application
  4. If we get a ClassNotFoundException back, we know the library is not on the classpath

We have released the code of this project on GitHub, together with a tool that can build a database of libraries (JavaClassDB.py). You can download the pre-compiled version over here.

JMX/RMI

JMX/RMI is a perfect example where Java classpath enumeration can be done, as it meets the above conditions. When the endpoint is running a vulnerable Java version (that is, before JEP290 implementation), JMX/RMI endpoints are vulnerable by default (regardless whether authentication is required). This is because a JMX/RMI endpoint exposes the RMIServerImpl_Stub.newClient(Object o) function which deserializes any object provided (RMI functions that accept parameters of type Object should always be a red flag).

EnumJavaLibs has a specific ‘RMI’ mode which allows you to specify IP and port of the RMI service. It will then invoke the newClient() function for each serialized object from the jars in the database. Upon deserialization error, we get the full stacktrace back via RMI, so we can easily read whether the class was found or not – and conclude if the library is loaded in the classpath.

Web applications

Because the way to probe web applications is highly specific case to case, Enumjavalibs will not perform the HTTP requests for you. Instead, it will create a CSV file with (classname, base64_encoded_serialized_object) for you, which you can then use to build requests yourself. One way to do this would be to use Burp intruder.

Internals

The tool uses a few Java ‘tricks’ which I would like to give some more details about. The first one is dynamic loading of libraries. This is made a lot harder (but not impossible) since Java 11, which is why the tool should be compiled with Java 8. Using URLClassLoader.addUrl(), it is possible to load a jar file specified by its path. Because this function is not exposed by the JDK, reflection is required to make it accessible.

Once we load the jar, we go through all its classes one-by-one, and try to serialize them. It doesn’t matter to us which class, all we want is some class from the jar file. If the remote server is able to deserialize this class, there’s a high probability the library it comes from is loaded. But how do we serialize an arbitrary class from a jar file? Normally serialization happens as follows:

  1. Instantiate an object of the class with ‘new …’
  2. Serialize the object using ObjectOutputStream

To instantiate the object, we need information about its constructors though. We could get that via reflection, but what if it has a constructor that takes as argument another object? Or what if some conditions need to be met for these arguments, or otherwise the constructor will throw an exception? This results in a complex situation, which is hard to handle automatically. Fortunately, there is a way to instantiate objects without invoking constructors. And it’s not pretty. But it works.

It’s called the Unsafe class and it let’s you do things that Java would normally forbid you to (for good reasons). One of them being instantiating classes without invoking the constructor. This works for our purposes, because at the remote side the ClassNotFoundException is thrown based on the name of the class – we don’t actually care about it being initialized properly. So after instantiation via Unsafe, we can serialize the object and – depending how you run the tool – send it off over RMI or store the Base64 encoded version in the output file.

False positives

It might happen that the class which is deserialized at the remote server, could actually be traced back to multiple libraries. Recall that we distinguish classes based on the combination of FQDN and SerialVersionUID. So when the serializable class doesn’t specify a custom SerialVersionUID in the source code, it is not unthinkable that a collision occurs (which means FQDN is the same). But even in this case we can still be pretty sure about which library is used – just not the exact version. Take for example the FQDN “org.apache.commons.fileupload.DiskFileUpload”; we can be pretty sure it comes from the commons-fileupload library, even though we might be unable to identify the exact version because the SerialVersionUID is the same between different versions.

Hey! Red Timmy Security will be at Blackhat Las Vegas this year too! Check out our trainings!

Practical Web Application Hacking Advanced 1-2 August and 3-4 August 2020.

JMX RMI – Multiple Applications (RCE)

After achieving a foothold inside the targeted organization, an attacker surely will search for vulnerabilities giving him/her the ability to compromise other machines and move laterally into the network. Especially for companies adopting Java-based software solutions, one of the most abused services to achieve Remote Command Execution is JMX/RMI. It has revealed to be very pervasive in the business LAN/DMZ contexts. Indeed around one year ago we performed a random analysis of both open source and business tools, checking for the presence of JMX/RMI ports. We ended up discovering (and sometimes directly reporting) some new vulnerabilities. Few CVE(s) were registered as well. This whitepaper includes main highlights of our findings.


WHAT IS JMX

JMX (Java Management Extension) is a documental specification for remote management and monitoring of Java applications. Its main unit is the MBean (management bean), a java object exposing some attributes that can be read/written through the network, and most importantly a series of functions or operations invokable from remote. A so-called “MBeanServer” (or more simply JMX Server) keeps track of all registered MBeans inside a kind of searchable register/archive. It is the component puts in charge of managing the communication between clients that want access to one or more exposed functions/attributes of an MBean and the MBean itself.

JMX was not built with the security principle in mind. Therefore, whoever is able to reach the network port it is listening to can also invoke the exposed methods anonymously, without going through a formal authentication process.

WHAT IS RMI

The RMI (Remote Method Invocation) protocol is the most common mechanism (as well as the only one that the JMX standard expressly requires to be supported by default) through which the methods and functions the MBeans remotely expose (made available by means of JMX server) are invoked by clients.

WHERE IS THE PROBLEM WITH JMX/RMI?

By default no authentication is enabled for JMX/RMI. Furthermore the authentication, when rarely adopted, is only restricted to a couple of options:

  • File-based: insecure as passwords are left in clear-text in the filesystem (also transmitted in clear-text over the network);
  • TLS mutual authentication: difficult to set up and maintain with the growth of the numbers of clients and nodes, as it requires the generation of digital keys and certificates for each of them.

The Oracle Java documentation is self-explanatory when it comes to determine where the problem stems from: (https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html)

Caution – […] any remote user who knows (or guesses) your port number and host name will be able to monitor and control your Java applications and platform. Furthermore, possible harm is not limited to the operations you define in your MBeans. A remote client could create a “javax.management.loading.MLet” MBean and use it to create new MBeans from arbitrary URLs, at least if there is no security manager. In other words, a rogue remote client could make your Java application execute arbitrary code”.

AFFECTED SOFTWARE

Below follows the list of components and software solutions that we found affected by this specific issue during an analysis conducted in the period February-March 2018.

CISCO UNIFIED CUSTOMER VOICE PORTAL <= 11.x

Cisco Unified CVP is an intelligent IVR (Interactive Voice Response) and call control solution. In its default configuration, until version <= 11.x and potentially above, an unauthenticated JMX/RMI interface is bound to a wildcard address on TCP ports 2098 and 2099. An attacker establishing a network connection to one of these affected ports and sending the malicious payload could easily trigger the RCE.

The vulnerability was first discovered on February 2018. The vendor was made aware almost immediately. After multiple meetings and discussions occurred between April and July 2018, Cisco agreed to document two new security procedures in to its CVP configuration guide:

  • Secure JMX Communication between CVP Components
  • Secure JMX Communication between OAMP and Call Server using Mutual Authentication.

We reopen the dialogue with Cisco on February 2019 when a CVE has been asked but not assigned, as the vendor considers the flaw as a configuration issue.

Anyway Cisco has published a security bulletin for the flaw we reported at the URL https://quickview.cloudapps.cisco.com/quickview/bug/CSCvi31075

NASDAQ BWISE <= 5.x

This is a commercial GRC (Governance, Risk and Compliance Management) solution for risk handling and management. Branch 5.x of Nasdaq BWise is vulnerable because by default the SAP BO Component enables JMX/RMI on TCP port 81 without authentication.

We discovered the vulnerability and contacted the vendor on March 2018. After discussing with them, the release of Service Pack (SP02) has been announced to solve the problem.

A CVE number has not been requested until February 2019, when we realized that in the meantime another security researcher had registered CVE-2018-11247 for the same issue. As indicated in their published security bulletin https://packetstormsecurity.com/files/148918/Nasdaq-BWise-5.0-JMX-RMI-Interface-Remote-Code-Execution.html, the researcher had discovered the vulnerability 2 months after us (May 2018).

However as a CVE already existed, we did not requested a new one.

NICE ENGAGE PLATFORM <= 6.5

NICE Engage is an interaction recording platform. Versions <= 6.5 (and potentially above) open up the TCP port 6338 where a JMX/RMI service listens to without authentication. Of course this may be abused to launch remote commands by deploying a malicious MBean.

On March 4th 2018 we contacted the vendor and on 7th same month they have recognized the vulnerability. They also declared that no specific fix would have been released because enabling the JMX file-based authentication was considered enough to mitigate the finding. Anyway, this change is not reflected in the default configuration and must be applied manually, leaving at risk the companies using the product and are not aware of the problem. Only very recently, February 2019, we have registered CVE-2019-7727 for this vulnerability

APACHE CASSANDRA 3.8 through 3.11.1 and CLOUDERA ZOOKEEPER/CDH 5.X/6.X

On February 2018 we discovered that the Apache Software Foundation project dubbed Cassandra (release between 3.8 and 3.11) exposed the TCP port 7199 on which JMX/RMI was running. We did not report the finding immediately. Soon after someone else did it and registered CVE-2018-8016. All details are perfectly explain here:

https://lists.apache.org/thread.html/bafb9060bbdf958a1c15ba66c68531116fba4a83858a2796254da066@%3Cuser.cassandra.apache.org%3E

During a contextual security investigation on March 2018 we also managed to spot multiple instances of Cloudera Zookeeper/CDH (versions 5.x and 6.x) affected. In this case the TCP port 9010 was exposing a JMX/RMI service. The vendor is aware of the problem at least since June 2018. In one of their release notes they wrote:

A successful attack may leak data, cause denial of service, or even allow arbitrary code execution on the Java process that exposes a JMX port. Beginning in Cloudera Manager 6.1.0, it is possible to configure mutual TLS authentication on ZooKeeper’s JMX port”.

The possibility to configure mutual TLS authentication for previous product versions is unknown instead. We did not register a CVE for this vulnerability.

EXPLOIT CODE

Working tools to exploit JMX/RMI vulnerabilities exist out there. Some good examples are sjet and mjet. When we have started our first investigations in this field did not manage to find one fitting all requirements (we have had problems to target specific contexts and configurations) and have decided to develop our own. This tool is not going to be publicly shared for now. It probably will in future, so visit our blog (https://redtimmysec.wordpress.com).

Moreover, if you want to know more about JMX/RMI exploitation and mitigation, check out our Blackhat Las Vegas courses on 3-4 and 5-6 August 2019, because this will be one of the topics covered there.

Stay tuned!