Add standalone runner for external modules
parent
64c38ec6b8
commit
0dd89bf428
|
@ -5,6 +5,7 @@ class Msf::Modules::External
|
||||||
|
|
||||||
autoload :Bridge, 'msf/core/modules/external/bridge'
|
autoload :Bridge, 'msf/core/modules/external/bridge'
|
||||||
autoload :Message, 'msf/core/modules/external/message'
|
autoload :Message, 'msf/core/modules/external/message'
|
||||||
|
autoload :CLI, 'msf/core/modules/external/cli'
|
||||||
|
|
||||||
attr_reader :path
|
attr_reader :path
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
# CLI for interaction with modules outside of msfconsole
|
||||||
|
|
||||||
|
require 'optparse'
|
||||||
|
|
||||||
|
module Msf::Modules::External::CLI
|
||||||
|
def self.parse_options(mod)
|
||||||
|
action = 'run'
|
||||||
|
actions = ['run'] + mod.meta['capabilities']
|
||||||
|
args = mod.meta['options'].reduce({}) do |defaults, (n, opt)|
|
||||||
|
if opt['default'].nil?
|
||||||
|
if opt['required']
|
||||||
|
defaults
|
||||||
|
else
|
||||||
|
defaults[n] = nil
|
||||||
|
defaults
|
||||||
|
end
|
||||||
|
else
|
||||||
|
defaults[n] = opt['default']
|
||||||
|
defaults
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
op = OptionParser.new do |opts|
|
||||||
|
if $0 != mod.path
|
||||||
|
opts.banner = "Usage: #{$0} #{mod.path} [OPTIONS] [ACTION]"
|
||||||
|
end
|
||||||
|
opts.separator ""
|
||||||
|
|
||||||
|
opts.separator mod.meta['description']
|
||||||
|
opts.separator ""
|
||||||
|
|
||||||
|
opts.separator "Postitional arguments:"
|
||||||
|
opts.separator " ACTION: The action to take (#{actions.inspect})"
|
||||||
|
opts.separator ""
|
||||||
|
|
||||||
|
opts.separator "Required arguments:"
|
||||||
|
make_options opts, args, mod.meta['options'].select {|n, o| o['required'] && o['default'].nil?}
|
||||||
|
opts.separator ""
|
||||||
|
|
||||||
|
opts.separator "Optional arguments:"
|
||||||
|
make_options opts, args, mod.meta['options'].select {|n, o| !o['required'] || !o['default'].nil?}
|
||||||
|
|
||||||
|
opts.on '-h', '--help', 'Prints this help' do
|
||||||
|
$stderr.puts opts
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
extra = op.permute *ARGV
|
||||||
|
# If no extra args are given we use the default action
|
||||||
|
if extra.length == 1
|
||||||
|
action = extra.shift
|
||||||
|
elsif extra.length > 1
|
||||||
|
action = extra.shift
|
||||||
|
$stderr.puts "WARNING: unrecognized arguments #{extra.inspect}"
|
||||||
|
end
|
||||||
|
rescue OptionParser::InvalidArgument => e
|
||||||
|
$stderr.puts e.message
|
||||||
|
abort
|
||||||
|
rescue OptionParser::MissingArgument => e
|
||||||
|
$stderr.puts e.message
|
||||||
|
abort
|
||||||
|
end
|
||||||
|
|
||||||
|
required = mod.meta['options'].select {|_, o| o['required']}.map {|n, _| n}.sort
|
||||||
|
|
||||||
|
# Were we run with any non-module options if we need them?
|
||||||
|
if args.empty? && !required.empty?
|
||||||
|
$stderr.puts op
|
||||||
|
exit
|
||||||
|
# Did someone forget to add some options we need?
|
||||||
|
elsif (args.keys & required).sort != required
|
||||||
|
missing = required - (args.keys & required)
|
||||||
|
abort "Missing required option(s): #{missing.map {|o| '--' + o}.join ', '}"
|
||||||
|
end
|
||||||
|
|
||||||
|
unless action == 'run' || mod.meta['capabilities'].include?(action)
|
||||||
|
$stderr.puts "Invalid ACTION choice #{action.inspect} (choose from #{actions.inspect})"
|
||||||
|
abort
|
||||||
|
end
|
||||||
|
|
||||||
|
action =
|
||||||
|
case action
|
||||||
|
when 'run'; :run
|
||||||
|
when 'soft_check'; :soft_check
|
||||||
|
when 'hard_check'; :hard_check
|
||||||
|
end
|
||||||
|
[args, action]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.choose_type(t)
|
||||||
|
if t == 'int' or t == 'port'
|
||||||
|
Integer
|
||||||
|
elsif t == 'float'
|
||||||
|
Float
|
||||||
|
elsif t.match /range$/
|
||||||
|
Array
|
||||||
|
else # XXX TODO add validation for addresses and other MSF option types
|
||||||
|
String
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.make_options(parser, out, args)
|
||||||
|
args.each do |n, opt|
|
||||||
|
name = n.gsub '_', '-'
|
||||||
|
desc = if opt['default']
|
||||||
|
"#{opt['description']}, (default: #{opt['default']})"
|
||||||
|
else
|
||||||
|
opt['description']
|
||||||
|
end
|
||||||
|
parser.on "--#{name} #{n.upcase}", choose_type(opt['type']), desc do |arg|
|
||||||
|
out[n] = arg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
module Msf
|
||||||
|
module Modules
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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 'msf/core/modules/external'
|
||||||
|
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
module_path = ARGV.shift
|
||||||
|
|
||||||
|
# Usage when we don't have a module name
|
||||||
|
def usage(mod='MODULE_FILE', name='Run a module outside of Metasploit Framework')
|
||||||
|
$stderr.puts "Usage: solo.rb #{mod} [OPTIONS] [ACTION]"
|
||||||
|
$stderr.puts name
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_output(m)
|
||||||
|
message = m.params['message']
|
||||||
|
|
||||||
|
sigil = case m.params['level']
|
||||||
|
when 'error', 'warning'
|
||||||
|
'!'
|
||||||
|
when 'good'
|
||||||
|
'+'
|
||||||
|
else
|
||||||
|
'*'
|
||||||
|
end
|
||||||
|
|
||||||
|
$stderr.puts "[#{sigil}] #{message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_report(m)
|
||||||
|
puts "[+] Found #{m.params['type']}: #{JSON.generate m.params['data']}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if !module_path || module_path[0] == '-'
|
||||||
|
usage
|
||||||
|
else
|
||||||
|
mod = Msf::Modules::External.new module_path
|
||||||
|
args, method = Msf::Modules::External::CLI.parse_options mod
|
||||||
|
|
||||||
|
success = mod.exec(method: method, args: args) do |m|
|
||||||
|
begin
|
||||||
|
case m.method
|
||||||
|
when :message
|
||||||
|
log_output(m)
|
||||||
|
when :report
|
||||||
|
process_report(m)
|
||||||
|
when :reply
|
||||||
|
puts m.params['return']
|
||||||
|
end
|
||||||
|
rescue Interrupt => e
|
||||||
|
abort 'Exiting...'
|
||||||
|
rescue Exception => e
|
||||||
|
abort "Encountered an error: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
abort 'Module exited abnormally' if !success
|
||||||
|
end
|
Loading…
Reference in New Issue