Add standalone runner for external modules

GSoC/Meterpreter_Web_Console
Adam Cammack 2018-07-03 10:10:53 -05:00
parent 64c38ec6b8
commit 0dd89bf428
No known key found for this signature in database
GPG Key ID: C9378BA088092D66
3 changed files with 188 additions and 0 deletions

View File

@ -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

118
lib/msf/core/modules/external/cli.rb vendored Normal file
View File

@ -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

69
tools/modules/solo.rb Executable file
View File

@ -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