diff --git a/lib/msf/core/post/common.rb b/lib/msf/core/post/common.rb index 7f4db478d9..d15134f4ca 100644 --- a/lib/msf/core/post/common.rb +++ b/lib/msf/core/post/common.rb @@ -49,6 +49,74 @@ 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'. + # + # @returns [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 # @@ -216,5 +284,40 @@ module Msf::Post::Common nil end + 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 /i386/, /i686/ + return ARCH_X86 + when /amd64/i, /ia64/i + return ARCH_X86_64 + end + + # 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 'x86_64', and if the ARCH_X86 kicks in first, then we will get 'x86' + # instead of x86_64, 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/modules/post/multi/recon/local_exploit_suggestor.rb b/modules/post/multi/recon/local_exploit_suggestor.rb new file mode 100644 index 0000000000..64962b4ac6 --- /dev/null +++ b/modules/post/multi/recon/local_exploit_suggestor.rb @@ -0,0 +1,189 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Post + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Multi Recon Local Exploit Suggester', + 'Description' => %q{ + This module suggests local meterpreter exploits that can be used. The + exploits are suggested based on the architecture and platform that + the user has a shell opened as well as the available exploits in + meterpreter. Additionally, the ShowDescription option can be set + to 'true' to a detailed description on the suggested exploits. + + It's important to note that not all local exploits will be fired. + They are chosen based on these conditions: session type, + platform, architecture, and required default options. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'sinn3r', 'Mo' ], + 'Platform' => all_platforms, + 'SessionTypes' => [ 'meterpreter', 'shell' ] + )) + + register_options([ + Msf::OptInt.new('SESSION', [ true, "The session to run this module on." ]), + Msf::OptBool.new('SHOWDESCRIPTION', [true, "Displays a detailed description for the available exploits", false]) + ],Msf::Post) + end + + + def all_platforms + Msf::Module::Platform.subclasses.collect {|c| c.realname.downcase } + 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) + end + + + def is_module_options_ready?(mod) + mod.options.each_pair do |option_name, option| + if option.required && option.default.nil? && mod.datastore[option_name].blank? + return false + end + end + + true + end + + + def is_module_platform?(mod) + platform_obj = nil + begin + platform_obj = Msf::Module::Platform.find_platform(get_target_os) + rescue ArgumentError => e + # When not found, find_platform raises an ArgumentError + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") + return false + end + + module_platforms = mod.target.platform ? mod.target.platform.platforms : mod.platform.platforms + module_platforms.include?(platform_obj) + end + + + def is_session_type_compat?(mod) + mod.session_compatible?(session.sid) + end + + + def set_module_options(mod) + mod.datastore.merge!(self.datastore) + if !mod.datastore['SESSION'] && session.present? + mod.datastore['SESSION'] = session.sid + end + end + + + def is_module_wanted?(mod) + ( + mod.kind_of?(Msf::Exploit::Local) && + mod.respond_to?(:check) && + is_session_type_compat?(mod) && + is_module_platform?(mod) && + is_module_arch?(mod) && + is_module_options_ready?(mod) + ) + end + + + def setup + print_status "Collecting local exploits for #{get_target_arch}/#{get_target_os}..." + # Initializes an array + @local_exploits = [] + # Collects exploits into an array + framework.exploits.each do |name, obj| + mod = framework.exploits.create(name) + next unless mod + set_module_options(mod) + @local_exploits << mod if is_module_wanted?(mod) + end + end + + def show_found_exploits + if datastore['VERBOSE'] + print_status("The following #{@local_exploits.length} exploit checks are being tried:") + else + print_status("#{@local_exploits.length} exploit checks are being tried...") + end + @local_exploits.each do |x| + vprint_status(x.fullname) + end + end + + + def run + if @local_exploits.length < 1 + print_error "No suggestions available." + return + end + + show_found_exploits + + @local_exploits.each do |m| + begin + checkcode = m.check + # See def is_check_interesting? + if is_check_interesting?(checkcode) + # Prints the full name and the checkcode message for the exploit + print_good("#{m.fullname}: #{checkcode.second}") + # If the datastore option is true, a detailed description will show + if datastore['SHOWDESCRIPTION'] + # Formatting for the description text + print_line Rex::Text.wordwrap(Rex::Text.compress(m.description), 2, 70) + end + else + vprint_status("#{m.fullname}: #{checkcode.second}") + end + rescue Rex::Post::Meterpreter::RequestError => e + # Creates a log record in framework.log + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") + vprint_error("#{e.class} #{m.shortname} failled to run: #{e.message}") + end + end + end + + + def is_check_interesting?(checkcode) + [ + Msf::Exploit::CheckCode::Vulnerable, + Msf::Exploit::CheckCode::Appears, + Msf::Exploit::CheckCode::Detected + ].include?(checkcode) + end + + + def print_status(msg='') + super("#{session.session_host} - #{msg}") + end + + + def print_good(msg='') + super("#{session.session_host} - #{msg}") + end + + + def print_error(msg='') + super("#{session.session_host} - #{msg}") + end + +end