qtc-de / beanshooter

JMX enumeration and attacking tool.
GNU General Public License v3.0
378 stars 45 forks source link

Errors caused by NAT port mapping #42

Open genxor opened 4 months ago

genxor commented 4 months ago

This issue has been troubling me for a long time. When encountering a JMX service in the internal network, and the jmxremote.port is mapped to the upstream IP through NAT port forwarding, all tests will fail. I look forward to a solution.

Here, my setup is as follows: vmware workstation host ip: 172.16.67.128 os: Ubuntu 20.04

docker container ip:172.17.0.2 image: docker pull tomcat:7.0.68-jre8

a Docker container installed on VMHost, with Tomcat 7.0.68 running JMX service, IP mapped from 172.17.0.2:9999 to 172.16.67.128:39001.

The 1st screenshot describes the problem I encountered. image

The 2nd screenshot shows that testing is successful when directly accessing the internal IP and port. image

qtc-de commented 4 months ago

Hi @genxor :wave:

did you only map the 9999 port? This is not sufficient for RMI (or at least not with your JMX configuration). You can see this in the nmap output of your first screenshot that shows the endpoint 172.17.0.2:36119. This is the actual JMX service and beanshooter complains that it cannot reach this endpoint.

What you did is only mapping the RMI registry. This is the naming service of Java RMI that maps human readable bound names to the actual RMI endpoints (IP:PORT). In your case, the registry is listening on 9999. The actual JMX service seems to use a random port. It creates a listening socket on this port and informs the registry that the JMX service is now available on 172.17.0.2:36119.

Notice that regular RMI tools (e.g. jconsole) will completely fail here, as they will try to connect to 172.17.0.2, which is probably not reachable in your setup. beanshooter implements an auto-remapping and remaps the above endpoint to 172.16.67.128:36119. But since you did not forward that port, it reports that the connection was refused.

Now you probably ask how to map a randomly assigned port? Luckily, Java RMI allows multiple remote objects to run on the same port. Therefore, you can use the same port for the RMI registry and the JMX service. If you use Java's default JMX service, you can achieve this by using the following JAVA_OPTIONS:

-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.rmi.port=9999

If the above written stuff does not make sense to you, you may want to take a look at my Java RMI Arsenal talk. It starts with a beginner friendly overview of Java RMI :)

genxor commented 4 months ago

Hi @qtc-de ,

Thanks for your reply. I followed your instructions and modified the configuration, but it seems that beanshooter still doesn't work, like showing in the following pic

image And the port maping is here image I also used nmap to scan the rmi port and confirmed that the JMX service has been bound on the port 9999. image And beanshooter still doesn't work image

My additional inquiry concerns whether there are potential exploitation techniques available if the JMX service port is not mapped externally but is instead listening only within an internal network environment.

Looking forward to your answer.

qtc-de commented 4 months ago

I'm sorry, I was missing this detail in my explanation above.

The problem is now the port mapping. The RMI registry now tells beanshooter to connect to port 9999, since it does not know, that the port is actually remapped. Therefore, beanshooter attempts to connect to this port and fails again. To fix this issue, you could configure the registry and JMX to run on the same port as you forward:

-Dcom.sun.management.jmxremote.port=39001
-Dcom.sun.management.jmxremote.rmi.port=39001

This should work. I may add an option to beanshooter in future that allows to overwrite the JMX IP/port. However, you will probably never encounter a configuration like yours in an productive environment. It is simply wrong configured and no Tool will be able to work with it. But well, never say never and administrators could of course fail to configure JMX properly. So I will add these options in future :+1:

If the JMX service port is only available internally you can only exploit it if there are other vulnerabilities. E.g. the registry itself could be vulnerable (you can use https://github.com/qtc-de/remote-method-guesser to check this). Or you may have other services that allow to perform SSRF attacks (check this blog https://blog.tneitzel.eu/posts/01-attacking-java-rmi-via-ssrf/).

genxor commented 4 months ago

Hi @qtc-de,

Thank you for answering my doubts. Really appreciated your feeback. Btw, could you elaborate on how to overwrite the JMX IP/port? Many Thanks.

qtc-de commented 4 months ago

Overwriting the JMX IP/Port is not that straight forward. These properties are deeply anchored within the internal Java types used during RMI communication. Luckily, beanshooter already implements host redirection (as mentioned above). This is done by defining a custom socket factory.

Java RMI lets you define which socket factory to use when RMI objects create outbound connection. beanshooter creates a custom socket factory on startup, which saves the initially entered target host as variable. Afterwards, if RMI connections attempt to connect to a different host, the socket factory will replace the new target with the previously specified one.

I extended this functionality a little bit. Users can now specify the --overwrite-host parameter to overwrite the destination host of an RMI object. This can be different than the actual target specified in beanshooters main arguments. Additionally, the --overwrite-port option allows you to overwrite the port.

With these options, you should now be able to solve your problem from above. The options are only available on the develop branch yet and were not tested. Feel free to report any bugs you encounter :upside_down_face: