Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/modules/post/multi/recon/local_exploit_suggester.rb
Views: 16263
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post67include Msf::Auxiliary::Report89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Multi Recon Local Exploit Suggester',14'Description' => %q{15This module suggests local meterpreter exploits that can be used.1617The exploits are suggested based on the architecture and platform18that the user has a shell opened as well as the available exploits19in meterpreter.2021It's important to note that not all local exploits will be fired.22Exploits are chosen based on these conditions: session type,23platform, architecture, and required default options.24},25'License' => MSF_LICENSE,26'Author' => [ 'sinn3r', 'Mo' ],27'Platform' => all_platforms,28'SessionTypes' => [ 'meterpreter', 'shell' ]29)30)31register_options [32Msf::OptInt.new('SESSION', [ true, 'The session to run this module on' ]),33Msf::OptBool.new('SHOWDESCRIPTION', [true, 'Displays a detailed description for the available exploits', false])34]3536register_advanced_options(37[38Msf::OptBool.new('ValidateArch', [true, 'Validate architecture', true]),39Msf::OptBool.new('ValidatePlatform', [true, 'Validate platform', true]),40Msf::OptBool.new('ValidateMeterpreterCommands', [true, 'Validate Meterpreter commands', false]),41Msf::OptString.new('Colors', [false, 'Valid, Invalid and Ignored colors for module checks (unset to disable)', 'grn/red/blu'])42]43)44end4546def all_platforms47Msf::Module::Platform.subclasses.collect { |c| c.realname.downcase }48end4950def session_arch51# Prefer calling native arch when available, as most LPEs will require this (e.g. x86, x64) as opposed to Java/Python Meterpreter's values (e.g. Java, Python)52session.respond_to?(:native_arch) ? session.native_arch : session.arch53end5455def is_module_arch?(mod)56mod_arch = mod.target.arch || mod.arch57mod_arch.include?(session_arch)58end5960def is_module_wanted?(mod)61mod[:result][:incompatibility_reasons].empty?62end6364def is_session_type?(mod)65# There are some modules that do not define any compatible session types.66# We could assume that means the module can run on all session types,67# Or we could consider that as incorrect module metadata.68mod.session_types.include?(session.type)69end7071def is_module_platform?(mod)72platform_obj = Msf::Module::Platform.find_platform session.platform73return false if mod.target.nil?7475module_platforms = mod.target.platform ? mod.target.platform.platforms : mod.platform.platforms76module_platforms.include? platform_obj77rescue ArgumentError => e78# When not found, find_platform raises an ArgumentError79elog('Could not find a platform', error: e)80return false81end8283def has_required_module_options?(mod)84get_all_missing_module_options(mod).empty?85end8687def get_all_missing_module_options(mod)88missing_options = []89mod.options.each_pair do |option_name, option|90missing_options << option_name if option.required && option.default.nil? && mod.datastore[option_name].blank?91end92missing_options93end9495def valid_incompatibility_reasons(mod, verify_reasons)96# As we can potentially ignore some `reasons` (e.g. accepting arch values which are, on paper, not compatible),97# this keeps track of valid reasons why we will not consider the module that we are evaluating to be valid.98valid_reasons = []99valid_reasons << "Missing required module options (#{get_all_missing_module_options(mod).join('. ')})" unless verify_reasons[:has_required_module_options]100101incompatible_opts = []102incompatible_opts << 'architecture' unless verify_reasons[:is_module_arch]103incompatible_opts << 'platform' unless verify_reasons[:is_module_platform]104incompatible_opts << 'session type' unless verify_reasons[:is_session_type]105valid_reasons << "Not Compatible (#{incompatible_opts.join(', ')})" if incompatible_opts.any?106107valid_reasons << 'Missing/unloadable Meterpreter commands' if verify_reasons[:missing_meterpreter_commands].any?108valid_reasons109end110111def set_module_options(mod)112ignore_list = ['ACTION', 'TARGET'].freeze113datastore.each_pair do |k, v|114mod.datastore[k] = v unless ignore_list.include?(k.upcase)115end116if !mod.datastore['SESSION'] && session.present?117mod.datastore['SESSION'] = session.sid118end119end120121def set_module_target(mod)122session_platform = Msf::Module::Platform.find_platform(session.platform)123target_index = mod.targets.find_index do |target|124# If the target doesn't define its own compatible platforms or architectures, default to the parent (module) values.125target_platforms = target.platform&.platforms || mod.platform.platforms126target_architectures = target.arch || mod.arch127128target_platforms.include?(session_platform) && target_architectures.include?(session_arch)129end130mod.datastore['Target'] = target_index if target_index131end132133def setup134return unless session135136print_status "Collecting local exploits for #{session.session_type}..."137138setup_validation_options139setup_color_options140141# Collects exploits into an array142@local_exploits = []143exploit_refnames = framework.exploits.module_refnames144exploit_refnames.each_with_index do |name, index|145print "%bld%blu[*]%clr Collecting exploit #{index + 1} / #{exploit_refnames.count}\r"146mod = framework.exploits.create name147next unless mod148149set_module_options mod150set_module_target mod151152verify_result = verify_mod(mod)153@local_exploits << { module: mod, result: verify_result } if verify_result[:has_check]154end155end156157def verify_mod(mod)158return { has_check: false } unless mod.is_a?(Msf::Exploit::Local) && mod.has_check?159160result = {161has_check: true,162is_module_platform: (@validate_platform ? is_module_platform?(mod) : true),163is_module_arch: (@validate_arch ? is_module_arch?(mod) : true),164has_required_module_options: has_required_module_options?(mod),165missing_meterpreter_commands: (@validate_meterpreter_commands && session.type == 'meterpreter') ? meterpreter_session_incompatibility_reasons(session) : [],166is_session_type: is_session_type?(mod)167}168result[:incompatibility_reasons] = valid_incompatibility_reasons(mod, result)169result170end171172def setup_validation_options173@validate_arch = datastore['ValidateArch']174@validate_platform = datastore['ValidatePlatform']175@validate_meterpreter_commands = datastore['ValidateMeterpreterCommands']176end177178def setup_color_options179@valid_color, @invalid_color, @ignored_color =180(datastore['Colors'] || '').split('/')181182@valid_color = "%#{@valid_color}" unless @valid_color.blank?183@invalid_color = "%#{@invalid_color}" unless @invalid_color.blank?184@ignored_color = "%#{@ignored_color}" unless @ignored_color.blank?185end186187def show_found_exploits188unless datastore['VERBOSE']189print_status "#{@local_exploits.length} exploit checks are being tried..."190return191end192193vprint_status "The following #{@local_exploits.length} exploit checks are being tried:"194@local_exploits.each do |x|195vprint_status x[:module].fullname196end197end198199def run200runnable_exploits = @local_exploits.select { |mod| is_module_wanted?(mod) }201if runnable_exploits.empty?202print_error 'No suggestions available.'203vprint_line204vprint_session_info205vprint_status unwanted_modules_table(@local_exploits.reject { |mod| is_module_wanted?(mod) })206return207end208209show_found_exploits210results = runnable_exploits.map.with_index do |mod, index|211print "%bld%blu[*]%clr Running check method for exploit #{index + 1} / #{runnable_exploits.count}\r"212begin213checkcode = mod[:module].check214rescue StandardError => e215elog("#Local Exploit Suggester failed with: #{e.class} when using #{mod[:module].shortname}", error: e)216vprint_error "Check with module #{mod[:module].fullname} failed with error #{e.class}"217next { module: mod[:module], errors: ['The check raised an exception.'] }218end219220if checkcode.nil?221vprint_error "Check failed with #{mod[:module].fullname} for unknown reasons"222next { module: mod[:module], errors: ['The check failed for unknown reasons.'] }223end224225# See def is_check_interesting?226unless is_check_interesting? checkcode227vprint_status "#{mod[:module].fullname}: #{checkcode.message}"228next { module: mod[:module], errors: [checkcode.message] }229end230231# Prints the full name and the checkcode message for the exploit232print_good "#{mod[:module].fullname}: #{checkcode.message}"233234# If the datastore option is true, a detailed description will show235if datastore['SHOWDESCRIPTION']236# Formatting for the description text237Rex::Text.wordwrap(Rex::Text.compress(mod[:module].description), 2, 70).split(/\n/).each do |line|238print_line line239end240end241242next { module: mod[:module], checkcode: checkcode.message }243end244245print_line246print_status valid_modules_table(results)247248vprint_line249vprint_session_info250vprint_status unwanted_modules_table(@local_exploits.reject { |mod| is_module_wanted?(mod) })251252report_data = []253results.each do |result|254report_data << [result[:module].fullname, result[:checkcode]] if result[:checkcode]255end256report_note(257host: session.session_host,258type: 'local.suggested_exploits',259data: report_data260)261end262263def valid_modules_table(results)264name_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new265check_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new266267# Split all the results by their checkcode.268# We want the modules that returned a checkcode to be at the top.269checkcode_rows, without_checkcode_rows = results.partition { |result| result[:checkcode] }270rows = (checkcode_rows + without_checkcode_rows).map.with_index do |result, index|271color = result[:checkcode] ? @valid_color : @invalid_color272check_res = result.fetch(:checkcode) { result[:errors].join('. ') }273name_styler.merge!({ result[:module].fullname => color })274check_styler.merge!({ check_res => color })275276[277index + 1,278result[:module].fullname,279result[:checkcode] ? 'Yes' : 'No',280check_res281]282end283284Rex::Text::Table.new(285'Header' => "Valid modules for session #{session.sid}:",286'Indent' => 1,287'Columns' => [ '#', 'Name', 'Potentially Vulnerable?', 'Check Result' ],288'SortIndex' => -1,289'WordWrap' => false, # Don't wordwrap as it messes up coloured output when it is broken up into more than one line290'ColProps' => {291'Name' => {292'Stylers' => [name_styler]293},294'Potentially Vulnerable?' => {295'Stylers' => [::Msf::Ui::Console::TablePrint::CustomColorStyler.new({ 'Yes' => @valid_color, 'No' => @invalid_color })]296},297'Check Result' => {298'Stylers' => [check_styler]299}300},301'Rows' => rows302)303end304305def unwanted_modules_table(unwanted_modules)306arch_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new307platform_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new308session_type_styler = ::Msf::Ui::Console::TablePrint::CustomColorStyler.new309310rows = unwanted_modules.map.with_index do |mod, index|311platforms = mod[:module].target.platform&.platforms&.any? ? mod[:module].target.platform.platforms : mod[:module].platform.platforms312platforms ||= []313arch = mod[:module].target.arch&.any? ? mod[:module].target.arch : mod[:module].arch314arch ||= []315316arch.each do |a|317if a != session_arch318if @validate_arch319color = @invalid_color320else321color = @ignored_color322end323else324color = @valid_color325end326327arch_styler.merge!({ a.to_s => color })328end329330platforms.each do |module_platform|331if module_platform != ::Msf::Module::Platform.find_platform(session.platform)332if @validate_platform333color = @invalid_color334else335color = @ignored_color336end337else338color = @valid_color339end340341platform_styler.merge!({ module_platform.realname => color })342end343344mod[:module].session_types.each do |session_type|345color = session_type == session.type ? @valid_color : @invalid_color346session_type_styler.merge!(session_type.to_s => color)347end348349[350index + 1,351mod[:module].fullname,352mod[:result][:incompatibility_reasons].join('. '),353platforms.map(&:realname).sort.join(', '),354arch.any? ? arch.sort.join(', ') : 'No defined architectures',355mod[:module].session_types.any? ? mod[:module].session_types.sort.join(', ') : 'No defined session types'356]357end358359Rex::Text::Table.new(360'Header' => "Incompatible modules for session #{session.sid}:",361'Indent' => 1,362'Columns' => [ '#', 'Name', 'Reasons', 'Platform', 'Architecture', 'Session Type' ],363'WordWrap' => false,364'ColProps' => {365'Architecture' => {366'Stylers' => [arch_styler]367},368'Platform' => {369'Stylers' => [platform_styler]370},371'Session Type' => {372'Stylers' => [session_type_styler]373}374},375'Rows' => rows376)377end378379def vprint_session_info380vprint_status 'Current Session Info:'381vprint_status "Session Type: #{session.type}"382vprint_status "Architecture: #{session_arch}"383vprint_status "Platform: #{session.platform}"384end385386def is_check_interesting?(checkcode)387[388Msf::Exploit::CheckCode::Vulnerable,389Msf::Exploit::CheckCode::Appears,390Msf::Exploit::CheckCode::Detected391].include? checkcode392end393394def print_status(msg = '')395super(session ? "#{session.session_host} - #{msg}" : msg)396end397398def print_good(msg = '')399super(session ? "#{session.session_host} - #{msg}" : msg)400end401402def print_error(msg = '')403super(session ? "#{session.session_host} - #{msg}" : msg)404end405end406407408