From 8b971839243ac9c22ef1b28d6ed13c861c31ad76 Mon Sep 17 00:00:00 2001 From: OJ Date: Sat, 29 Oct 2016 13:45:28 +1000 Subject: [PATCH] Update UUID to match detected platform, fail exploit on invalid session --- lib/msf/base/sessions/meterpreter.rb | 56 +++++++-- lib/msf/core/post/common.rb | 107 +----------------- lib/msf/core/post_mixin.rb | 33 +++--- lib/rex/post/meterpreter/client_core.rb | 13 +-- .../extensions/stdapi/sys/config.rb | 4 +- .../ui/console/command_dispatcher/core.rb | 19 ++-- .../multi/recon/local_exploit_suggester.rb | 16 +-- 7 files changed, 90 insertions(+), 158 deletions(-) diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index db16e3b6f0..03573c9d50 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -324,6 +324,17 @@ class Meterpreter < Rex::Post::Meterpreter::Client username = self.sys.config.getuid sysinfo = self.sys.config.sysinfo + # when updating session information, we need to make sure we update the platform + # in the UUID to match what the target is actually running on, but only for a + # subset of platforms. + if ['java', 'python', 'php'].include?(self.platform) + new_platform = guess_target_platform(sysinfo['OS']) + if self.platform != new_platform + self.payload_uuid.platform = new_platform + self.core.set_uuid(self.payload_uuid) + end + end + safe_info = "#{username} @ #{sysinfo['Computer']}" safe_info.force_encoding("ASCII-8BIT") if safe_info.respond_to?(:force_encoding) # Should probably be using Rex::Text.ascii_safe_hex but leave @@ -333,6 +344,24 @@ class Meterpreter < Rex::Post::Meterpreter::Client self.info = safe_info end + def guess_target_platform(os) + case os + when /windows/i + Msf::Module::Platform::Windows.realname.downcase + when /darwin/i + Msf::Module::Platform::OSX.realname.downcase + when /mac os ?x/i + # this happens with java on OSX (for real!) + Msf::Module::Platform::OSX.realname.downcase + when /freebsd/i + Msf::Module::Platform::FreeBSD.realname.downcase + when /GENERIC\.MP/i, /netbsd/i + Msf::Module::Platform::BSD.realname.downcase + else + Msf::Module::Platform::Linux.realname.downcase + end + end + # # Populate the session information. # @@ -510,20 +539,31 @@ class Meterpreter < Rex::Post::Meterpreter::Client # Generate a binary suffix based on arch # def binary_suffix - # generate a file/binary suffix based on the current platform - case self.platform - when 'windows' - "#{self.arch}.dll" - when 'android', 'java' + # generate a file/binary suffix based on the current arch and platform. + # Platform-agnostic archs go first + case self.arch + when 'java' 'jar' - when 'linux' , 'aix' , 'hpux' , 'irix' , 'unix' - 'lso' when 'php' 'php' when 'python' 'py' else - nil + # otherwise we fall back to the platform + case self.platform + when 'windows' + "#{self.arch}.dll" + when 'linux' , 'aix' , 'hpux' , 'irix' , 'unix' + 'lso' + when 'android', 'java' + 'jar' + when 'php' + 'php' + when 'python' + 'py' + else + nil + end end end diff --git a/lib/msf/core/post/common.rb b/lib/msf/core/post/common.rb index 79f85f936a..e219af6aa0 100644 --- a/lib/msf/core/post/common.rb +++ b/lib/msf/core/post/common.rb @@ -35,7 +35,7 @@ module Msf::Post::Common when /meterpreter/ pid_list = client.sys.process.processes.collect {|e| e['pid']} when /shell/ - if client.platform =~ /win/ + if client.platform == 'windows' o = cmd_exec('tasklist /FO LIST') pid_list = o.scan(/^PID:\s+(\d+)/).flatten else @@ -49,74 +49,6 @@ module Msf::Post::Common pid_list.include?(pid) end - - # Returns the target architecture. - # You should use this instead of session.platform, because of the following reasons: - # 1. session.platform doesn't always give you an arch. For example: a shell session. - # 2. session.platform doesn't mean the target platform/arch, it means whatever the session is. - # For example: you can use a python meterpreter on a Windows platform, and you will - # get 'python/python' as your arch/platform, and not 'x86/win32'. - # - # @return [String] The archtecture recognizable by framework's ARCH_TYPES. - def get_target_arch - arch = nil - - case session.type - when 'meterpreter' - arch = get_recognizable_arch(client.sys.config.sysinfo['Architecture']) - when 'shell' - if session.platform =~ /win/ - arch = get_recognizable_arch(get_env('PROCESSOR_ARCHITECTURE').strip) - else - arch = get_recognizable_arch(get_env('MACHTYPE').strip) - end - end - - arch - end - - - # Returns the target OS. - # You should use this instead of session.platform, because of the following reasons: - # 1. session.platform doesn't always provide a consistent OS name. For example: for a Windows - # target, session.platform might return 'win32', which isn't recognized by Msf::Module::Platform. - # 2. session.platform doesn't mean the target platform/arch, it means whatever the session is. - # For example: You can use a python meterpreter on a Windows platform, and you will get - # 'python/python', as your arch/platform, and not 'windows'. - # - # @return [String] The OS name recognizable by Msf::Module::Platform. - def get_target_os - os = nil - info = '' - - case session.type - when 'meterpreter' - info = client.sys.config.sysinfo['OS'] - when 'shell' - if session.platform =~ /win/ - info = get_env('OS').strip - else - info = cmd_exec('uname -s').strip - end - end - - case info - when /windows/i - os = Msf::Module::Platform::Windows.realname.downcase - when /darwin/i - os = Msf::Module::Platform::OSX.realname.downcase - when /freebsd/i - os = Msf::Module::Platform::FreeBSD.realname.downcase - when /GENERIC\.MP/i, /netbsd/i - os = Msf::Module::Platform::BSD.realname.downcase - else - os = Msf::Module::Platform::Linux.realname.downcase - end - - - os - end - # # Executes +cmd+ on the remote system # @@ -258,7 +190,7 @@ module Msf::Post::Common when /meterpreter/ return session.sys.config.getenv(env) when /shell/ - if session.platform =~ /win/ + if session.platform == 'windows' if env[0,1] == '%' unless env[-1,1] == '%' env << '%' @@ -302,39 +234,4 @@ module Msf::Post::Common private - # Returns an architecture recognizable by ARCH_TYPES. - # - # @param [String] target_arch The arch. Example: x86 - # @return [String] One of the archs from ARCH_TYPES. - def get_recognizable_arch(target_arch) - arch = nil - - # Special handle some cases that ARCH_TYPES won't recognize. - # https://msdn.microsoft.com/en-us/library/aa384274.aspx - case target_arch - when /i[3456]86|wow64/i - return ARCH_X86 - when /(amd|ia|x)64/i - return ARCH_X64 - end - - # Detect tricky variants of architecture types upfront - - # Rely on ARCH_TYPES to tell us a framework-recognizable ARCH. - # Notice we're sorting ARCH_TYPES first, so that the longest string - # goes first. This step is used because sometimes let's say if the target - # is 'x64', and if the ARCH_X86 kicks in first, then we will get 'x86' - # instead of x64, which is inaccurate. - recognizable_archs = ARCH_TYPES - recognizable_archs = recognizable_archs.sort_by {|a| a.length}.reverse - recognizable_archs.each do |a| - if target_arch =~ /#{a}/ - arch = a - break - end - end - - arch - end - end diff --git a/lib/msf/core/post_mixin.rb b/lib/msf/core/post_mixin.rb index aab331b7d2..58eae0ced0 100644 --- a/lib/msf/core/post_mixin.rb +++ b/lib/msf/core/post_mixin.rb @@ -29,8 +29,12 @@ module Msf::PostMixin # # @raise [OptionValidateError] if {#session} returns nil def setup - if not session - raise Msf::OptionValidateError.new(["SESSION"]) + unless session + raise Msf::OptionValidateError.new(["SESSION (not specified)"]) + end + + unless session_compatible?(session) + raise Msf::OptionValidateError.new(["SESSION (type not valid for this module)"]) end super @@ -160,39 +164,36 @@ module Msf::PostMixin return false unless self.module_info["SessionTypes"].include?(s.type) end - # XXX: Special-case java and php for now. This sucks and Session - # should have a method to auto-detect the underlying platform of - # platform-independent sessions such as these. - plat = s.platform - if plat =~ /php|java/ and sysinfo and sysinfo["OS"] - plat = sysinfo["OS"] - end - # Types are okay, now check the platform. This is kind of a ghetto # workaround for session platforms being ad-hoc and Platform being # inflexible. if self.platform and self.platform.kind_of?(Msf::Module::PlatformList) [ # Add as necessary - "win", "linux", "osx" + "windows", "linux", "osx" ].each do |name| - if plat =~ /#{name}/ + if self.platform =~ /#{name}/ p = Msf::Module::PlatformList.transform(name) return false unless self.platform.supports? p end end elsif self.platform and self.platform.kind_of?(Msf::Module::Platform) p_klass = Msf::Module::Platform - case plat.downcase - when /win/ + case self.platform + when 'windows' return false unless self.platform.kind_of?(p_klass::Windows) - when /osx/ + when 'osx' return false unless self.platform.kind_of?(p_klass::OSX) - when /linux/ + when 'linux' return false unless self.platform.kind_of?(p_klass::Linux) end end + # Check to make sure architectures match + mod_arch = self.module_info['Arch'] + mod_arch = [mod_arch] unless mod_arch.kind_of?(Array) + return false unless mod_arch.include?(self.arch) + # If we got here, we haven't found anything that definitely # disqualifies this session. Assume that means we can use it. return true diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 23f7030d3e..d86c1281d3 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -296,16 +296,13 @@ class ClientCore < Extension return true end - def uuid(timeout=nil) - request = Packet.create_request('core_uuid') + def set_uuid(uuid) + request = Packet.create_request('core_set_uuid') + request.add_tlv(TLV_TYPE_UUID, uuid.to_raw) - args = [ request ] - args << timeout if timeout - response = client.send_request(*args) + client.send_request(request) - id = response.get_tlv_value(TLV_TYPE_UUID) - - return Msf::Payload::UUID.new({:raw => id}) + true end def machine_id(timeout=nil) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb b/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb index 7269e87e47..cd11f4dd6c 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb @@ -120,7 +120,9 @@ class Config # make sure we map the architecture across to x64 if x86_64 is returned # to keep arch consistent across all session/machine types - @sysinfo['Architecture'] = ARCH_X64 if @sysinfo['Architecture'].strip == ARCH_X86_64 + if @sysinfo['Architecture'] + @sysinfo['Architecture'] = ARCH_X64 if @sysinfo['Architecture'].strip == ARCH_X86_64 + end end @sysinfo end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb index d37652b2a1..17d52eae10 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -77,19 +77,25 @@ class Console::CommandDispatcher::Core end # Currently we have some windows-specific core commands` - if client.platform =~ /win/ + if client.platform == 'windows' # only support the SSL switching for HTTPS if client.passive_service && client.sock.type? == 'tcp-ssl' c["ssl_verify"] = "Modify the SSL certificate verification setting" end end - if client.platform =~ /win/ || client.platform =~ /linux/ + if client.platform == 'windows' || client.platform == 'linux' # Migration only supported on windows and linux c["migrate"] = "Migrate the server to another process" end - if client.platform =~ /win/ || client.platform =~ /linux/ || client.platform =~ /python/ || client.platform =~ /java/ + # TODO: This code currently checks both platform and architecture for the python + # and java types because technically the platform should be updated to indicate + # the OS platform rather than the meterpreter arch. When we've properly implemented + # the platform update feature we can remove some of these conditions + if client.platform == 'windows' || client.platform == 'linux' || + client.platform == 'python' || client.platform == 'java' || + client.arch == ARCH_PYTHON || (client.arch == ARCH_JAVA && client.platform != 'android') # Yet to implement transport hopping for other meterpreters. c["transport"] = "Change the current transport mechanism" @@ -98,7 +104,7 @@ class Console::CommandDispatcher::Core c["sleep"] = "Force Meterpreter to go quiet, then re-establish session." end - if (msf_loaded?) + if msf_loaded? c["info"] = "Displays information about a Post module" end @@ -462,10 +468,9 @@ class Console::CommandDispatcher::Core end # - # Get the machine ID of the target + # Get the machine ID of the target (should always be up to date locally) # def cmd_uuid(*args) - client.payload_uuid = client.core.uuid unless client.payload_uuid print_good("UUID: #{client.payload_uuid}") end @@ -850,7 +855,7 @@ class Console::CommandDispatcher::Core ) def cmd_migrate_help - if client.platform =~ /linux/ + if client.platform == 'linux' print_line('Usage: migrate < | -P | -N > [-p writable_path] [-t timeout]') else print_line('Usage: migrate < | -P | -N > [-t timeout]') diff --git a/modules/post/multi/recon/local_exploit_suggester.rb b/modules/post/multi/recon/local_exploit_suggester.rb index eeb0a2087a..db23f770d2 100644 --- a/modules/post/multi/recon/local_exploit_suggester.rb +++ b/modules/post/multi/recon/local_exploit_suggester.rb @@ -40,19 +40,9 @@ class MetasploitModule < Msf::Post end - def get_target_arch - @target_arch ||= super - end - - - def get_target_os - @target_os ||= super - end - - def is_module_arch?(mod) mod_arch = mod.target.arch || mod.arch - mod_arch.include?(get_target_arch) + mod_arch.include?(session.arch) end @@ -70,7 +60,7 @@ class MetasploitModule < Msf::Post def is_module_platform?(mod) platform_obj = nil begin - platform_obj = Msf::Module::Platform.find_platform(get_target_os) + platform_obj = Msf::Module::Platform.find_platform(session.platform) rescue ArgumentError => e # When not found, find_platform raises an ArgumentError elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") @@ -110,7 +100,7 @@ class MetasploitModule < Msf::Post def setup - print_status "Collecting local exploits for #{get_target_arch}/#{get_target_os}..." + print_status "Collecting local exploits for #{session.session_type}..." # Initializes an array @local_exploits = [] # Collects exploits into an array