123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- #!/usr/bin/env ruby
- # -*- coding: binary -*-
- #
- # This user interface allows users to interact with the framework through a
- # command line interface (CLI) rather than having to use a prompting console
- # or web-based interface.
- #
- $stderr.puts "[!] ************************************************************************"
- $stderr.puts "[!] * The utility msfcli is deprecated! *"
- $stderr.puts "[!] * It will be removed on or about 2015-06-18 *"
- $stderr.puts "[!] * Please use msfconsole -r or -x instead *"
- $stderr.puts "[!] * Details: https://github.com/rapid7/metasploit-framework/pull/3802 *"
- $stderr.puts "[!] ************************************************************************"
- msfbase = __FILE__
- while File.symlink?(msfbase)
- msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
- end
- $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
- require 'rex'
- class Msfcli
- #
- # Attributes
- #
- # @attribute framework
- # @return [Msf::Simple::Framework]
- #
- # initialize
- #
- def initialize(args)
- @args = {}
- @indent = ' '
- @args[:module_name] = args.shift # First argument should be the module name
- @args[:mode] = args.pop || 'h' # Last argument should be the mode
- @args[:params] = args # Whatever is in the middle should be the params
- if @args[:module_name] =~ /^exploit(s)*\//i
- @args[:module_name] = @args[:module_name].split('/')
- @args[:module_name] = @args[:module_name][1, @args[:module_name].length] * "/"
- end
- end
- #
- # Instance Methods
- #
- # The framework to create and list modules.
- #
- # @return [Msf::Simple::Framework]
- def framework
- @framework ||= Msf::Simple::Framework.create({'DeferModuleLoads'=>true})
- end
- # Sets {#framework}.
- #
- # @raise [ArgumentError] if {#framework} already set
- def framework=(framework)
- if instance_variable_defined? :@framework
- fail ArgumentError.new("framework already set")
- end
- @framework = framework
- end
- #
- # Returns a usage Rex table
- #
- def usage (str = nil, extra = nil)
- tbl = Rex::Ui::Text::Table.new(
- 'Header' => "Usage: #{$0} <exploit_name> <option=value> [mode]",
- 'Indent' => 4,
- 'Columns' => ['Mode', 'Description']
- )
- tbl << ['(H)elp', "You're looking at it baby!"]
- tbl << ['(S)ummary', 'Show information about this module']
- tbl << ['(O)ptions', 'Show available options for this module']
- tbl << ['(M)issing', 'Show empty required options for this module']
- tbl << ['(A)dvanced', 'Show available advanced options for this module']
- tbl << ['(I)DS Evasion', 'Show available ids evasion options for this module']
- tbl << ['(P)ayloads', 'Show available payloads for this module']
- tbl << ['(T)argets', 'Show available targets for this exploit module']
- tbl << ['(AC)tions', 'Show available actions for this module']
- tbl << ['(C)heck', 'Run the check routine of the selected module']
- tbl << ['(E)xecute', 'Execute the selected module']
- tbl.to_s
- $stdout.puts "Error: #{str}\n\n" if str
- $stdout.puts tbl.to_s + "\n"
- $stdout.puts "Examples:" + "\n"
- $stdout.puts "msfcli multi/handler payload=windows/meterpreter/reverse_tcp lhost=IP E" + "\n"
- $stdout.puts "msfcli auxiliary/scanner/http/http_version rhosts=IP encoder= post= nop= E" + "\n"
- $stdout.puts extra + "\n" if extra
- $stdout.puts
- end
- #
- # Loads up everything in framework, and then returns the module list
- #
- def dump_module_list
- # This is what happens if the user doesn't specify a module name:
- # msfcli will end up loading EVERYTHING to memory to show you a help
- # menu plus a list of modules available. Really expensive if you ask me.
- $stdout.puts "[*] Please wait while we load the module tree..."
- self.framework = Msf::Simple::Framework.create
- ext = ''
- tbl = Rex::Ui::Text::Table.new(
- 'Header' => 'Exploits',
- 'Indent' => 4,
- 'Columns' => [ 'Name', 'Description' ])
- framework.exploits.each_module { |name, mod|
- tbl << [ 'exploit/' + name, mod.new.name ]
- }
- ext << tbl.to_s + "\n"
- tbl = Rex::Ui::Text::Table.new(
- 'Header' => 'Auxiliary',
- 'Indent' => 4,
- 'Columns' => [ 'Name', 'Description' ])
- framework.auxiliary.each_module { |name, mod|
- tbl << [ 'auxiliary/' + name, mod.new.name ]
- }
- ext << tbl.to_s + "\n"
- ext
- end
- #
- # Payload naming style is kind of inconsistent, so instead of
- # finding the exact path name, we provide the most educated guess (whitelist)
- # based on platform/stage type/session type/payload name suffix/etc.
- #
- def guess_payload_name(p)
- matches = []
- payload = p.split('/')
- platform = payload[0]
- suffix = payload[-1]
- stage_types = ['singles', 'stagers', 'stages']
- session_types = ['meterpreter', 'shell']
- arch = ''
- # Rule out some possibilities
- if p =~ /meterpreter/i
- session_types.delete('shell')
- stage_types.delete('singles')
- end
- if p =~ /shell\/.+$/i
- session_types.delete('meterpreter')
- stage_types.delete('singles')
- end
- if p =~ /x64/i
- arch = 'x64'
- elsif p =~ /x86/i
- arch = 'x86'
- end
- # Determine if the payload is staged. If it is, then
- # we need to load that staged module too.
- if session_types.include?('shell') and stage_types.include?('stages')
- if arch == 'x64'
- matches << /stages\/#{platform}\/x64\/shell/
- elsif arch == 'x86'
- matches << /stages\/#{platform}\/x86\/shell/
- else
- matches << /stages\/#{platform}\/shell/
- end
- elsif session_types.include?('meterpreter') and stage_types.include?('stages')
- if arch == 'x64'
- matches << /stages\/#{platform}\/x64\/meterpreter/
- elsif arch == 'x86'
- matches << /stages\/#{platform}\/x86\/meterpreter/
- else
- matches << /stages\/#{platform}\/meterpreter/
- end
- end
- # Guess the second possible match
- stage_types *= "|"
- session_types *= "|"
- if arch == 'x64'
- matches << /payloads\/(#{stage_types})\/#{platform}\/x64\/.*(#{suffix})\.rb$/
- elsif arch == 'x86'
- matches << /payloads\/(#{stage_types})\/#{platform}\/x86\/.*(#{suffix})\.rb$/
- else
- matches << /payloads\/(#{stage_types})\/#{platform}\/.*(#{suffix})\.rb$/
- end
- matches
- end
- #
- # Returns a whitelist for encoder modules
- #
- def guess_encoder_name(e)
- [/encoders\/#{e}/]
- end
- #
- # Returns a whitelist for nop modules
- #
- def guess_nop_name(n)
- [/nops\/#{n}/]
- end
- #
- # Returns a whitelist for post modules
- #
- def guess_post_name(p)
- [/post\/#{p}/]
- end
- #
- # Returns possible patterns like exploit/aux, encoders, nops we want to
- # load to the whitelist.
- #
- def generate_whitelist
- whitelist = []
- whitelist << /#{@args[:module_name]}/ # Add exploit
- # nil = not set, empty = manually set to load nothing
- encoder_val = nil
- nops_val = nil
- post_val = nil
- payload_param = ''
- junk_args = []
- @args[:params].each { |args|
- var, val = args.split('=', 2)
- next if val.nil?
- case var.downcase
- when 'payload'
- payload_param = val
- if val.empty?
- junk_args << args
- else
- whitelist.concat(guess_payload_name(val))
- end
- when 'encoder'
- encoder_val = val
- if val.empty?
- junk_args << args
- else
- whitelist.concat(guess_encoder_name(val))
- end
- when 'nop'
- nops_val = val
- if val.empty?
- junk_args << args
- else
- whitelist.concat(guess_nop_name(val))
- end
- when 'post'
- post_val = val
- if val.empty?
- junk_args << args
- else
- whitelist.concat(guess_post_name(val))
- end
- end
- }
- # Cleanup empty args
- junk_args.each { |args| @args[:params].delete(args) }
- # If it's an exploit and no payload set, load them all.
- if @args[:module_name] !~ /auxiliary\// and payload_param.empty?
- whitelist << /payloads\/.+/
- end
- # Add post modules list if not set
- if post_val.nil?
- whitelist << /post\/.+/
- end
- # Add default encoders if not set
- # This one is needed no matter what
- whitelist << /encoders\/generic\/*/
- if encoder_val.nil?
- if payload_param =~ /^.+\.x64.+/
- whitelist << /encoders\/x64\/.+/
- elsif payload_param =~ /^.+\.x86.+/
- whitelist << /encoders\/x86\/.+/
- else
- whitelist << /encoders\/.+/
- end
- end
- # Add default NOP modules if not set
- if nops_val.nil?
- whitelist << /nops\/.+/
- end
- whitelist
- end
- #
- # Initializes exploit/payload/encoder/nop modules.
- #
- def init_modules
- $stdout.puts "[*] Initializing modules..."
- module_name = @args[:module_name]
- modules = {
- :module => nil, # aux or exploit instance
- :payload => nil, # payload instance
- :encoder => nil, # encoder instance
- :nop => nil # nop instance
- }
- whitelist = generate_whitelist
- # Load up all the possible modules, this is where things get slow again
- framework.init_module_paths({:whitelist=>whitelist})
- if (framework.modules.module_load_error_by_path.length > 0)
- print("Warning: The following modules could not be loaded!\n\n")
- framework.modules.module_load_error_by_path.each do |path, error|
- print("\t#{path}: #{error}\n\n")
- end
- return {}
- end
- # Determine what type of module it is
- if module_name =~ /exploit\/(.*)/
- modules[:module] = framework.exploits.create($1)
- elsif module_name =~ /auxiliary\/(.*)/
- modules[:module] = framework.auxiliary.create($1)
- elsif module_name =~ /post\/(.*)/
- modules[:module] = framework.post.create($1)
- else
- modules[:module] = framework.exploits.create(module_name)
- if modules[:module].nil?
- # Try falling back on aux modules
- modules[:module] = framework.auxiliary.create(module_name)
- end
- end
- if modules[:module].nil?
- # Still nil? Ok then, probably invalid
- return {}
- end
- modules[:module].init_ui(
- Rex::Ui::Text::Input::Stdio.new,
- Rex::Ui::Text::Output::Stdio.new
- )
- # Import options
- begin
- modules[:module].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
- rescue Rex::ArgumentParseError => e
- raise e
- end
- # Create the payload to use
- if (modules[:module].datastore['PAYLOAD'])
- modules[:payload] = framework.payloads.create(modules[:module].datastore['PAYLOAD'])
- if modules[:payload]
- modules[:payload].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
- end
- end
- # Create the encoder to use
- if modules[:module].datastore['ENCODER']
- modules[:encoder] = framework.encoders.create(modules[:module].datastore['ENCODER'])
- if modules[:encoder]
- modules[:encoder].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
- end
- end
- # Create the NOP to use
- if modules[:module].datastore['NOP']
- modules[:nop] = framework.nops.create(modules[:module].datastore['NOP'])
- if modules[:nop]
- modules[:nop].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
- end
- end
- modules
- end
- def show_summary(m)
- readable = Msf::Serializer::ReadableText
- $stdout.puts("\n" + readable.dump_module(m[:module], @indent))
- $stdout.puts("\n" + readable.dump_module(m[:payload], @indent)) if m[:payload]
- $stdout.puts("\n" + readable.dump_module(m[:encoder], @indent)) if m[:encoder]
- $stdout.puts("\n" + readable.dump_module(m[:nop], @indent)) if m[:nop]
- end
- def show_options(m)
- readable = Msf::Serializer::ReadableText
- $stdout.puts("\n" + readable.dump_options(m[:module], @indent))
- $stdout.puts("\nPayload:\n\n" + readable.dump_options(m[:payload], @indent)) if m[:payload]
- $stdout.puts("\nEncoder:\n\n" + readable.dump_options(m[:encoder], @indent)) if m[:encoder]
- $stdout.puts("\nNOP\n\n" + readable.dump_options(m[:nop], @indent)) if m[:nop]
- end
- def show_missing(m)
- readable = Msf::Serializer::ReadableText
- $stdout.puts("\n" + readable.dump_options(m[:module], @indent, true))
- $stdout.puts("\nPayload:\n\n" + readable.dump_options(m[:payload], @indent, true)) if m[:payload]
- $stdout.puts("\nEncoder:\n\n" + readable.dump_options(m[:encoder], @indent, true)) if m[:encoder]
- $stdout.puts("\nNOP\n\n" + readable.dump_options(m[:nop], @indent, true)) if m[:nop]
- end
- def show_advanced(m)
- readable = Msf::Serializer::ReadableText
- $stdout.puts("\n" + readable.dump_advanced_options(m[:module], @indent))
- $stdout.puts("\nPayload:\n\n" + readable.dump_advanced_options(m[:payload], @indent)) if m[:payload]
- $stdout.puts("\nEncoder:\n\n" + readable.dump_advanced_options(m[:encoder], @indent)) if m[:encoder]
- $stdout.puts("\nNOP:\n\n" + readable.dump_advanced_options(m[:nop], @indent)) if m[:nop]
- end
- def show_ids_evasion(m)
- readable = Msf::Serializer::ReadableText
- $stdout.puts("\n" + readable.dump_evasion_options(m[:module], @indent))
- $stdout.puts("\nPayload:\n\n" + readable.dump_evasion_options(m[:payload], @indent)) if m[:payload]
- $stdout.puts("\nEncoder:\n\n" + readable.dump_evasion_options(m[:encoder], @indent)) if m[:encoder]
- $stdout.puts("\nNOP:\n\n" + readable.dump_evasion_options(m[:nop], @indent)) if m[:nop]
- end
- def show_payloads(m)
- readable = Msf::Serializer::ReadableText
- txt = "Compatible payloads"
- $stdout.puts("\n" + readable.dump_compatible_payloads(m[:module], @indent, txt))
- end
- def show_targets(m)
- readable = Msf::Serializer::ReadableText
- $stdout.puts("\n" + readable.dump_exploit_targets(m[:module], @indent))
- end
- def show_actions(m)
- readable = Msf::Serializer::ReadableText
- $stdout.puts("\n" + readable.dump_module_actions(m[:module], @indent))
- end
- def show_check(m)
- begin
- if (code = m[:module].check_simple(
- 'LocalInput' => Rex::Ui::Text::Input::Stdio.new,
- 'LocalOutput' => Rex::Ui::Text::Output::Stdio.new))
- stat = (code == Msf::Exploit::CheckCode::Vulnerable) ? '[+]' : '[*]'
- $stdout.puts("#{stat} #{code[1]}")
- else
- $stdout.puts("Check failed: The state could not be determined.")
- end
- rescue
- $stdout.puts("Check failed: #{$!}")
- end
- end
- def execute_module(m)
- con = Msf::Ui::Console::Driver.new(
- Msf::Ui::Console::Driver::DefaultPrompt,
- Msf::Ui::Console::Driver::DefaultPromptChar,
- {
- 'Framework' => framework,
- # When I use msfcli, chances are I want speed, so ASCII art fanciness
- # probably isn't much of a big deal for me.
- 'DisableBanner' => true
- })
- module_class = (m[:module].fullname =~ /^auxiliary/ ? 'auxiliary' : 'exploit')
- con.run_single("use #{module_class}/#{m[:module].refname}")
- # Assign console parameters
- @args[:params].each do |arg|
- k,v = arg.split("=", 2)
- con.run_single("set #{k} #{v}")
- end
- # Run the exploit
- con.run_single("exploit")
- # If we have sessions or jobs, keep running
- if framework.sessions.length > 0 or framework.jobs.length > 0
- con.run
- else
- con.run_single("quit")
- end
- end
- #
- # Selects a mode chosen by the user and run it
- #
- def engage_mode(modules)
- case @args[:mode].downcase
- when 'h'
- usage
- when "s"
- show_summary(modules)
- when "o"
- show_options(modules)
- when "m"
- show_missing(modules)
- when "a"
- show_advanced(modules)
- when "i"
- show_ids_evasion(modules)
- when "p"
- if modules[:module].file_path =~ /auxiliary\//i
- $stdout.puts("\nError: This type of module does not support payloads")
- else
- show_payloads(modules)
- end
- when "t"
- puts
- if modules[:module].file_path =~ /auxiliary\//i
- $stdout.puts("\nError: This type of module does not support targets")
- else
- show_targets(modules)
- end
- when "ac"
- if modules[:module].kind_of?(Msf::Module::HasActions)
- show_actions(modules)
- else
- $stdout.puts("\nError: This type of module does not support actions")
- end
- when "c"
- show_check(modules)
- when "e"
- execute_module(modules)
- else
- usage("Invalid mode #{@args[:mode]}")
- end
- end
- def run!
- if @args[:module_name] == "-h"
- usage()
- exit
- end
- $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
- require 'msfenv'
- require 'msf/ui'
- require 'msf/base'
- if @args[:module_name].nil?
- ext = dump_module_list
- usage(nil, ext)
- exit
- end
- begin
- modules = init_modules
- rescue Rex::ArgumentParseError => e
- puts "[!] Error: #{e.message}\n\n"
- exit
- end
- if modules[:module].nil?
- usage("Invalid module: #{@args[:module_name]}")
- exit
- end
- # Process special var/val pairs...
- Msf::Ui::Common.process_cli_arguments(framework, @args[:params])
- engage_mode(modules)
- $stdout.puts
- end
- end
- if __FILE__ == $PROGRAM_NAME
- cli = Msfcli.new(ARGV)
- cli.run!
- end
|