Apache Tomcat Session Deserialization Code Execution Vulnerability (CVE-2020-9484)
A few days ago, Apache Tomcat revealed a new vulnerability to remote code execution. The versions affected are:
- Apache Tomcat 10.x < 10.0.0—M5
- Apache Tomcat 9.x < 9.0.35 x 9.
- Apache Tomcat 8.x < 8.5.55 x 8.
- Apache Tomcat 7.x < 220.127.116.11.
In other words, all versions of tomcat 7, 8, 9 and 10 were released before April 2020. This most certainly means that you need to update your tomcat instance in order not to be vulnerable.
There are a number of prerequisites for this vulnerability to be exploitable.
The PersistentManager is enabled and it’s using a FileStore
The attacker is able to upload a file with arbitrary content, has control over the filename and knows the location where it is uploaded
There are gadgets in the classpath that can be used for a Java deserialization attack
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:
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
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.
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“?
Tomcat requests the Manager to check if a session with session ID “../../../../../../tmp/12345” exists
It will first check if it has that session in memory.
It does not. But the currently running Manager is a PersistentManager, so it will also check if it has the session on disk.
It will check at location directory + sessionid + ".session", which evaluates to “./session/../../../../../../tmp/12345.session“
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.
- 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).