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 of JSESSIONID sent in the request, what would happen if he put something like “JSESSIONID=../../../../../../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. If you are unfamiliar with exploiting Java deserialization vulnerabilities, we have an online course available that teaches you all the details.

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, check our courses. And don’t forget to follow us on twitter: @redtimmysec

References