Retab all the things (except external/)
This commit is contained in:
+305
-305
@@ -8,339 +8,339 @@ require 'rex/ui/text/table'
|
||||
module Msf
|
||||
|
||||
class Plugin::Alias < Msf::Plugin
|
||||
class AliasCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
class AliasCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
attr_reader :aliases
|
||||
def initialize(driver)
|
||||
super(driver)
|
||||
@aliases = {}
|
||||
end
|
||||
attr_reader :aliases
|
||||
def initialize(driver)
|
||||
super(driver)
|
||||
@aliases = {}
|
||||
end
|
||||
|
||||
def name
|
||||
"Alias"
|
||||
end
|
||||
def name
|
||||
"Alias"
|
||||
end
|
||||
|
||||
@@alias_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-c" => [ true, "Clear an alias (* to clear all)."],
|
||||
"-f" => [ true, "Force an alias assignment." ]
|
||||
)
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands # driver.dispatcher_stack[3].commands
|
||||
{
|
||||
"alias" => "create or view an alias."
|
||||
# "alias_clear" => "clear an alias (or all aliases).",
|
||||
# "alias_force" => "Force an alias (such as to override)"
|
||||
}.merge(aliases) # make aliased commands available as commands of their own
|
||||
end
|
||||
@@alias_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-c" => [ true, "Clear an alias (* to clear all)."],
|
||||
"-f" => [ true, "Force an alias assignment." ]
|
||||
)
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands # driver.dispatcher_stack[3].commands
|
||||
{
|
||||
"alias" => "create or view an alias."
|
||||
# "alias_clear" => "clear an alias (or all aliases).",
|
||||
# "alias_force" => "Force an alias (such as to override)"
|
||||
}.merge(aliases) # make aliased commands available as commands of their own
|
||||
end
|
||||
|
||||
#
|
||||
# the main alias command handler
|
||||
#
|
||||
# usage: alias [options] [name [value]]
|
||||
def cmd_alias(*args)
|
||||
# we parse args manually instead of using @@alias.opts.parse to handle special cases
|
||||
case args.length
|
||||
when 0 # print the list of current aliases
|
||||
if @aliases.length == 0
|
||||
return print_status("No aliases currently defined")
|
||||
else
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => "Current Aliases",
|
||||
'Prefix' => "\n",
|
||||
'Postfix' => "\n",
|
||||
'Columns' => [ '', 'Alias Name', 'Alias Value' ]
|
||||
)
|
||||
# add 'alias' in front of each row so that the output can be copy pasted into an rc file if desired
|
||||
@aliases.each_pair do |key,val|
|
||||
tbl << ["alias",key,val]
|
||||
end
|
||||
return print(tbl.to_s)
|
||||
end
|
||||
when 1 # display the alias if one matches this name (or help)
|
||||
return cmd_alias_help if args[0] == "-h" or args[0] == "--help"
|
||||
if @aliases.keys.include?(args[0])
|
||||
print_status("\'#{args[0]}\' is aliased to \'#{@aliases[args[0]]}\'")
|
||||
else
|
||||
print_status("\'#{args[0]}\' is not currently aliased")
|
||||
end
|
||||
else # let's see if we can assign or clear the alias
|
||||
force = false
|
||||
clear = false
|
||||
# if using -f or -c, they must be the first arg, because -f/-c may also show up in the alias
|
||||
# value so we can't do something like if args.include("-f") or delete_if etc
|
||||
# we should never have to force and clear simultaneously.
|
||||
if args[0] == "-f"
|
||||
force = true
|
||||
args.shift
|
||||
elsif args[0] == "-c"
|
||||
clear = true
|
||||
args.shift
|
||||
end
|
||||
name = args.shift
|
||||
# alias name can NEVER be certain reserved words like 'alias', add any other reserved words here
|
||||
# We prevent the user from naming the alias "alias" cuz they could end up unable to clear the aliases,
|
||||
# for example you 'alias -f set unset and then 'alias -f alias sessions', now you're screwed. The byproduct
|
||||
# of this is that it prevents you from aliasing 'alias' to 'alias -f' etc, but that's acceptable
|
||||
reserved_words = [/^alias$/i]
|
||||
reserved_words.each do |regex|
|
||||
if name =~ regex
|
||||
print_error "You cannot use #{name} as the name for an alias, sorry"
|
||||
return false
|
||||
end
|
||||
end
|
||||
#
|
||||
# the main alias command handler
|
||||
#
|
||||
# usage: alias [options] [name [value]]
|
||||
def cmd_alias(*args)
|
||||
# we parse args manually instead of using @@alias.opts.parse to handle special cases
|
||||
case args.length
|
||||
when 0 # print the list of current aliases
|
||||
if @aliases.length == 0
|
||||
return print_status("No aliases currently defined")
|
||||
else
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => "Current Aliases",
|
||||
'Prefix' => "\n",
|
||||
'Postfix' => "\n",
|
||||
'Columns' => [ '', 'Alias Name', 'Alias Value' ]
|
||||
)
|
||||
# add 'alias' in front of each row so that the output can be copy pasted into an rc file if desired
|
||||
@aliases.each_pair do |key,val|
|
||||
tbl << ["alias",key,val]
|
||||
end
|
||||
return print(tbl.to_s)
|
||||
end
|
||||
when 1 # display the alias if one matches this name (or help)
|
||||
return cmd_alias_help if args[0] == "-h" or args[0] == "--help"
|
||||
if @aliases.keys.include?(args[0])
|
||||
print_status("\'#{args[0]}\' is aliased to \'#{@aliases[args[0]]}\'")
|
||||
else
|
||||
print_status("\'#{args[0]}\' is not currently aliased")
|
||||
end
|
||||
else # let's see if we can assign or clear the alias
|
||||
force = false
|
||||
clear = false
|
||||
# if using -f or -c, they must be the first arg, because -f/-c may also show up in the alias
|
||||
# value so we can't do something like if args.include("-f") or delete_if etc
|
||||
# we should never have to force and clear simultaneously.
|
||||
if args[0] == "-f"
|
||||
force = true
|
||||
args.shift
|
||||
elsif args[0] == "-c"
|
||||
clear = true
|
||||
args.shift
|
||||
end
|
||||
name = args.shift
|
||||
# alias name can NEVER be certain reserved words like 'alias', add any other reserved words here
|
||||
# We prevent the user from naming the alias "alias" cuz they could end up unable to clear the aliases,
|
||||
# for example you 'alias -f set unset and then 'alias -f alias sessions', now you're screwed. The byproduct
|
||||
# of this is that it prevents you from aliasing 'alias' to 'alias -f' etc, but that's acceptable
|
||||
reserved_words = [/^alias$/i]
|
||||
reserved_words.each do |regex|
|
||||
if name =~ regex
|
||||
print_error "You cannot use #{name} as the name for an alias, sorry"
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if clear
|
||||
# clear all aliases if "*"
|
||||
if name == "*"
|
||||
@aliases.keys.each do |a|
|
||||
deregister_alias(a)
|
||||
end
|
||||
print_status "Cleared all aliases"
|
||||
else # clear the named alias if it exists
|
||||
if @aliases.keys.include?(name)
|
||||
deregister_alias(name)
|
||||
print_status "Cleared alias #{name}"
|
||||
else
|
||||
print_error("#{name} is not a currently active alias")
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
# smash everything that's left together
|
||||
value = args.join(" ")
|
||||
value.strip!
|
||||
# value can NEVER be certain bad words like 'rm -rf /', add any other reserved words here
|
||||
# this is basic idiot protection, not meant to be impervious to subversive intentions
|
||||
reserved_words = [/^rm +(-rf|-r +-f|-f +-r) +\/.*$/]
|
||||
reserved_words.each do |regex|
|
||||
if value =~ regex
|
||||
print_error "You cannot use #{value} as the value for an alias, sorry"
|
||||
return false
|
||||
end
|
||||
end
|
||||
if clear
|
||||
# clear all aliases if "*"
|
||||
if name == "*"
|
||||
@aliases.keys.each do |a|
|
||||
deregister_alias(a)
|
||||
end
|
||||
print_status "Cleared all aliases"
|
||||
else # clear the named alias if it exists
|
||||
if @aliases.keys.include?(name)
|
||||
deregister_alias(name)
|
||||
print_status "Cleared alias #{name}"
|
||||
else
|
||||
print_error("#{name} is not a currently active alias")
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
# smash everything that's left together
|
||||
value = args.join(" ")
|
||||
value.strip!
|
||||
# value can NEVER be certain bad words like 'rm -rf /', add any other reserved words here
|
||||
# this is basic idiot protection, not meant to be impervious to subversive intentions
|
||||
reserved_words = [/^rm +(-rf|-r +-f|-f +-r) +\/.*$/]
|
||||
reserved_words.each do |regex|
|
||||
if value =~ regex
|
||||
print_error "You cannot use #{value} as the value for an alias, sorry"
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
is_valid_alias = is_valid_alias?(name,value)
|
||||
#print_good "Alias validity = #{is_valid_alias.to_s}"
|
||||
is_sys_cmd = Rex::FileUtils.find_full_path(name)
|
||||
is_already_alias = @aliases.keys.include?(name)
|
||||
if is_valid_alias and not is_sys_cmd and not is_already_alias
|
||||
register_alias(name, value)
|
||||
elsif force
|
||||
if not is_valid_alias
|
||||
print_status "The alias failed validation, but force is set so we allow this. This is often the case"
|
||||
print_status "when for instance 'exploit' is being overridden but msfconsole is not currently in the"
|
||||
print_status "exploit context (an exploit is not loaded), or you are overriding a system command"
|
||||
end
|
||||
register_alias(name, value)
|
||||
else
|
||||
print_error("#{name} already exists as a system command, use -f to force override") if is_sys_cmd
|
||||
print_error("#{name} is already an alias, use -f to force override") if is_already_alias
|
||||
if not is_valid_alias and not force
|
||||
print_error("\'#{name}\' is not a permitted name or \'#{value}\' is not valid/permitted")
|
||||
print_error("It's possible the responding dispatcher isn't loaded yet, try changing to the proper context or using -f to force")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
is_valid_alias = is_valid_alias?(name,value)
|
||||
#print_good "Alias validity = #{is_valid_alias.to_s}"
|
||||
is_sys_cmd = Rex::FileUtils.find_full_path(name)
|
||||
is_already_alias = @aliases.keys.include?(name)
|
||||
if is_valid_alias and not is_sys_cmd and not is_already_alias
|
||||
register_alias(name, value)
|
||||
elsif force
|
||||
if not is_valid_alias
|
||||
print_status "The alias failed validation, but force is set so we allow this. This is often the case"
|
||||
print_status "when for instance 'exploit' is being overridden but msfconsole is not currently in the"
|
||||
print_status "exploit context (an exploit is not loaded), or you are overriding a system command"
|
||||
end
|
||||
register_alias(name, value)
|
||||
else
|
||||
print_error("#{name} already exists as a system command, use -f to force override") if is_sys_cmd
|
||||
print_error("#{name} is already an alias, use -f to force override") if is_already_alias
|
||||
if not is_valid_alias and not force
|
||||
print_error("\'#{name}\' is not a permitted name or \'#{value}\' is not valid/permitted")
|
||||
print_error("It's possible the responding dispatcher isn't loaded yet, try changing to the proper context or using -f to force")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_alias_help
|
||||
print_line "Usage: alias [options] [name [value]]"
|
||||
print_line
|
||||
print(@@alias_opts.usage())
|
||||
end
|
||||
def cmd_alias_help
|
||||
print_line "Usage: alias [options] [name [value]]"
|
||||
print_line
|
||||
print(@@alias_opts.usage())
|
||||
end
|
||||
|
||||
#
|
||||
# Tab completion for the alias command
|
||||
#
|
||||
def cmd_alias_tabs(str, words)
|
||||
if words.length <= 1
|
||||
#puts "1 word or less"
|
||||
return @@alias_opts.fmt.keys + tab_complete_aliases_and_commands
|
||||
else
|
||||
#puts "more than 1 word"
|
||||
return tab_complete_aliases_and_commands
|
||||
end
|
||||
end
|
||||
#
|
||||
# Tab completion for the alias command
|
||||
#
|
||||
def cmd_alias_tabs(str, words)
|
||||
if words.length <= 1
|
||||
#puts "1 word or less"
|
||||
return @@alias_opts.fmt.keys + tab_complete_aliases_and_commands
|
||||
else
|
||||
#puts "more than 1 word"
|
||||
return tab_complete_aliases_and_commands
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
#
|
||||
# do everything needed to add an alias of +name+ having the value +value+
|
||||
#
|
||||
def register_alias(name, value)
|
||||
#TODO: begin rescue?
|
||||
#TODO: security concerns since we are using eval
|
||||
private
|
||||
#
|
||||
# do everything needed to add an alias of +name+ having the value +value+
|
||||
#
|
||||
def register_alias(name, value)
|
||||
#TODO: begin rescue?
|
||||
#TODO: security concerns since we are using eval
|
||||
|
||||
# define some class instance methods
|
||||
self.class_eval do
|
||||
# define a class instance method that will respond for the alias
|
||||
define_method "cmd_#{name}" do |*args|
|
||||
# just replace the alias w/the alias' value and run that
|
||||
driver.run_single("#{value} #{args.join(' ')}")
|
||||
end
|
||||
# define a class instance method that will tab complete the aliased command
|
||||
# we just proxy to the top-level tab complete function and let them handle it
|
||||
define_method "cmd_#{name}_tabs" do |str, words|
|
||||
# we need to repair the tab complete string/words and pass back
|
||||
# replace alias name with the root alias value
|
||||
value_words = value.split(/[\s\t\n]+/) # in case value is e.g. 'sessions -l'
|
||||
# valwords is now [sessions,-l]
|
||||
words[0] = value_words[0]
|
||||
# words[0] is now 'sessions' (was 'sue')
|
||||
value_words.shift # valwords is now ['-l']
|
||||
# insert any remaining parts of value and rebuild the line
|
||||
line = words.join(" ") + " " + value_words.join(" ") + " " + str
|
||||
# define some class instance methods
|
||||
self.class_eval do
|
||||
# define a class instance method that will respond for the alias
|
||||
define_method "cmd_#{name}" do |*args|
|
||||
# just replace the alias w/the alias' value and run that
|
||||
driver.run_single("#{value} #{args.join(' ')}")
|
||||
end
|
||||
# define a class instance method that will tab complete the aliased command
|
||||
# we just proxy to the top-level tab complete function and let them handle it
|
||||
define_method "cmd_#{name}_tabs" do |str, words|
|
||||
# we need to repair the tab complete string/words and pass back
|
||||
# replace alias name with the root alias value
|
||||
value_words = value.split(/[\s\t\n]+/) # in case value is e.g. 'sessions -l'
|
||||
# valwords is now [sessions,-l]
|
||||
words[0] = value_words[0]
|
||||
# words[0] is now 'sessions' (was 'sue')
|
||||
value_words.shift # valwords is now ['-l']
|
||||
# insert any remaining parts of value and rebuild the line
|
||||
line = words.join(" ") + " " + value_words.join(" ") + " " + str
|
||||
|
||||
#print_good "passing (#{line.strip}) back to tab_complete"
|
||||
# clear current tab_words
|
||||
driver.tab_words = []
|
||||
driver.tab_complete(line.strip)
|
||||
end
|
||||
# add a cmd_#{name}_help method
|
||||
define_method "cmd_#{name}_help" do |*args|
|
||||
driver.run_single("help #{value}")
|
||||
end
|
||||
end
|
||||
# add the alias to the list
|
||||
@aliases[name] = value
|
||||
end
|
||||
#print_good "passing (#{line.strip}) back to tab_complete"
|
||||
# clear current tab_words
|
||||
driver.tab_words = []
|
||||
driver.tab_complete(line.strip)
|
||||
end
|
||||
# add a cmd_#{name}_help method
|
||||
define_method "cmd_#{name}_help" do |*args|
|
||||
driver.run_single("help #{value}")
|
||||
end
|
||||
end
|
||||
# add the alias to the list
|
||||
@aliases[name] = value
|
||||
end
|
||||
|
||||
#
|
||||
# do everything required to remove an alias of name +name+
|
||||
#
|
||||
def deregister_alias(name)
|
||||
self.class_eval do
|
||||
# remove the class methods we created when the alias was registered
|
||||
remove_method("cmd_#{name}")
|
||||
remove_method("cmd_#{name}_tabs")
|
||||
remove_method("cmd_#{name}_help")
|
||||
end
|
||||
# remove the alias from the list of active aliases
|
||||
@aliases.delete(name)
|
||||
end
|
||||
#
|
||||
# do everything required to remove an alias of name +name+
|
||||
#
|
||||
def deregister_alias(name)
|
||||
self.class_eval do
|
||||
# remove the class methods we created when the alias was registered
|
||||
remove_method("cmd_#{name}")
|
||||
remove_method("cmd_#{name}_tabs")
|
||||
remove_method("cmd_#{name}_help")
|
||||
end
|
||||
# remove the alias from the list of active aliases
|
||||
@aliases.delete(name)
|
||||
end
|
||||
|
||||
#
|
||||
# Validate a proposed alias with the +name+ and having the value +value+
|
||||
#
|
||||
def is_valid_alias?(name,value)
|
||||
#print_good "Assessing validay for #{name} and #{value}"
|
||||
# we validate two things, the name and the value
|
||||
#
|
||||
# Validate a proposed alias with the +name+ and having the value +value+
|
||||
#
|
||||
def is_valid_alias?(name,value)
|
||||
#print_good "Assessing validay for #{name} and #{value}"
|
||||
# we validate two things, the name and the value
|
||||
|
||||
### name
|
||||
# we don't check if this alias name exists or if it's a console command already etc as -f can override
|
||||
# that so those need to be checked externally, we pretty much just check to see if the name is sane
|
||||
name.strip!
|
||||
bad_words = [/\*/] # add any additional "bad word" regexes here
|
||||
bad_words.each do |regex|
|
||||
# don't mess around, just return false in this case, prevents wasted processing
|
||||
return false if name =~ regex
|
||||
end
|
||||
### name
|
||||
# we don't check if this alias name exists or if it's a console command already etc as -f can override
|
||||
# that so those need to be checked externally, we pretty much just check to see if the name is sane
|
||||
name.strip!
|
||||
bad_words = [/\*/] # add any additional "bad word" regexes here
|
||||
bad_words.each do |regex|
|
||||
# don't mess around, just return false in this case, prevents wasted processing
|
||||
return false if name =~ regex
|
||||
end
|
||||
|
||||
### value
|
||||
# value is considered valid if it's a ref to a valid console cmd, a system executable, or an existing
|
||||
# alias AND isn't a "bad word"
|
||||
# Here we check for "bad words" to avoid for the value...value would have to NOT match these regexes
|
||||
# this is just basic idiot protection
|
||||
value.strip!
|
||||
bad_words = [/^msfconsole$/]
|
||||
bad_words.each do |regex|
|
||||
# don't mess around, just return false if we match
|
||||
return false if value =~ regex
|
||||
end
|
||||
### value
|
||||
# value is considered valid if it's a ref to a valid console cmd, a system executable, or an existing
|
||||
# alias AND isn't a "bad word"
|
||||
# Here we check for "bad words" to avoid for the value...value would have to NOT match these regexes
|
||||
# this is just basic idiot protection
|
||||
value.strip!
|
||||
bad_words = [/^msfconsole$/]
|
||||
bad_words.each do |regex|
|
||||
# don't mess around, just return false if we match
|
||||
return false if value =~ regex
|
||||
end
|
||||
|
||||
# we're only gonna validate the first part of the cmd, e.g. just ls from "ls -lh"
|
||||
value = value.split(" ").first
|
||||
if @aliases.keys.include?(value)
|
||||
return true
|
||||
else
|
||||
[value, value+".exe"].each do |cmd|
|
||||
if Rex::FileUtils.find_full_path(cmd)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
# we're only gonna validate the first part of the cmd, e.g. just ls from "ls -lh"
|
||||
value = value.split(" ").first
|
||||
if @aliases.keys.include?(value)
|
||||
return true
|
||||
else
|
||||
[value, value+".exe"].each do |cmd|
|
||||
if Rex::FileUtils.find_full_path(cmd)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# gather all the current commands the driver's dispatcher's have & check 'em
|
||||
driver.dispatcher_stack.each do |dispatcher|
|
||||
next unless dispatcher.respond_to?(:commands)
|
||||
next if (dispatcher.commands.nil?)
|
||||
next if (dispatcher.commands.length == 0)
|
||||
# gather all the current commands the driver's dispatcher's have & check 'em
|
||||
driver.dispatcher_stack.each do |dispatcher|
|
||||
next unless dispatcher.respond_to?(:commands)
|
||||
next if (dispatcher.commands.nil?)
|
||||
next if (dispatcher.commands.length == 0)
|
||||
|
||||
if dispatcher.respond_to?("cmd_#{value.split(" ").first}")
|
||||
#print_status "Dispatcher (#{dispatcher.name}) responds to cmd_#{value.split(" ").first}"
|
||||
return true
|
||||
else
|
||||
#print_status "Dispatcher (#{dispatcher.name}) does not respond to cmd_#{value.split(" ").first}"
|
||||
end
|
||||
end
|
||||
if dispatcher.respond_to?("cmd_#{value.split(" ").first}")
|
||||
#print_status "Dispatcher (#{dispatcher.name}) responds to cmd_#{value.split(" ").first}"
|
||||
return true
|
||||
else
|
||||
#print_status "Dispatcher (#{dispatcher.name}) does not respond to cmd_#{value.split(" ").first}"
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
#
|
||||
# Provide tab completion list for aliases and commands
|
||||
#
|
||||
def tab_complete_aliases_and_commands
|
||||
items = []
|
||||
# gather all the current commands the driver's dispatcher's have
|
||||
driver.dispatcher_stack.each do |dispatcher|
|
||||
next unless dispatcher.respond_to?(:commands)
|
||||
next if (dispatcher.commands.nil? or dispatcher.commands.length == 0)
|
||||
items << dispatcher.commands.keys
|
||||
end
|
||||
# add all the current aliases to the list
|
||||
items.concat(@aliases.keys)
|
||||
return items
|
||||
end
|
||||
#
|
||||
# Provide tab completion list for aliases and commands
|
||||
#
|
||||
def tab_complete_aliases_and_commands
|
||||
items = []
|
||||
# gather all the current commands the driver's dispatcher's have
|
||||
driver.dispatcher_stack.each do |dispatcher|
|
||||
next unless dispatcher.respond_to?(:commands)
|
||||
next if (dispatcher.commands.nil? or dispatcher.commands.length == 0)
|
||||
items << dispatcher.commands.keys
|
||||
end
|
||||
# add all the current aliases to the list
|
||||
items.concat(@aliases.keys)
|
||||
return items
|
||||
end
|
||||
|
||||
end # end AliasCommandDispatcher class
|
||||
end # end AliasCommandDispatcher class
|
||||
|
||||
#
|
||||
# The constructor is called when an instance of the plugin is created. The
|
||||
# framework instance that the plugin is being associated with is passed in
|
||||
# the framework parameter. Plugins should call the parent constructor when
|
||||
# inheriting from Msf::Plugin to ensure that the framework attribute on
|
||||
# their instance gets set.
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
#
|
||||
# The constructor is called when an instance of the plugin is created. The
|
||||
# framework instance that the plugin is being associated with is passed in
|
||||
# the framework parameter. Plugins should call the parent constructor when
|
||||
# inheriting from Msf::Plugin to ensure that the framework attribute on
|
||||
# their instance gets set.
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
## Register the commands above
|
||||
add_console_dispatcher(AliasCommandDispatcher)
|
||||
end
|
||||
## Register the commands above
|
||||
add_console_dispatcher(AliasCommandDispatcher)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# The cleanup routine for plugins gives them a chance to undo any actions
|
||||
# they may have done to the framework. For instance, if a console
|
||||
# dispatcher was added, then it should be removed in the cleanup routine.
|
||||
#
|
||||
def cleanup
|
||||
# If we had previously registered a console dispatcher with the console,
|
||||
# deregister it now.
|
||||
remove_console_dispatcher('Alias')
|
||||
#
|
||||
# The cleanup routine for plugins gives them a chance to undo any actions
|
||||
# they may have done to the framework. For instance, if a console
|
||||
# dispatcher was added, then it should be removed in the cleanup routine.
|
||||
#
|
||||
def cleanup
|
||||
# If we had previously registered a console dispatcher with the console,
|
||||
# deregister it now.
|
||||
remove_console_dispatcher('Alias')
|
||||
|
||||
# we don't need to remove class methods we added because they were added to
|
||||
# AliasCommandDispatcher class
|
||||
end
|
||||
# we don't need to remove class methods we added because they were added to
|
||||
# AliasCommandDispatcher class
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a short, friendly name for the plugin.
|
||||
#
|
||||
def name
|
||||
"alias"
|
||||
end
|
||||
#
|
||||
# This method returns a short, friendly name for the plugin.
|
||||
#
|
||||
def name
|
||||
"alias"
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a brief description of the plugin. It should be no
|
||||
# more than 60 characters, but there are no hard limits.
|
||||
#
|
||||
def desc
|
||||
"Adds the ability to alias console commands"
|
||||
end
|
||||
#
|
||||
# This method returns a brief description of the plugin. It should be no
|
||||
# more than 60 characters, but there are no hard limits.
|
||||
#
|
||||
def desc
|
||||
"Adds the ability to alias console commands"
|
||||
end
|
||||
|
||||
end ## End Plugin Class
|
||||
end ## End Module
|
||||
|
||||
+27
-27
@@ -5,37 +5,37 @@
|
||||
|
||||
module Msf
|
||||
class Plugin::AutoAddRoute < Msf::Plugin
|
||||
include Msf::SessionEvent
|
||||
def name; 'auto_add_route'; end
|
||||
include Msf::SessionEvent
|
||||
def name; 'auto_add_route'; end
|
||||
|
||||
def desc
|
||||
"Adds routes for any new subnets whenever a session opens"
|
||||
end
|
||||
def desc
|
||||
"Adds routes for any new subnets whenever a session opens"
|
||||
end
|
||||
|
||||
def on_session_open(session)
|
||||
return if not session.type == 'meterpreter'
|
||||
session.load_stdapi
|
||||
sb = Rex::Socket::SwitchBoard.instance
|
||||
session.net.config.each_route { |route|
|
||||
# Remove multicast and loopback interfaces
|
||||
next if route.subnet =~ /^(224\.|127\.)/
|
||||
next if route.subnet == '0.0.0.0'
|
||||
next if route.netmask == '255.255.255.255'
|
||||
if not sb.route_exists?(route.subnet, route.netmask)
|
||||
print_status("AutoAddRoute: Routing new subnet #{route.subnet}/#{route.netmask} through session #{session.sid}")
|
||||
sb.add_route(route.subnet, route.netmask, session)
|
||||
end
|
||||
}
|
||||
end
|
||||
def on_session_open(session)
|
||||
return if not session.type == 'meterpreter'
|
||||
session.load_stdapi
|
||||
sb = Rex::Socket::SwitchBoard.instance
|
||||
session.net.config.each_route { |route|
|
||||
# Remove multicast and loopback interfaces
|
||||
next if route.subnet =~ /^(224\.|127\.)/
|
||||
next if route.subnet == '0.0.0.0'
|
||||
next if route.netmask == '255.255.255.255'
|
||||
if not sb.route_exists?(route.subnet, route.netmask)
|
||||
print_status("AutoAddRoute: Routing new subnet #{route.subnet}/#{route.netmask} through session #{session.sid}")
|
||||
sb.add_route(route.subnet, route.netmask, session)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
self.framework.events.add_session_subscriber(self)
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
self.framework.events.add_session_subscriber(self)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
self.framework.events.remove_session_subscriber(self)
|
||||
end
|
||||
def cleanup
|
||||
self.framework.events.remove_session_subscriber(self)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
+80
-80
@@ -9,109 +9,109 @@
|
||||
module Msf
|
||||
|
||||
class Plugin::CredCollect < Msf::Plugin
|
||||
include Msf::SessionEvent
|
||||
include Msf::SessionEvent
|
||||
|
||||
class CredCollectCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
class CredCollectCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"credcollect"
|
||||
end
|
||||
def name
|
||||
"credcollect"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
"db_hashes" => "Dumps hashes (deprecated: use 'creds -s smb')",
|
||||
"db_tokens" => "Dumps tokens (deprecated: use 'notes -t smb_token')"
|
||||
}
|
||||
end
|
||||
def commands
|
||||
{
|
||||
"db_hashes" => "Dumps hashes (deprecated: use 'creds -s smb')",
|
||||
"db_tokens" => "Dumps tokens (deprecated: use 'notes -t smb_token')"
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_db_hashes()
|
||||
print_error ""
|
||||
print_error "db_hashes is deprecated. Use 'creds -s smb' instead."
|
||||
print_error ""
|
||||
end
|
||||
def cmd_db_hashes()
|
||||
print_error ""
|
||||
print_error "db_hashes is deprecated. Use 'creds -s smb' instead."
|
||||
print_error ""
|
||||
end
|
||||
|
||||
def cmd_db_tokens()
|
||||
print_error ""
|
||||
print_error "db_tokens is deprecated. Use 'notes -t smb_token' instead."
|
||||
print_error ""
|
||||
end
|
||||
def cmd_db_tokens()
|
||||
print_error ""
|
||||
print_error "db_tokens is deprecated. Use 'notes -t smb_token' instead."
|
||||
print_error ""
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def on_session_open(session)
|
||||
def on_session_open(session)
|
||||
|
||||
return if not self.framework.db.active
|
||||
return if not self.framework.db.active
|
||||
|
||||
print_status("This is CredCollect, I have the conn!")
|
||||
print_status("This is CredCollect, I have the conn!")
|
||||
|
||||
if (session.type == "meterpreter")
|
||||
if (session.type == "meterpreter")
|
||||
|
||||
# Make sure we're rockin Priv and Incognito
|
||||
session.core.use("priv")
|
||||
session.core.use("incognito")
|
||||
# Make sure we're rockin Priv and Incognito
|
||||
session.core.use("priv")
|
||||
session.core.use("incognito")
|
||||
|
||||
# It wasn't me mom! Stinko did it!
|
||||
hashes = session.priv.sam_hashes
|
||||
# It wasn't me mom! Stinko did it!
|
||||
hashes = session.priv.sam_hashes
|
||||
|
||||
# Target infos for the db record
|
||||
addr = session.sock.peerhost
|
||||
# This ought to read from the exploit's datastore.
|
||||
# Use the meterpreter script if you need to control it.
|
||||
smb_port = 445
|
||||
# Target infos for the db record
|
||||
addr = session.sock.peerhost
|
||||
# This ought to read from the exploit's datastore.
|
||||
# Use the meterpreter script if you need to control it.
|
||||
smb_port = 445
|
||||
|
||||
# Record hashes to the running db instance
|
||||
hashes.each do |hash|
|
||||
data = {}
|
||||
data[:host] = addr
|
||||
data[:port] = smb_port
|
||||
data[:sname] = 'smb'
|
||||
data[:user] = hash.user_name
|
||||
data[:pass] = hash.lanman + ":" + hash.ntlm
|
||||
data[:type] = "smb_hash"
|
||||
data[:active] = true
|
||||
# Record hashes to the running db instance
|
||||
hashes.each do |hash|
|
||||
data = {}
|
||||
data[:host] = addr
|
||||
data[:port] = smb_port
|
||||
data[:sname] = 'smb'
|
||||
data[:user] = hash.user_name
|
||||
data[:pass] = hash.lanman + ":" + hash.ntlm
|
||||
data[:type] = "smb_hash"
|
||||
data[:active] = true
|
||||
|
||||
self.framework.db.report_auth_info(data)
|
||||
end
|
||||
self.framework.db.report_auth_info(data)
|
||||
end
|
||||
|
||||
# Record user tokens
|
||||
tokens = session.incognito.incognito_list_tokens(0).values
|
||||
# Meh, tokens come to us as a formatted string
|
||||
tokens = tokens.join.strip!.split("\n")
|
||||
# Record user tokens
|
||||
tokens = session.incognito.incognito_list_tokens(0).values
|
||||
# Meh, tokens come to us as a formatted string
|
||||
tokens = tokens.join.strip!.split("\n")
|
||||
|
||||
tokens.each do |token|
|
||||
data = {}
|
||||
data[:host] = addr
|
||||
data[:type] = 'smb_token'
|
||||
data[:data] = token
|
||||
data[:update] = :unique_data
|
||||
tokens.each do |token|
|
||||
data = {}
|
||||
data[:host] = addr
|
||||
data[:type] = 'smb_token'
|
||||
data[:data] = token
|
||||
data[:update] = :unique_data
|
||||
|
||||
self.framework.db.report_note(data)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.framework.db.report_note(data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def on_session_close(session,reason='')
|
||||
end
|
||||
def on_session_close(session,reason='')
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
self.framework.events.add_session_subscriber(self)
|
||||
add_console_dispatcher(CredCollectCommandDispatcher)
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
self.framework.events.add_session_subscriber(self)
|
||||
add_console_dispatcher(CredCollectCommandDispatcher)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
self.framework.events.remove_session_subscriber(self)
|
||||
remove_console_dispatcher('credcollect')
|
||||
end
|
||||
def cleanup
|
||||
self.framework.events.remove_session_subscriber(self)
|
||||
remove_console_dispatcher('credcollect')
|
||||
end
|
||||
|
||||
def name
|
||||
"db_credcollect"
|
||||
end
|
||||
def name
|
||||
"db_credcollect"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Automatically grabs hashes and tokens from meterpreter session events and stores them in the db"
|
||||
end
|
||||
def desc
|
||||
"Automatically grabs hashes and tokens from meterpreter session events and stores them in the db"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
+44
-44
@@ -14,62 +14,62 @@ module Msf
|
||||
|
||||
class Plugin::DB_Tracer < Msf::Plugin
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a socket communication tracker
|
||||
#
|
||||
###
|
||||
class DBTracerEventHandler
|
||||
include Rex::Socket::Comm::Events
|
||||
###
|
||||
#
|
||||
# This class implements a socket communication tracker
|
||||
#
|
||||
###
|
||||
class DBTracerEventHandler
|
||||
include Rex::Socket::Comm::Events
|
||||
|
||||
def on_before_socket_create(comm, param)
|
||||
end
|
||||
def on_before_socket_create(comm, param)
|
||||
end
|
||||
|
||||
def on_socket_created(comm, sock, param)
|
||||
# Ignore local listening sockets
|
||||
return if not sock.peerhost
|
||||
def on_socket_created(comm, sock, param)
|
||||
# Ignore local listening sockets
|
||||
return if not sock.peerhost
|
||||
|
||||
if (sock.peerhost != '0.0.0.0' and sock.peerport)
|
||||
if (sock.peerhost != '0.0.0.0' and sock.peerport)
|
||||
|
||||
# Ignore sockets that didn't set up their context
|
||||
# to hold the framework in 'Msf'
|
||||
return if not param.context['Msf']
|
||||
# Ignore sockets that didn't set up their context
|
||||
# to hold the framework in 'Msf'
|
||||
return if not param.context['Msf']
|
||||
|
||||
host = param.context['Msf'].db.find_or_create_host(:host => sock.peerhost, :state => Msf::HostState::Alive)
|
||||
return if not host
|
||||
host = param.context['Msf'].db.find_or_create_host(:host => sock.peerhost, :state => Msf::HostState::Alive)
|
||||
return if not host
|
||||
|
||||
param.context['Msf'].db.report_service(:host => host, :proto => param.proto, :port => sock.peerport)
|
||||
end
|
||||
end
|
||||
end
|
||||
param.context['Msf'].db.report_service(:host => host, :proto => param.proto, :port => sock.peerport)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
if(not framework.db.active)
|
||||
raise PluginLoadError.new("The database backend has not been initialized")
|
||||
end
|
||||
framework.plugins.each { |plugin|
|
||||
if (plugin.class == Msf::Plugin::DB_Tracer)
|
||||
raise PluginLoadError.new("This plugin should not be loaded more than once")
|
||||
end
|
||||
}
|
||||
if(not framework.db.active)
|
||||
raise PluginLoadError.new("The database backend has not been initialized")
|
||||
end
|
||||
framework.plugins.each { |plugin|
|
||||
if (plugin.class == Msf::Plugin::DB_Tracer)
|
||||
raise PluginLoadError.new("This plugin should not be loaded more than once")
|
||||
end
|
||||
}
|
||||
|
||||
@eh = DBTracerEventHandler.new
|
||||
Rex::Socket::Comm::Local.register_event_handler(@eh)
|
||||
end
|
||||
@eh = DBTracerEventHandler.new
|
||||
Rex::Socket::Comm::Local.register_event_handler(@eh)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
Rex::Socket::Comm::Local.deregister_event_handler(@eh)
|
||||
end
|
||||
def cleanup
|
||||
Rex::Socket::Comm::Local.deregister_event_handler(@eh)
|
||||
end
|
||||
|
||||
def name
|
||||
"db_tracker"
|
||||
end
|
||||
def name
|
||||
"db_tracker"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Monitors socket calls and updates the database backend"
|
||||
end
|
||||
def desc
|
||||
"Monitors socket calls and updates the database backend"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
+56
-56
@@ -12,74 +12,74 @@ module Msf
|
||||
###
|
||||
class Plugin::Editor < Msf::Plugin
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a single edit command.
|
||||
#
|
||||
###
|
||||
class EditorCommandDispatcher
|
||||
include Msf::Ui::Console::ModuleCommandDispatcher
|
||||
###
|
||||
#
|
||||
# This class implements a single edit command.
|
||||
#
|
||||
###
|
||||
class EditorCommandDispatcher
|
||||
include Msf::Ui::Console::ModuleCommandDispatcher
|
||||
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
def name
|
||||
"Editor"
|
||||
end
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
def name
|
||||
"Editor"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
# Don't update super here since we don't want the commands from
|
||||
# super, just the methods
|
||||
{
|
||||
"edit" => "A handy editor commmand"
|
||||
}
|
||||
end
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
# Don't update super here since we don't want the commands from
|
||||
# super, just the methods
|
||||
{
|
||||
"edit" => "A handy editor commmand"
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# This method handles the edit command.
|
||||
#
|
||||
def cmd_edit(*args)
|
||||
print_line("Launching editor...")
|
||||
#
|
||||
# This method handles the edit command.
|
||||
#
|
||||
def cmd_edit(*args)
|
||||
print_line("Launching editor...")
|
||||
|
||||
e = Rex::Compat.getenv("EDITOR") || "vi"
|
||||
e = Rex::Compat.getenv("EDITOR") || "vi"
|
||||
|
||||
if (not mod) or (not (path = mod.file_path))
|
||||
print_line("Error: No active module selected")
|
||||
return nil
|
||||
end
|
||||
if (not mod) or (not (path = mod.file_path))
|
||||
print_line("Error: No active module selected")
|
||||
return nil
|
||||
end
|
||||
|
||||
ret = system(e, path)
|
||||
if not ret
|
||||
print_line("Failed to execute your editor (#{e})")
|
||||
return
|
||||
end
|
||||
ret = system(e, path)
|
||||
if not ret
|
||||
print_line("Failed to execute your editor (#{e})")
|
||||
return
|
||||
end
|
||||
|
||||
reload
|
||||
ret
|
||||
end
|
||||
end
|
||||
reload
|
||||
ret
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
# console dispatcher commands.
|
||||
add_console_dispatcher(EditorCommandDispatcher)
|
||||
end
|
||||
# console dispatcher commands.
|
||||
add_console_dispatcher(EditorCommandDispatcher)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('Editor')
|
||||
end
|
||||
def cleanup
|
||||
remove_console_dispatcher('Editor')
|
||||
end
|
||||
|
||||
def name
|
||||
"editor"
|
||||
end
|
||||
def name
|
||||
"editor"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Simple Editor Plugin"
|
||||
end
|
||||
def desc
|
||||
"Simple Editor Plugin"
|
||||
end
|
||||
|
||||
protected
|
||||
end
|
||||
|
||||
+26
-26
@@ -6,34 +6,34 @@
|
||||
module Msf
|
||||
|
||||
class Plugin::EventTester < Msf::Plugin
|
||||
class Subscriber
|
||||
def respond_to?(name)
|
||||
# Why yes, I can do that.
|
||||
true
|
||||
end
|
||||
def method_missing(name, *args)
|
||||
$stdout.puts("Event fired: #{name}(#{args.join(", ")})")
|
||||
end
|
||||
end
|
||||
class Subscriber
|
||||
def respond_to?(name)
|
||||
# Why yes, I can do that.
|
||||
true
|
||||
end
|
||||
def method_missing(name, *args)
|
||||
$stdout.puts("Event fired: #{name}(#{args.join(", ")})")
|
||||
end
|
||||
end
|
||||
|
||||
def name; "event_tester"; end
|
||||
def name; "event_tester"; end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
@subscriber = Subscriber.new
|
||||
framework.events.add_exploit_subscriber(@subscriber)
|
||||
framework.events.add_session_subscriber(@subscriber)
|
||||
framework.events.add_general_subscriber(@subscriber)
|
||||
framework.events.add_db_subscriber(@subscriber)
|
||||
framework.events.add_ui_subscriber(@subscriber)
|
||||
end
|
||||
def cleanup
|
||||
framework.events.remove_exploit_subscriber(@subscriber)
|
||||
framework.events.remove_session_subscriber(@subscriber)
|
||||
framework.events.remove_general_subscriber(@subscriber)
|
||||
framework.events.remove_db_subscriber(@subscriber)
|
||||
framework.events.remove_ui_subscriber(@subscriber)
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
@subscriber = Subscriber.new
|
||||
framework.events.add_exploit_subscriber(@subscriber)
|
||||
framework.events.add_session_subscriber(@subscriber)
|
||||
framework.events.add_general_subscriber(@subscriber)
|
||||
framework.events.add_db_subscriber(@subscriber)
|
||||
framework.events.add_ui_subscriber(@subscriber)
|
||||
end
|
||||
def cleanup
|
||||
framework.events.remove_exploit_subscriber(@subscriber)
|
||||
framework.events.remove_session_subscriber(@subscriber)
|
||||
framework.events.remove_general_subscriber(@subscriber)
|
||||
framework.events.remove_db_subscriber(@subscriber)
|
||||
framework.events.remove_ui_subscriber(@subscriber)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+73
-73
@@ -12,95 +12,95 @@ module Msf
|
||||
###
|
||||
class Plugin::FFAutoRegen < Msf::Plugin
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a single edit command.
|
||||
#
|
||||
###
|
||||
class FFAutoRegenCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
###
|
||||
#
|
||||
# This class implements a single edit command.
|
||||
#
|
||||
###
|
||||
class FFAutoRegenCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
def name
|
||||
"FFAutoRegen"
|
||||
end
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
def name
|
||||
"FFAutoRegen"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"ffautoregen" => "Automatically regenerate the document when the exploti source changes"
|
||||
}
|
||||
end
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"ffautoregen" => "Automatically regenerate the document when the exploti source changes"
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# This method handles the command.
|
||||
#
|
||||
def cmd_ffautoregen(*args)
|
||||
if (not active_module) or (not (path = active_module.file_path))
|
||||
print_line("Error: No active module selected")
|
||||
return nil
|
||||
end
|
||||
#
|
||||
# This method handles the command.
|
||||
#
|
||||
def cmd_ffautoregen(*args)
|
||||
if (not active_module) or (not (path = active_module.file_path))
|
||||
print_line("Error: No active module selected")
|
||||
return nil
|
||||
end
|
||||
|
||||
last = mt = File.stat(path).mtime
|
||||
last = mt = File.stat(path).mtime
|
||||
|
||||
loop {
|
||||
sleep(1)
|
||||
mt = File.stat(path).mtime
|
||||
loop {
|
||||
sleep(1)
|
||||
mt = File.stat(path).mtime
|
||||
|
||||
if (mt != last)
|
||||
last = mt
|
||||
if (mt != last)
|
||||
last = mt
|
||||
|
||||
omod = active_module
|
||||
nmod = framework.modules.reload_module(active_module)
|
||||
if not nmod
|
||||
print_line("Error: Failed to reload module, trying again on next change...")
|
||||
next
|
||||
end
|
||||
omod = active_module
|
||||
nmod = framework.modules.reload_module(active_module)
|
||||
if not nmod
|
||||
print_line("Error: Failed to reload module, trying again on next change...")
|
||||
next
|
||||
end
|
||||
|
||||
active_module = nmod
|
||||
active_module = nmod
|
||||
|
||||
jobify = false
|
||||
payload = nmod.datastore['PAYLOAD']
|
||||
encoder = nmod.datastore['ENCODER']
|
||||
target = nmod.datastore['TARGET']
|
||||
nop = nmod.datastore['NOP']
|
||||
jobify = false
|
||||
payload = nmod.datastore['PAYLOAD']
|
||||
encoder = nmod.datastore['ENCODER']
|
||||
target = nmod.datastore['TARGET']
|
||||
nop = nmod.datastore['NOP']
|
||||
|
||||
nmod.exploit_simple(
|
||||
'Encoder' => encoder,
|
||||
'Payload' => payload,
|
||||
'Target' => target,
|
||||
'Nop' => nop,
|
||||
nmod.exploit_simple(
|
||||
'Encoder' => encoder,
|
||||
'Payload' => payload,
|
||||
'Target' => target,
|
||||
'Nop' => nop,
|
||||
# 'OptionStr' => opt_str,
|
||||
'LocalInput' => driver.input,
|
||||
'LocalOutput' => driver.output,
|
||||
'RunAsJob' => jobify)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
'LocalInput' => driver.input,
|
||||
'LocalOutput' => driver.output,
|
||||
'RunAsJob' => jobify)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
# console dispatcher commands.
|
||||
add_console_dispatcher(FFAutoRegenCommandDispatcher)
|
||||
end
|
||||
# console dispatcher commands.
|
||||
add_console_dispatcher(FFAutoRegenCommandDispatcher)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('FFAutoRegen')
|
||||
end
|
||||
def cleanup
|
||||
remove_console_dispatcher('FFAutoRegen')
|
||||
end
|
||||
|
||||
def name
|
||||
"ffautoregen"
|
||||
end
|
||||
def name
|
||||
"ffautoregen"
|
||||
end
|
||||
|
||||
def desc
|
||||
"FileFormat AutoRegen Plugin"
|
||||
end
|
||||
def desc
|
||||
"FileFormat AutoRegen Plugin"
|
||||
end
|
||||
|
||||
protected
|
||||
end
|
||||
|
||||
+75
-75
@@ -15,44 +15,44 @@ module Msf
|
||||
|
||||
class Plugin::IPSFilter < Msf::Plugin
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a socket communication logger
|
||||
#
|
||||
###
|
||||
class IPSSocketEventHandler
|
||||
include Rex::Socket::Comm::Events
|
||||
###
|
||||
#
|
||||
# This class implements a socket communication logger
|
||||
#
|
||||
###
|
||||
class IPSSocketEventHandler
|
||||
include Rex::Socket::Comm::Events
|
||||
|
||||
def on_before_socket_create(comm, param)
|
||||
end
|
||||
def on_before_socket_create(comm, param)
|
||||
end
|
||||
|
||||
def on_socket_created(comm, sock, param)
|
||||
# Sockets created by the exploit have MsfExploit set and MsfPayload not set
|
||||
if (param.context['MsfExploit'] and (! param.context['MsfPayload'] ))
|
||||
sock.extend(IPSFilter::SocketTracer)
|
||||
sock.context = param.context
|
||||
end
|
||||
end
|
||||
end
|
||||
def on_socket_created(comm, sock, param)
|
||||
# Sockets created by the exploit have MsfExploit set and MsfPayload not set
|
||||
if (param.context['MsfExploit'] and (! param.context['MsfPayload'] ))
|
||||
sock.extend(IPSFilter::SocketTracer)
|
||||
sock.context = param.context
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
@ips_eh = IPSSocketEventHandler.new
|
||||
Rex::Socket::Comm::Local.register_event_handler(@ips_eh)
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
@ips_eh = IPSSocketEventHandler.new
|
||||
Rex::Socket::Comm::Local.register_event_handler(@ips_eh)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
Rex::Socket::Comm::Local.deregister_event_handler(@ips_eh)
|
||||
end
|
||||
def cleanup
|
||||
Rex::Socket::Comm::Local.deregister_event_handler(@ips_eh)
|
||||
end
|
||||
|
||||
def name
|
||||
"ips_filter"
|
||||
end
|
||||
def name
|
||||
"ips_filter"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Scans all outgoing data to see if it matches a known IPS signature"
|
||||
end
|
||||
def desc
|
||||
"Scans all outgoing data to see if it matches a known IPS signature"
|
||||
end
|
||||
|
||||
protected
|
||||
end
|
||||
@@ -63,57 +63,57 @@ end
|
||||
module IPSFilter
|
||||
module SocketTracer
|
||||
|
||||
attr_accessor :context
|
||||
attr_accessor :context
|
||||
|
||||
# Hook the write method
|
||||
def write(buf, opts = {})
|
||||
if (ips_match(buf))
|
||||
print_error "Outbound write blocked due to possible signature match"
|
||||
return 0
|
||||
end
|
||||
super(buf, opts)
|
||||
end
|
||||
# Hook the write method
|
||||
def write(buf, opts = {})
|
||||
if (ips_match(buf))
|
||||
print_error "Outbound write blocked due to possible signature match"
|
||||
return 0
|
||||
end
|
||||
super(buf, opts)
|
||||
end
|
||||
|
||||
# Hook the read method
|
||||
def read(length = nil, opts = {})
|
||||
r = super(length, opts)
|
||||
if (ips_match(r))
|
||||
print_error "Incoming read may match a known signature"
|
||||
end
|
||||
return r
|
||||
end
|
||||
# Hook the read method
|
||||
def read(length = nil, opts = {})
|
||||
r = super(length, opts)
|
||||
if (ips_match(r))
|
||||
print_error "Incoming read may match a known signature"
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
def close(*args)
|
||||
super(*args)
|
||||
end
|
||||
def close(*args)
|
||||
super(*args)
|
||||
end
|
||||
|
||||
def ips_match(data)
|
||||
lp = localport
|
||||
rp = peerport
|
||||
def ips_match(data)
|
||||
lp = localport
|
||||
rp = peerport
|
||||
|
||||
SIGS.each do |s|
|
||||
begin
|
||||
r = Regexp.new(s[1])
|
||||
if (data.match(r))
|
||||
print_error "Matched IPS signature #{s[0]}"
|
||||
return true
|
||||
end
|
||||
rescue ::Exception => e
|
||||
print_error "Compiled error: #{s[1]}"
|
||||
end
|
||||
end
|
||||
SIGS.each do |s|
|
||||
begin
|
||||
r = Regexp.new(s[1])
|
||||
if (data.match(r))
|
||||
print_error "Matched IPS signature #{s[0]}"
|
||||
return true
|
||||
end
|
||||
rescue ::Exception => e
|
||||
print_error "Compiled error: #{s[1]}"
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Extend this as needed :-)
|
||||
SIGS =
|
||||
[
|
||||
['DCOM.C', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"],
|
||||
['BLASTER', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"],
|
||||
['REMACT', ".*\xb8\x4a\x9f\x4d\x1c\\}\xcf\x11\x86\x1e\x00\x20\xaf\x6e.*"],
|
||||
['x86 NOP SLED', "\x90\x90"],
|
||||
]
|
||||
# Extend this as needed :-)
|
||||
SIGS =
|
||||
[
|
||||
['DCOM.C', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"],
|
||||
['BLASTER', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"],
|
||||
['REMACT', ".*\xb8\x4a\x9f\x4d\x1c\\}\xcf\x11\x86\x1e\x00\x20\xaf\x6e.*"],
|
||||
['x86 NOP SLED', "\x90\x90"],
|
||||
]
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
+562
-562
@@ -10,568 +10,568 @@ require 'yaml'
|
||||
module Msf
|
||||
|
||||
class Plugin::Lab < Msf::Plugin
|
||||
class LabCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
attr_accessor :controller
|
||||
|
||||
def initialize(driver)
|
||||
super(driver)
|
||||
@controller = nil
|
||||
|
||||
#
|
||||
# Require the lab gem, but fail nicely if it's not there.
|
||||
#
|
||||
begin
|
||||
require 'lab'
|
||||
rescue LoadError
|
||||
raise "WARNING: Lab gem not found, Please 'gem install lab'"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"lab_help" => "lab_help <lab command> - Show that command's description.",
|
||||
"lab_show" => "lab_show - show all vms in the lab.",
|
||||
"lab_search" => "lab_search - search local vms in the lab.",
|
||||
"lab_search_tags" => "lab_search_tag - search local vms in the lab.",
|
||||
#"lab_search_remote" => "lab_search_remote - search remote vms in the lab.",
|
||||
"lab_show_running" => "lab_show_running - show running vms.",
|
||||
"lab_load" => "lab_load [file] - load a lab definition from disk.",
|
||||
"lab_save" => "lab_save [filename] - persist a lab definition in a file.",
|
||||
"lab_load_running" => "lab_load_running [type] [user] [host] - use the running vms to create a lab.",
|
||||
"lab_load_config" => "lab_load_config [type] [user] [host] - use the vms in the config to create a lab.",
|
||||
"lab_load_dir" => "lab_load_dir [type] [directory] - create a lab from a specified directory.",
|
||||
"lab_clear" => "lab_clear - clear the running lab.",
|
||||
"lab_start" => "lab_start [vmid+|all] start the specified vm.",
|
||||
"lab_reset" => "lab_reset [vmid+|all] reset the specified vm.",
|
||||
"lab_suspend" => "lab_suspend [vmid+|all] suspend the specified vm.",
|
||||
"lab_stop" => "lab_stop [vmid+|all] stop the specified vm.",
|
||||
"lab_revert" => "lab_revert [vmid+|all] [snapshot] revert the specified vm.",
|
||||
"lab_snapshot" => "lab_snapshot [vmid+|all] [snapshot] snapshot all targets for this exploit.",
|
||||
"lab_upload" => "lab_upload [vmid] [local_path] [remote_path] upload a file.",
|
||||
"lab_run_command" => "lab_run_command [vmid+|all] [command] run a command on all targets.",
|
||||
"lab_browse_to" => "lab_browse_to [vmid+|all] [uri] use the default browser to browse to a uri."
|
||||
}
|
||||
end
|
||||
|
||||
def name
|
||||
"Lab"
|
||||
end
|
||||
|
||||
##
|
||||
## Regular Lab Commands
|
||||
##
|
||||
|
||||
def cmd_lab_load(*args)
|
||||
return lab_usage unless args.count == 1
|
||||
|
||||
res = args[0]
|
||||
good_res = nil
|
||||
if (File.file? res and File.readable? res)
|
||||
# then the provided argument is an absolute path and is gtg.
|
||||
good_res = res
|
||||
elsif
|
||||
# let's check to see if it's in the data/lab dir (like when tab completed)
|
||||
[
|
||||
::Msf::Config.data_directory + File::SEPARATOR + "lab",
|
||||
# there isn't a user_data_directory, but could use:
|
||||
#::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab"
|
||||
].each do |dir|
|
||||
res_path = dir + File::SEPARATOR + res
|
||||
if (File.file?(res_path) and File.readable?(res_path))
|
||||
good_res = res_path
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if good_res
|
||||
@controller.from_file(good_res)
|
||||
else
|
||||
print_error("#{res} is not a valid lab definition file (.yml)")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Tab completion for the lab_load command
|
||||
#
|
||||
def cmd_lab_load_tabs(str, words)
|
||||
tabs = []
|
||||
#return tabs if words.length > 1
|
||||
if ( str and str =~ /^#{Regexp.escape(File::SEPARATOR)}/ )
|
||||
# then you are probably specifying a full path so let's just use normal file completion
|
||||
return tab_complete_filenames(str,words)
|
||||
elsif (not words[1] or not words[1].match(/^\//))
|
||||
# then let's start tab completion in the data/lab directory
|
||||
begin
|
||||
[
|
||||
::Msf::Config.data_directory + File::SEPARATOR + "lab",
|
||||
# there isn't a user_data_directory, but could use:
|
||||
#::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab"
|
||||
].each do |dir|
|
||||
next if not ::File.exist? dir
|
||||
tabs += ::Dir.new(dir).find_all { |e|
|
||||
path = dir + File::SEPARATOR + e
|
||||
::File.file?(path) and File.readable?(path)
|
||||
}
|
||||
end
|
||||
rescue Exception
|
||||
end
|
||||
else
|
||||
tabs += tab_complete_filenames(str,words)
|
||||
end
|
||||
return tabs
|
||||
end
|
||||
|
||||
def cmd_lab_load_dir(*args)
|
||||
return lab_usage unless args.count == 2
|
||||
@controller.build_from_dir(args[0],args[1],true)
|
||||
end
|
||||
|
||||
def cmd_lab_clear(*args)
|
||||
@controller.clear!
|
||||
end
|
||||
|
||||
def cmd_lab_save(*args)
|
||||
return lab_usage if args.empty?
|
||||
@controller.to_file(args[0])
|
||||
end
|
||||
|
||||
def cmd_lab_load_running(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] =~ /^remote_/
|
||||
return lab_usage unless args.count == 3
|
||||
## Expect a username & password
|
||||
@controller.build_from_running(args[0], args[1], args[2])
|
||||
else
|
||||
return lab_usage unless args.count == 1
|
||||
@controller.build_from_running(args[0])
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_load_config(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] =~ /^remote_/
|
||||
return lab_usage unless args.count == 3
|
||||
## Expect a username & password
|
||||
@controller.build_from_config(args[0], args[1], args[2])
|
||||
else
|
||||
return lab_usage unless args.count == 1
|
||||
@controller.build_from_config(args[0])
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_load_dir(*args)
|
||||
return lab_usage unless args.count == 2
|
||||
@controller.build_from_dir(args[0],args[1],true)
|
||||
end
|
||||
|
||||
def cmd_lab_clear(*args)
|
||||
@controller.clear!
|
||||
end
|
||||
|
||||
def cmd_lab_save(*args)
|
||||
return lab_usage if args.empty?
|
||||
@controller.to_file(args[0])
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
## Commands for dealing with a currently-loaded lab
|
||||
##
|
||||
def cmd_lab_show(*args)
|
||||
if args.empty?
|
||||
hlp_print_lab
|
||||
else
|
||||
args.each do |name|
|
||||
if @controller.includes_hostname? name
|
||||
print_line @controller[name].to_yaml
|
||||
else
|
||||
print_error "Unknown vm '#{name}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_show_running(*args)
|
||||
hlp_print_lab_running
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_search(*args)
|
||||
if args.empty?
|
||||
hlp_print_lab
|
||||
else
|
||||
args.each do |arg|
|
||||
print_line "Searching for vms with hostname matching #{arg}"
|
||||
@controller.each do |vm|
|
||||
print_line "checking to see #{vm.hostname} matches #{arg}"
|
||||
print_line "#{vm.hostname} matched #{arg}" if vm.hostname =~ Regexp.new(arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_search_tags(*args)
|
||||
if args.empty?
|
||||
hlp_print_lab
|
||||
else
|
||||
args.each do |arg|
|
||||
print_line "Searching for vms with tags matching #{arg}"
|
||||
@controller.each do |vm|
|
||||
print_line "checking to see #{vm.hostname} is tagged #{arg}"
|
||||
print_line "#{vm.hostname} tagged #{arg}" if vm.tagged?(arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_start(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] == "all"
|
||||
@controller.each do |vm|
|
||||
print_line "Starting lab vm #{vm.hostname}."
|
||||
if !vm.running?
|
||||
vm.start
|
||||
else
|
||||
print_line "Lab vm #{vm.hostname} already running."
|
||||
end
|
||||
end
|
||||
else
|
||||
args.each do |arg|
|
||||
if @controller.includes_hostname? arg
|
||||
vm = @controller.find_by_hostname(arg)
|
||||
if !vm.running?
|
||||
print_line "Starting lab vm #{vm.hostname}."
|
||||
vm.start
|
||||
else
|
||||
print_line "Lab vm #{vm.hostname} already running."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_stop(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] == "all"
|
||||
@controller.each do |vm|
|
||||
print_line "Stopping lab vm #{vm.hostname}."
|
||||
if vm.running?
|
||||
vm.stop
|
||||
else
|
||||
print_line "Lab vm #{vm.hostname} not running."
|
||||
end
|
||||
end
|
||||
else
|
||||
args.each do |arg|
|
||||
if @controller.includes_hostname? arg
|
||||
vm = @controller.find_by_hostname(arg)
|
||||
if vm.running?
|
||||
print_line "Stopping lab vm #{vm.hostname}."
|
||||
vm.stop
|
||||
else
|
||||
print_line "Lab vm #{vm.hostname} not running."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_suspend(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] == "all"
|
||||
@controller.each{ |vm| vm.suspend }
|
||||
else
|
||||
args.each do |arg|
|
||||
if @controller.includes_hostname? arg
|
||||
if @controller.find_by_hostname(arg).running?
|
||||
print_line "Suspending lab vm #{arg}."
|
||||
@controller.find_by_hostname(arg).suspend
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_reset(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] == "all"
|
||||
print_line "Resetting all lab vms."
|
||||
@controller.each{ |vm| vm.reset }
|
||||
else
|
||||
args.each do |arg|
|
||||
if @controller.includes_hostname? arg
|
||||
if @controller.find_by_hostname(arg).running?
|
||||
print_line "Resetting lab vm #{arg}."
|
||||
@controller.find_by_hostname(arg).reset
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_snapshot(*args)
|
||||
return lab_usage if args.count < 2
|
||||
snapshot = args[args.count-1]
|
||||
|
||||
if args[0] == "all"
|
||||
print_line "Snapshotting all lab vms to snapshot: #{snapshot}."
|
||||
@controller.each{ |vm| vm.create_snapshot(snapshot) }
|
||||
else
|
||||
args[0..-2].each do |name_arg|
|
||||
next unless @controller.includes_hostname? name_arg
|
||||
print_line "Snapshotting #{name_arg} to snapshot: #{snapshot}."
|
||||
@controller[name_arg].create_snapshot(snapshot)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_revert(*args)
|
||||
return lab_usage if args.count < 2
|
||||
snapshot = args[args.count-1]
|
||||
|
||||
if args[0] == "all"
|
||||
print_line "Reverting all lab vms to snapshot: #{snapshot}."
|
||||
@controller.each{ |vm| vm.revert_snapshot(snapshot) }
|
||||
else
|
||||
args[0..-2].each do |name_arg|
|
||||
next unless @controller.includes_hostname? name_arg
|
||||
print_line "Reverting #{name_arg} to snapshot: #{snapshot}."
|
||||
@controller[name_arg].revert_snapshot(snapshot)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_run_command(*args)
|
||||
return lab_usage if args.empty?
|
||||
command = args[args.count-1]
|
||||
if args[0] == "all"
|
||||
print_line "Running command #{command} on all vms."
|
||||
@controller.each do |vm|
|
||||
if vm.running?
|
||||
print_line "#{vm.hostname} running command: #{command}."
|
||||
vm.run_command(command)
|
||||
end
|
||||
end
|
||||
else
|
||||
args[0..-2].each do |name_arg|
|
||||
next unless @controller.includes_hostname? name_arg
|
||||
if @controller[name_arg].running?
|
||||
print_line "#{name_arg} running command: #{command}."
|
||||
@controller[name_arg].run_command(command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Command: lab_upload [vmids] [from] [to]
|
||||
#
|
||||
# Description: Uploads a file to the guest(s)
|
||||
#
|
||||
# Quirks: Pass "all" as a vmid to have it operate on all vms.
|
||||
#
|
||||
def cmd_lab_upload(*args)
|
||||
return lab_usage if args.empty?
|
||||
return lab_usage if args.count < 3
|
||||
|
||||
local_path = args[args.count-2]
|
||||
vm_path = args[args.count-1]
|
||||
|
||||
if args[0] == "all"
|
||||
@controller.each do |vm|
|
||||
if vm.running?
|
||||
print_line "Copying from #{local_path} to #{vm_path} on #{vm.hostname}"
|
||||
vm.copy_to_guest(local_path, vm_path)
|
||||
end
|
||||
end
|
||||
else
|
||||
args[0..-2].each do |vmid_arg|
|
||||
next unless @controller.includes_hostname? vmid_arg
|
||||
if @controller[vmid_arg].running?
|
||||
print_line "Copying from #{local_path} to #{vm_path} on #{vmid_arg}"
|
||||
@controller[vmid_arg].copy_to_guest(local_path, vm_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_browse_to(*args)
|
||||
return lab_usage if args.empty?
|
||||
uri = args[args.count-1]
|
||||
if args[0] == "all"
|
||||
print_line "Opening: #{uri} on all vms."
|
||||
@controller.each do |vm|
|
||||
if vm.running?
|
||||
print_line "#{vm.hostname} opening to uri: #{uri}."
|
||||
vm.open_uri(uri)
|
||||
end
|
||||
end
|
||||
else
|
||||
args[0..-2].each do |name_arg|
|
||||
next unless @controller.includes_hostname? name_arg
|
||||
if @controller[name_arg].running?
|
||||
print_line "#{name_arg} opening to uri: #{uri}."
|
||||
@controller[name_arg].open_uri(uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
## Commands for help
|
||||
##
|
||||
|
||||
def longest_cmd_size
|
||||
commands.keys.map {|x| x.size}.sort.last
|
||||
end
|
||||
|
||||
# No extended help yet, but this is where more detailed documentation
|
||||
# on particular commands would live. Key is command, (not cmd_command),
|
||||
# value is the documentation.
|
||||
def extended_help
|
||||
{
|
||||
"lab_fake_cmd" => "This is a fake command. It's got its own special docs." +
|
||||
(" " * longest_cmd_size) + "It might be long so so deal with formatting somehow."
|
||||
}
|
||||
end
|
||||
|
||||
# Map for usages
|
||||
def lab_usage
|
||||
caller[0][/`cmd_(.*)'/]
|
||||
cmd = $1
|
||||
if extended_help[cmd] || commands[cmd]
|
||||
cmd_lab_help cmd
|
||||
else # Should never really get here...
|
||||
print_error "Unknown command. Try 'help'"
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_help(*args)
|
||||
if args.empty?
|
||||
commands.each_pair {|k,v| print_line "%-#{longest_cmd_size}s - %s" % [k,v] }
|
||||
else
|
||||
args.each do |c|
|
||||
if extended_help[c] || commands[c]
|
||||
print_line "%-#{longest_cmd_size}s - %s" % [c,extended_help[c] || commands[c]]
|
||||
else
|
||||
print_error "Unknown command '#{c}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_line
|
||||
print_line "In order to use this plugin, you'll want to configure a .yml lab file"
|
||||
print_line "You can find an example in data/lab/test_targets.yml"
|
||||
print_line
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def hlp_print_lab
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Available Lab VMs',
|
||||
'Indent' => indent.length,
|
||||
'Columns' => [ 'Hostname', 'Driver', 'Type' ]
|
||||
)
|
||||
|
||||
@controller.each do |vm|
|
||||
tbl << [ vm.hostname,
|
||||
vm.driver.class,
|
||||
vm.type]
|
||||
end
|
||||
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def hlp_print_lab_running
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Running Lab VMs',
|
||||
'Indent' => indent.length,
|
||||
'Columns' => [ 'Hostname', 'Driver', 'Type', 'Power?' ]
|
||||
)
|
||||
|
||||
@controller.each do |vm|
|
||||
if vm.running?
|
||||
tbl << [ vm.hostname,
|
||||
vm.driver.class,
|
||||
vm.type,
|
||||
vm.running?]
|
||||
end
|
||||
end
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# The constructor is called when an instance of the plugin is created. The
|
||||
# framework instance that the plugin is being associated with is passed in
|
||||
# the framework parameter. Plugins should call the parent constructor when
|
||||
# inheriting from Msf::Plugin to ensure that the framework attribute on
|
||||
# their instance gets set.
|
||||
#
|
||||
attr_accessor :controller
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
## Register the commands above
|
||||
console_dispatcher = add_console_dispatcher(LabCommandDispatcher)
|
||||
|
||||
@controller = ::Lab::Controllers::VmController.new
|
||||
|
||||
## Share the vms
|
||||
console_dispatcher.controller = @controller
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# The cleanup routine for plugins gives them a chance to undo any actions
|
||||
# they may have done to the framework. For instance, if a console
|
||||
# dispatcher was added, then it should be removed in the cleanup routine.
|
||||
#
|
||||
def cleanup
|
||||
# If we had previously registered a console dispatcher with the console,
|
||||
# deregister it now.
|
||||
remove_console_dispatcher('Lab')
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a short, friendly name for the plugin.
|
||||
#
|
||||
def name
|
||||
"lab"
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a brief description of the plugin. It should be no
|
||||
# more than 60 characters, but there are no hard limits.
|
||||
#
|
||||
def desc
|
||||
"Adds the ability to manage VMs"
|
||||
end
|
||||
class LabCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
attr_accessor :controller
|
||||
|
||||
def initialize(driver)
|
||||
super(driver)
|
||||
@controller = nil
|
||||
|
||||
#
|
||||
# Require the lab gem, but fail nicely if it's not there.
|
||||
#
|
||||
begin
|
||||
require 'lab'
|
||||
rescue LoadError
|
||||
raise "WARNING: Lab gem not found, Please 'gem install lab'"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"lab_help" => "lab_help <lab command> - Show that command's description.",
|
||||
"lab_show" => "lab_show - show all vms in the lab.",
|
||||
"lab_search" => "lab_search - search local vms in the lab.",
|
||||
"lab_search_tags" => "lab_search_tag - search local vms in the lab.",
|
||||
#"lab_search_remote" => "lab_search_remote - search remote vms in the lab.",
|
||||
"lab_show_running" => "lab_show_running - show running vms.",
|
||||
"lab_load" => "lab_load [file] - load a lab definition from disk.",
|
||||
"lab_save" => "lab_save [filename] - persist a lab definition in a file.",
|
||||
"lab_load_running" => "lab_load_running [type] [user] [host] - use the running vms to create a lab.",
|
||||
"lab_load_config" => "lab_load_config [type] [user] [host] - use the vms in the config to create a lab.",
|
||||
"lab_load_dir" => "lab_load_dir [type] [directory] - create a lab from a specified directory.",
|
||||
"lab_clear" => "lab_clear - clear the running lab.",
|
||||
"lab_start" => "lab_start [vmid+|all] start the specified vm.",
|
||||
"lab_reset" => "lab_reset [vmid+|all] reset the specified vm.",
|
||||
"lab_suspend" => "lab_suspend [vmid+|all] suspend the specified vm.",
|
||||
"lab_stop" => "lab_stop [vmid+|all] stop the specified vm.",
|
||||
"lab_revert" => "lab_revert [vmid+|all] [snapshot] revert the specified vm.",
|
||||
"lab_snapshot" => "lab_snapshot [vmid+|all] [snapshot] snapshot all targets for this exploit.",
|
||||
"lab_upload" => "lab_upload [vmid] [local_path] [remote_path] upload a file.",
|
||||
"lab_run_command" => "lab_run_command [vmid+|all] [command] run a command on all targets.",
|
||||
"lab_browse_to" => "lab_browse_to [vmid+|all] [uri] use the default browser to browse to a uri."
|
||||
}
|
||||
end
|
||||
|
||||
def name
|
||||
"Lab"
|
||||
end
|
||||
|
||||
##
|
||||
## Regular Lab Commands
|
||||
##
|
||||
|
||||
def cmd_lab_load(*args)
|
||||
return lab_usage unless args.count == 1
|
||||
|
||||
res = args[0]
|
||||
good_res = nil
|
||||
if (File.file? res and File.readable? res)
|
||||
# then the provided argument is an absolute path and is gtg.
|
||||
good_res = res
|
||||
elsif
|
||||
# let's check to see if it's in the data/lab dir (like when tab completed)
|
||||
[
|
||||
::Msf::Config.data_directory + File::SEPARATOR + "lab",
|
||||
# there isn't a user_data_directory, but could use:
|
||||
#::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab"
|
||||
].each do |dir|
|
||||
res_path = dir + File::SEPARATOR + res
|
||||
if (File.file?(res_path) and File.readable?(res_path))
|
||||
good_res = res_path
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if good_res
|
||||
@controller.from_file(good_res)
|
||||
else
|
||||
print_error("#{res} is not a valid lab definition file (.yml)")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Tab completion for the lab_load command
|
||||
#
|
||||
def cmd_lab_load_tabs(str, words)
|
||||
tabs = []
|
||||
#return tabs if words.length > 1
|
||||
if ( str and str =~ /^#{Regexp.escape(File::SEPARATOR)}/ )
|
||||
# then you are probably specifying a full path so let's just use normal file completion
|
||||
return tab_complete_filenames(str,words)
|
||||
elsif (not words[1] or not words[1].match(/^\//))
|
||||
# then let's start tab completion in the data/lab directory
|
||||
begin
|
||||
[
|
||||
::Msf::Config.data_directory + File::SEPARATOR + "lab",
|
||||
# there isn't a user_data_directory, but could use:
|
||||
#::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab"
|
||||
].each do |dir|
|
||||
next if not ::File.exist? dir
|
||||
tabs += ::Dir.new(dir).find_all { |e|
|
||||
path = dir + File::SEPARATOR + e
|
||||
::File.file?(path) and File.readable?(path)
|
||||
}
|
||||
end
|
||||
rescue Exception
|
||||
end
|
||||
else
|
||||
tabs += tab_complete_filenames(str,words)
|
||||
end
|
||||
return tabs
|
||||
end
|
||||
|
||||
def cmd_lab_load_dir(*args)
|
||||
return lab_usage unless args.count == 2
|
||||
@controller.build_from_dir(args[0],args[1],true)
|
||||
end
|
||||
|
||||
def cmd_lab_clear(*args)
|
||||
@controller.clear!
|
||||
end
|
||||
|
||||
def cmd_lab_save(*args)
|
||||
return lab_usage if args.empty?
|
||||
@controller.to_file(args[0])
|
||||
end
|
||||
|
||||
def cmd_lab_load_running(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] =~ /^remote_/
|
||||
return lab_usage unless args.count == 3
|
||||
## Expect a username & password
|
||||
@controller.build_from_running(args[0], args[1], args[2])
|
||||
else
|
||||
return lab_usage unless args.count == 1
|
||||
@controller.build_from_running(args[0])
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_load_config(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] =~ /^remote_/
|
||||
return lab_usage unless args.count == 3
|
||||
## Expect a username & password
|
||||
@controller.build_from_config(args[0], args[1], args[2])
|
||||
else
|
||||
return lab_usage unless args.count == 1
|
||||
@controller.build_from_config(args[0])
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_load_dir(*args)
|
||||
return lab_usage unless args.count == 2
|
||||
@controller.build_from_dir(args[0],args[1],true)
|
||||
end
|
||||
|
||||
def cmd_lab_clear(*args)
|
||||
@controller.clear!
|
||||
end
|
||||
|
||||
def cmd_lab_save(*args)
|
||||
return lab_usage if args.empty?
|
||||
@controller.to_file(args[0])
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
## Commands for dealing with a currently-loaded lab
|
||||
##
|
||||
def cmd_lab_show(*args)
|
||||
if args.empty?
|
||||
hlp_print_lab
|
||||
else
|
||||
args.each do |name|
|
||||
if @controller.includes_hostname? name
|
||||
print_line @controller[name].to_yaml
|
||||
else
|
||||
print_error "Unknown vm '#{name}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_show_running(*args)
|
||||
hlp_print_lab_running
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_search(*args)
|
||||
if args.empty?
|
||||
hlp_print_lab
|
||||
else
|
||||
args.each do |arg|
|
||||
print_line "Searching for vms with hostname matching #{arg}"
|
||||
@controller.each do |vm|
|
||||
print_line "checking to see #{vm.hostname} matches #{arg}"
|
||||
print_line "#{vm.hostname} matched #{arg}" if vm.hostname =~ Regexp.new(arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_search_tags(*args)
|
||||
if args.empty?
|
||||
hlp_print_lab
|
||||
else
|
||||
args.each do |arg|
|
||||
print_line "Searching for vms with tags matching #{arg}"
|
||||
@controller.each do |vm|
|
||||
print_line "checking to see #{vm.hostname} is tagged #{arg}"
|
||||
print_line "#{vm.hostname} tagged #{arg}" if vm.tagged?(arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_start(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] == "all"
|
||||
@controller.each do |vm|
|
||||
print_line "Starting lab vm #{vm.hostname}."
|
||||
if !vm.running?
|
||||
vm.start
|
||||
else
|
||||
print_line "Lab vm #{vm.hostname} already running."
|
||||
end
|
||||
end
|
||||
else
|
||||
args.each do |arg|
|
||||
if @controller.includes_hostname? arg
|
||||
vm = @controller.find_by_hostname(arg)
|
||||
if !vm.running?
|
||||
print_line "Starting lab vm #{vm.hostname}."
|
||||
vm.start
|
||||
else
|
||||
print_line "Lab vm #{vm.hostname} already running."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_stop(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] == "all"
|
||||
@controller.each do |vm|
|
||||
print_line "Stopping lab vm #{vm.hostname}."
|
||||
if vm.running?
|
||||
vm.stop
|
||||
else
|
||||
print_line "Lab vm #{vm.hostname} not running."
|
||||
end
|
||||
end
|
||||
else
|
||||
args.each do |arg|
|
||||
if @controller.includes_hostname? arg
|
||||
vm = @controller.find_by_hostname(arg)
|
||||
if vm.running?
|
||||
print_line "Stopping lab vm #{vm.hostname}."
|
||||
vm.stop
|
||||
else
|
||||
print_line "Lab vm #{vm.hostname} not running."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_suspend(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] == "all"
|
||||
@controller.each{ |vm| vm.suspend }
|
||||
else
|
||||
args.each do |arg|
|
||||
if @controller.includes_hostname? arg
|
||||
if @controller.find_by_hostname(arg).running?
|
||||
print_line "Suspending lab vm #{arg}."
|
||||
@controller.find_by_hostname(arg).suspend
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_reset(*args)
|
||||
return lab_usage if args.empty?
|
||||
|
||||
if args[0] == "all"
|
||||
print_line "Resetting all lab vms."
|
||||
@controller.each{ |vm| vm.reset }
|
||||
else
|
||||
args.each do |arg|
|
||||
if @controller.includes_hostname? arg
|
||||
if @controller.find_by_hostname(arg).running?
|
||||
print_line "Resetting lab vm #{arg}."
|
||||
@controller.find_by_hostname(arg).reset
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_snapshot(*args)
|
||||
return lab_usage if args.count < 2
|
||||
snapshot = args[args.count-1]
|
||||
|
||||
if args[0] == "all"
|
||||
print_line "Snapshotting all lab vms to snapshot: #{snapshot}."
|
||||
@controller.each{ |vm| vm.create_snapshot(snapshot) }
|
||||
else
|
||||
args[0..-2].each do |name_arg|
|
||||
next unless @controller.includes_hostname? name_arg
|
||||
print_line "Snapshotting #{name_arg} to snapshot: #{snapshot}."
|
||||
@controller[name_arg].create_snapshot(snapshot)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_revert(*args)
|
||||
return lab_usage if args.count < 2
|
||||
snapshot = args[args.count-1]
|
||||
|
||||
if args[0] == "all"
|
||||
print_line "Reverting all lab vms to snapshot: #{snapshot}."
|
||||
@controller.each{ |vm| vm.revert_snapshot(snapshot) }
|
||||
else
|
||||
args[0..-2].each do |name_arg|
|
||||
next unless @controller.includes_hostname? name_arg
|
||||
print_line "Reverting #{name_arg} to snapshot: #{snapshot}."
|
||||
@controller[name_arg].revert_snapshot(snapshot)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_lab_run_command(*args)
|
||||
return lab_usage if args.empty?
|
||||
command = args[args.count-1]
|
||||
if args[0] == "all"
|
||||
print_line "Running command #{command} on all vms."
|
||||
@controller.each do |vm|
|
||||
if vm.running?
|
||||
print_line "#{vm.hostname} running command: #{command}."
|
||||
vm.run_command(command)
|
||||
end
|
||||
end
|
||||
else
|
||||
args[0..-2].each do |name_arg|
|
||||
next unless @controller.includes_hostname? name_arg
|
||||
if @controller[name_arg].running?
|
||||
print_line "#{name_arg} running command: #{command}."
|
||||
@controller[name_arg].run_command(command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Command: lab_upload [vmids] [from] [to]
|
||||
#
|
||||
# Description: Uploads a file to the guest(s)
|
||||
#
|
||||
# Quirks: Pass "all" as a vmid to have it operate on all vms.
|
||||
#
|
||||
def cmd_lab_upload(*args)
|
||||
return lab_usage if args.empty?
|
||||
return lab_usage if args.count < 3
|
||||
|
||||
local_path = args[args.count-2]
|
||||
vm_path = args[args.count-1]
|
||||
|
||||
if args[0] == "all"
|
||||
@controller.each do |vm|
|
||||
if vm.running?
|
||||
print_line "Copying from #{local_path} to #{vm_path} on #{vm.hostname}"
|
||||
vm.copy_to_guest(local_path, vm_path)
|
||||
end
|
||||
end
|
||||
else
|
||||
args[0..-2].each do |vmid_arg|
|
||||
next unless @controller.includes_hostname? vmid_arg
|
||||
if @controller[vmid_arg].running?
|
||||
print_line "Copying from #{local_path} to #{vm_path} on #{vmid_arg}"
|
||||
@controller[vmid_arg].copy_to_guest(local_path, vm_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_browse_to(*args)
|
||||
return lab_usage if args.empty?
|
||||
uri = args[args.count-1]
|
||||
if args[0] == "all"
|
||||
print_line "Opening: #{uri} on all vms."
|
||||
@controller.each do |vm|
|
||||
if vm.running?
|
||||
print_line "#{vm.hostname} opening to uri: #{uri}."
|
||||
vm.open_uri(uri)
|
||||
end
|
||||
end
|
||||
else
|
||||
args[0..-2].each do |name_arg|
|
||||
next unless @controller.includes_hostname? name_arg
|
||||
if @controller[name_arg].running?
|
||||
print_line "#{name_arg} opening to uri: #{uri}."
|
||||
@controller[name_arg].open_uri(uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
## Commands for help
|
||||
##
|
||||
|
||||
def longest_cmd_size
|
||||
commands.keys.map {|x| x.size}.sort.last
|
||||
end
|
||||
|
||||
# No extended help yet, but this is where more detailed documentation
|
||||
# on particular commands would live. Key is command, (not cmd_command),
|
||||
# value is the documentation.
|
||||
def extended_help
|
||||
{
|
||||
"lab_fake_cmd" => "This is a fake command. It's got its own special docs." +
|
||||
(" " * longest_cmd_size) + "It might be long so so deal with formatting somehow."
|
||||
}
|
||||
end
|
||||
|
||||
# Map for usages
|
||||
def lab_usage
|
||||
caller[0][/`cmd_(.*)'/]
|
||||
cmd = $1
|
||||
if extended_help[cmd] || commands[cmd]
|
||||
cmd_lab_help cmd
|
||||
else # Should never really get here...
|
||||
print_error "Unknown command. Try 'help'"
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_lab_help(*args)
|
||||
if args.empty?
|
||||
commands.each_pair {|k,v| print_line "%-#{longest_cmd_size}s - %s" % [k,v] }
|
||||
else
|
||||
args.each do |c|
|
||||
if extended_help[c] || commands[c]
|
||||
print_line "%-#{longest_cmd_size}s - %s" % [c,extended_help[c] || commands[c]]
|
||||
else
|
||||
print_error "Unknown command '#{c}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_line
|
||||
print_line "In order to use this plugin, you'll want to configure a .yml lab file"
|
||||
print_line "You can find an example in data/lab/test_targets.yml"
|
||||
print_line
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def hlp_print_lab
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Available Lab VMs',
|
||||
'Indent' => indent.length,
|
||||
'Columns' => [ 'Hostname', 'Driver', 'Type' ]
|
||||
)
|
||||
|
||||
@controller.each do |vm|
|
||||
tbl << [ vm.hostname,
|
||||
vm.driver.class,
|
||||
vm.type]
|
||||
end
|
||||
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def hlp_print_lab_running
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Running Lab VMs',
|
||||
'Indent' => indent.length,
|
||||
'Columns' => [ 'Hostname', 'Driver', 'Type', 'Power?' ]
|
||||
)
|
||||
|
||||
@controller.each do |vm|
|
||||
if vm.running?
|
||||
tbl << [ vm.hostname,
|
||||
vm.driver.class,
|
||||
vm.type,
|
||||
vm.running?]
|
||||
end
|
||||
end
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# The constructor is called when an instance of the plugin is created. The
|
||||
# framework instance that the plugin is being associated with is passed in
|
||||
# the framework parameter. Plugins should call the parent constructor when
|
||||
# inheriting from Msf::Plugin to ensure that the framework attribute on
|
||||
# their instance gets set.
|
||||
#
|
||||
attr_accessor :controller
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
## Register the commands above
|
||||
console_dispatcher = add_console_dispatcher(LabCommandDispatcher)
|
||||
|
||||
@controller = ::Lab::Controllers::VmController.new
|
||||
|
||||
## Share the vms
|
||||
console_dispatcher.controller = @controller
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# The cleanup routine for plugins gives them a chance to undo any actions
|
||||
# they may have done to the framework. For instance, if a console
|
||||
# dispatcher was added, then it should be removed in the cleanup routine.
|
||||
#
|
||||
def cleanup
|
||||
# If we had previously registered a console dispatcher with the console,
|
||||
# deregister it now.
|
||||
remove_console_dispatcher('Lab')
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a short, friendly name for the plugin.
|
||||
#
|
||||
def name
|
||||
"lab"
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a brief description of the plugin. It should be no
|
||||
# more than 60 characters, but there are no hard limits.
|
||||
#
|
||||
def desc
|
||||
"Adds the ability to manage VMs"
|
||||
end
|
||||
|
||||
end ## End Class
|
||||
end ## End Module
|
||||
|
||||
+123
-123
@@ -20,144 +20,144 @@ module Msf
|
||||
###
|
||||
class Plugin::Msfd < Msf::Plugin
|
||||
|
||||
#
|
||||
# The default local hostname that the server listens on.
|
||||
#
|
||||
DefaultHost = "127.0.0.1"
|
||||
#
|
||||
# The default local hostname that the server listens on.
|
||||
#
|
||||
DefaultHost = "127.0.0.1"
|
||||
|
||||
#
|
||||
# The default local port that the server listens on.
|
||||
#
|
||||
DefaultPort = 55554
|
||||
#
|
||||
# The default local port that the server listens on.
|
||||
#
|
||||
DefaultPort = 55554
|
||||
|
||||
#
|
||||
# Initializes the msfd plugin. The following options are supported in the
|
||||
# hash by this plugin:
|
||||
#
|
||||
# ServerHost
|
||||
#
|
||||
# The local hostname to listen on for connections. The default is
|
||||
# 127.0.0.1.
|
||||
#
|
||||
# ServerPort
|
||||
#
|
||||
# The local port to listen on for connections. The default is 55554.
|
||||
#
|
||||
# SSL
|
||||
#
|
||||
# Use SSL
|
||||
#
|
||||
# RunInForeground
|
||||
#
|
||||
# Instructs the plugin to now execute the daemon in a worker thread and to
|
||||
# instead allow the caller to manage executing the daemon through the
|
||||
# ``run'' method.
|
||||
#
|
||||
# HostsAllowed
|
||||
#
|
||||
# List of hosts (in NBO) allowed to use msfd
|
||||
#
|
||||
# HostsDenied
|
||||
#
|
||||
# List of hosts (in NBO) not allowed to use msfd
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
#
|
||||
# Initializes the msfd plugin. The following options are supported in the
|
||||
# hash by this plugin:
|
||||
#
|
||||
# ServerHost
|
||||
#
|
||||
# The local hostname to listen on for connections. The default is
|
||||
# 127.0.0.1.
|
||||
#
|
||||
# ServerPort
|
||||
#
|
||||
# The local port to listen on for connections. The default is 55554.
|
||||
#
|
||||
# SSL
|
||||
#
|
||||
# Use SSL
|
||||
#
|
||||
# RunInForeground
|
||||
#
|
||||
# Instructs the plugin to now execute the daemon in a worker thread and to
|
||||
# instead allow the caller to manage executing the daemon through the
|
||||
# ``run'' method.
|
||||
#
|
||||
# HostsAllowed
|
||||
#
|
||||
# List of hosts (in NBO) allowed to use msfd
|
||||
#
|
||||
# HostsDenied
|
||||
#
|
||||
# List of hosts (in NBO) not allowed to use msfd
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
# Start listening for connections.
|
||||
self.server = Rex::Socket::TcpServer.create(
|
||||
'LocalHost' => opts['ServerHost'] || DefaultHost,
|
||||
'LocalPort' => opts['ServerPort'] || DefaultPort,
|
||||
'SSL' => opts['SSL'])
|
||||
# Start listening for connections.
|
||||
self.server = Rex::Socket::TcpServer.create(
|
||||
'LocalHost' => opts['ServerHost'] || DefaultHost,
|
||||
'LocalPort' => opts['ServerPort'] || DefaultPort,
|
||||
'SSL' => opts['SSL'])
|
||||
|
||||
# If the run in foreground flag is not specified, then go ahead and fire
|
||||
# it off in a worker thread.
|
||||
if (opts['RunInForeground'] != true)
|
||||
Thread.new {
|
||||
run(opts)
|
||||
}
|
||||
end
|
||||
end
|
||||
# If the run in foreground flag is not specified, then go ahead and fire
|
||||
# it off in a worker thread.
|
||||
if (opts['RunInForeground'] != true)
|
||||
Thread.new {
|
||||
run(opts)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns 'msfd'
|
||||
#
|
||||
def name
|
||||
"msfd"
|
||||
end
|
||||
#
|
||||
# Returns 'msfd'
|
||||
#
|
||||
def name
|
||||
"msfd"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the msfd plugin description.
|
||||
#
|
||||
def desc
|
||||
"Provides a console interface to users over a listening TCP port."
|
||||
end
|
||||
#
|
||||
# Returns the msfd plugin description.
|
||||
#
|
||||
def desc
|
||||
"Provides a console interface to users over a listening TCP port."
|
||||
end
|
||||
|
||||
#
|
||||
# Runs the msfd plugin by blocking on new connections and then spawning
|
||||
# threads to handle the console interface for each client.
|
||||
#
|
||||
def run(opts={})
|
||||
while true
|
||||
client = server.accept
|
||||
#
|
||||
# Runs the msfd plugin by blocking on new connections and then spawning
|
||||
# threads to handle the console interface for each client.
|
||||
#
|
||||
def run(opts={})
|
||||
while true
|
||||
client = server.accept
|
||||
|
||||
addr = Rex::Socket.resolv_nbo(client.peerhost)
|
||||
addr = Rex::Socket.resolv_nbo(client.peerhost)
|
||||
|
||||
if opts['HostsAllowed'] and
|
||||
not opts['HostsAllowed'].find { |x| x == addr }
|
||||
client.close
|
||||
next
|
||||
end
|
||||
if opts['HostsAllowed'] and
|
||||
not opts['HostsAllowed'].find { |x| x == addr }
|
||||
client.close
|
||||
next
|
||||
end
|
||||
|
||||
if opts['HostsDenied'] and
|
||||
opts['HostsDenied'].find { |x| x == addr }
|
||||
client.close
|
||||
next
|
||||
end
|
||||
msg = "Msfd: New connection from #{client.peerhost}"
|
||||
ilog(msg, 'core')
|
||||
print_status(msg)
|
||||
if opts['HostsDenied'] and
|
||||
opts['HostsDenied'].find { |x| x == addr }
|
||||
client.close
|
||||
next
|
||||
end
|
||||
msg = "Msfd: New connection from #{client.peerhost}"
|
||||
ilog(msg, 'core')
|
||||
print_status(msg)
|
||||
|
||||
# Spawn a thread for the client connection
|
||||
Thread.new(client) { |cli|
|
||||
begin
|
||||
Msf::Ui::Console::Driver.new(
|
||||
Msf::Ui::Console::Driver::DefaultPrompt,
|
||||
Msf::Ui::Console::Driver::DefaultPromptChar,
|
||||
'Framework' => framework,
|
||||
'LocalInput' => Rex::Ui::Text::Input::Socket.new(cli),
|
||||
'LocalOutput' => Rex::Ui::Text::Output::Socket.new(cli),
|
||||
'AllowCommandPassthru' => false).run
|
||||
rescue
|
||||
elog("Msfd: Client error: #{$!}\n\n#{$@.join("\n")}", 'core')
|
||||
ensure
|
||||
msg = "Msfd: Closing client connection with #{cli.peerhost}"
|
||||
ilog(msg, 'core')
|
||||
print_status(msg)
|
||||
begin
|
||||
cli.shutdown
|
||||
cli.close
|
||||
rescue IOError
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
# Spawn a thread for the client connection
|
||||
Thread.new(client) { |cli|
|
||||
begin
|
||||
Msf::Ui::Console::Driver.new(
|
||||
Msf::Ui::Console::Driver::DefaultPrompt,
|
||||
Msf::Ui::Console::Driver::DefaultPromptChar,
|
||||
'Framework' => framework,
|
||||
'LocalInput' => Rex::Ui::Text::Input::Socket.new(cli),
|
||||
'LocalOutput' => Rex::Ui::Text::Output::Socket.new(cli),
|
||||
'AllowCommandPassthru' => false).run
|
||||
rescue
|
||||
elog("Msfd: Client error: #{$!}\n\n#{$@.join("\n")}", 'core')
|
||||
ensure
|
||||
msg = "Msfd: Closing client connection with #{cli.peerhost}"
|
||||
ilog(msg, 'core')
|
||||
print_status(msg)
|
||||
begin
|
||||
cli.shutdown
|
||||
cli.close
|
||||
rescue IOError
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Closes the listener service.
|
||||
#
|
||||
def cleanup
|
||||
ilog("Msfd: Shutting down server", 'core')
|
||||
self.server.close
|
||||
end
|
||||
#
|
||||
# Closes the listener service.
|
||||
#
|
||||
def cleanup
|
||||
ilog("Msfd: Shutting down server", 'core')
|
||||
self.server.close
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# The listening socket instance.
|
||||
#
|
||||
attr_accessor :server
|
||||
#
|
||||
# The listening socket instance.
|
||||
#
|
||||
attr_accessor :server
|
||||
|
||||
end
|
||||
|
||||
|
||||
+82
-82
@@ -23,103 +23,103 @@ module Msf
|
||||
###
|
||||
class Plugin::MSGRPC < Msf::Plugin
|
||||
|
||||
#
|
||||
# The default local hostname that the server listens on.
|
||||
#
|
||||
DefaultHost = "127.0.0.1"
|
||||
#
|
||||
# The default local hostname that the server listens on.
|
||||
#
|
||||
DefaultHost = "127.0.0.1"
|
||||
|
||||
#
|
||||
# The default local port that the server listens on.
|
||||
#
|
||||
DefaultPort = 55552
|
||||
#
|
||||
# The default local port that the server listens on.
|
||||
#
|
||||
DefaultPort = 55552
|
||||
|
||||
#
|
||||
# ServerPort
|
||||
#
|
||||
# The local port to listen on for connections. The default is 55553
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
#
|
||||
# ServerPort
|
||||
#
|
||||
# The local port to listen on for connections. The default is 55553
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
host = opts['ServerHost'] || DefaultHost
|
||||
port = opts['ServerPort'] || DefaultPort
|
||||
ssl = (opts['SSL'] and opts['SSL'].to_s =~ /^[ty]/i) ? true : false
|
||||
cert = opts['SSLCert']
|
||||
host = opts['ServerHost'] || DefaultHost
|
||||
port = opts['ServerPort'] || DefaultPort
|
||||
ssl = (opts['SSL'] and opts['SSL'].to_s =~ /^[ty]/i) ? true : false
|
||||
cert = opts['SSLCert']
|
||||
|
||||
user = opts['User'] || "msf"
|
||||
pass = opts['Pass'] || ::Rex::Text.rand_text_alphanumeric(8)
|
||||
uri = opts['URI'] || "/api"
|
||||
user = opts['User'] || "msf"
|
||||
pass = opts['Pass'] || ::Rex::Text.rand_text_alphanumeric(8)
|
||||
uri = opts['URI'] || "/api"
|
||||
|
||||
print_status("MSGRPC Service: #{host}:#{port} #{ssl ? " (SSL)" : ""}")
|
||||
print_status("MSGRPC Username: #{user}")
|
||||
print_status("MSGRPC Password: #{pass}")
|
||||
print_status("MSGRPC Service: #{host}:#{port} #{ssl ? " (SSL)" : ""}")
|
||||
print_status("MSGRPC Username: #{user}")
|
||||
print_status("MSGRPC Password: #{pass}")
|
||||
|
||||
self.server = ::Msf::RPC::Service.new(framework, {
|
||||
:host => host,
|
||||
:port => port,
|
||||
:ssl => ssl,
|
||||
:cert => cert,
|
||||
:uri => uri,
|
||||
:tokens => { }
|
||||
})
|
||||
self.server = ::Msf::RPC::Service.new(framework, {
|
||||
:host => host,
|
||||
:port => port,
|
||||
:ssl => ssl,
|
||||
:cert => cert,
|
||||
:uri => uri,
|
||||
:tokens => { }
|
||||
})
|
||||
|
||||
self.server.add_user(user, pass)
|
||||
self.server.add_user(user, pass)
|
||||
|
||||
# If the run in foreground flag is not specified, then go ahead and fire
|
||||
# it off in a worker thread.
|
||||
if (opts['RunInForeground'] != true)
|
||||
# Store a handle to the thread so we can kill it during
|
||||
# cleanup when we get unloaded.
|
||||
self.thread = Thread.new { run }
|
||||
framework.threads.register(self.thread, "MetasploitRPCServer", true)
|
||||
end
|
||||
end
|
||||
# If the run in foreground flag is not specified, then go ahead and fire
|
||||
# it off in a worker thread.
|
||||
if (opts['RunInForeground'] != true)
|
||||
# Store a handle to the thread so we can kill it during
|
||||
# cleanup when we get unloaded.
|
||||
self.thread = Thread.new { run }
|
||||
framework.threads.register(self.thread, "MetasploitRPCServer", true)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns 'msgrpc'
|
||||
#
|
||||
def name
|
||||
"msgrpc"
|
||||
end
|
||||
#
|
||||
# Returns 'msgrpc'
|
||||
#
|
||||
def name
|
||||
"msgrpc"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the plugin description.
|
||||
#
|
||||
def desc
|
||||
"Provides a MessagePack interface over HTTP"
|
||||
end
|
||||
#
|
||||
# Returns the plugin description.
|
||||
#
|
||||
def desc
|
||||
"Provides a MessagePack interface over HTTP"
|
||||
end
|
||||
|
||||
#
|
||||
# The meat of the plugin, sets up handlers for requests
|
||||
#
|
||||
def run
|
||||
# Start the actual service
|
||||
self.server.start
|
||||
#
|
||||
# The meat of the plugin, sets up handlers for requests
|
||||
#
|
||||
def run
|
||||
# Start the actual service
|
||||
self.server.start
|
||||
|
||||
# Register
|
||||
framework.threads.register(Thread.current, "MetasploitRPCServer", true)
|
||||
# Register
|
||||
framework.threads.register(Thread.current, "MetasploitRPCServer", true)
|
||||
|
||||
# Wait for the service to complete
|
||||
self.server.wait
|
||||
end
|
||||
# Wait for the service to complete
|
||||
self.server.wait
|
||||
end
|
||||
|
||||
#
|
||||
# Closes the listener service.
|
||||
#
|
||||
def cleanup
|
||||
self.server.stop if self.server
|
||||
self.thread.kill if self.thread
|
||||
self.server = nil
|
||||
super
|
||||
end
|
||||
#
|
||||
# Closes the listener service.
|
||||
#
|
||||
def cleanup
|
||||
self.server.stop if self.server
|
||||
self.thread.kill if self.thread
|
||||
self.server = nil
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
# The MSGRPC instance.
|
||||
#
|
||||
attr_accessor :server
|
||||
attr_accessor :thread
|
||||
attr_accessor :users
|
||||
attr_accessor :tokens
|
||||
#
|
||||
# The MSGRPC instance.
|
||||
#
|
||||
attr_accessor :server
|
||||
attr_accessor :thread
|
||||
attr_accessor :users
|
||||
attr_accessor :tokens
|
||||
|
||||
end
|
||||
|
||||
|
||||
+1675
-1675
@@ -5,1679 +5,1679 @@ require 'nessus/nessus-xmlrpc'
|
||||
require 'rex/parser/nessus_xml'
|
||||
|
||||
module Msf
|
||||
class Plugin::Nessus < Msf::Plugin
|
||||
|
||||
#creates the index of exploit details to make searching for exploits much faster.
|
||||
def create_xindex
|
||||
start = Time.now
|
||||
print_status("Creating Exploit Search Index - (#{@xindex}) - this wont take long.")
|
||||
count = 0
|
||||
# use Msf::Config.get_config_root as the location.
|
||||
File.open("#{@xindex}", "w+") do |f|
|
||||
#need to add version line.
|
||||
f.puts(Msf::Framework::RepoRevision)
|
||||
framework.exploits.sort.each { |refname, mod|
|
||||
stuff = ""
|
||||
o = nil
|
||||
begin
|
||||
o = mod.new
|
||||
rescue ::Exception
|
||||
end
|
||||
stuff << "#{refname}|#{o.name}|#{o.platform_to_s}|#{o.arch_to_s}"
|
||||
next if not o
|
||||
o.references.map do |x|
|
||||
if !(x.ctx_id == "URL")
|
||||
if (x.ctx_id == "MSB")
|
||||
stuff << "|#{x.ctx_val}"
|
||||
else
|
||||
stuff << "|#{x.ctx_id}-#{x.ctx_val}"
|
||||
end
|
||||
end
|
||||
end
|
||||
stuff << "\n"
|
||||
f.puts(stuff)
|
||||
}
|
||||
end
|
||||
total = Time.now - start
|
||||
print_status("It has taken : #{total} seconds to build the exploits search index")
|
||||
end
|
||||
|
||||
def nessus_index
|
||||
if File.exist?("#{@xindex}")
|
||||
#check if it's version line matches current version.
|
||||
File.open("#{@xindex}") {|f|
|
||||
line = f.readline
|
||||
line.chomp!
|
||||
if line.to_i == Msf::Framework::RepoRevision
|
||||
print_good("Exploit Index - (#{@xindex}) - is valid.")
|
||||
else
|
||||
create_xindex
|
||||
end
|
||||
}
|
||||
else
|
||||
create_xindex
|
||||
end
|
||||
end
|
||||
|
||||
class ConsoleCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"Nessus"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
"nessus_connect" => "Connect to a nessus server: nconnect username:password@hostname:port <ssl ok>.",
|
||||
"nessus_admin" => "Checks if user is an admin.",
|
||||
"nessus_help" => "Get help on all commands.",
|
||||
"nessus_logout" => "Terminate the session.",
|
||||
"nessus_server_status" => "Check the status of your Nessus Server.",
|
||||
"nessus_server_feed" => "Nessus Feed Type.",
|
||||
"nessus_server_prefs" => "Display Server Prefs.",
|
||||
"nessus_report_list" => "List all Nessus reports.",
|
||||
"nessus_report_get" => "Import a report from the nessus server in Nessus v2 format.",
|
||||
"nessus_report_del" => "Delete a report.",
|
||||
"nessus_report_vulns" => "Get list of vulns from a report.",
|
||||
"nessus_report_hosts" => "Get list of hosts from a report.",
|
||||
"nessus_report_host_ports" => "Get list of open ports from a host from a report.",
|
||||
"nessus_report_host_detail" => "Detail from a report item on a host.",
|
||||
"nessus_scan_status" => "List all currently running Nessus scans.",
|
||||
"nessus_scan_new" => "Create new Nessus Scan.",
|
||||
"nessus_scan_pause" => "Pause a Nessus Scan.",
|
||||
"nessus_scan_pause_all" => "Pause all Nessus Scans.",
|
||||
"nessus_scan_stop" => "Stop a Nessus Scan.",
|
||||
"nessus_scan_stop_all" => "Stop all Nessus Scans.",
|
||||
"nessus_scan_resume" => "Resume a Nessus Scan.",
|
||||
"nessus_scan_resume_all" => "Resume all Nessus Scans.",
|
||||
"nessus_user_list" => "Show Nessus Users.",
|
||||
"nessus_user_add" => "Add a new Nessus User.",
|
||||
"nessus_user_del" => "Delete a Nessus User.",
|
||||
"nessus_user_passwd" => "Change Nessus Users Password.",
|
||||
"nessus_plugin_family" => "List plugins in a family.",
|
||||
"nessus_plugin_details" => "List details of a particular plugin.",
|
||||
"nessus_plugin_list" => "Displays each plugin family and the number of plugins.",
|
||||
"nessus_plugin_prefs" => "Display Plugin Prefs.",
|
||||
"nessus_policy_list" => "List all polciies.",
|
||||
"nessus_policy_del" => "Delete a policy.",
|
||||
"nessus_index" => "Manually generates a search index for exploits.",
|
||||
"nessus_template_list" => "List all the templates on the server.",
|
||||
"nessus_db_scan" => "Create a scan of all ips in db_hosts.",
|
||||
"nessus_save" => "Save username/passowrd/server/port details."
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_nessus_index
|
||||
Msf::Plugin::Nessus.nessus_index
|
||||
end
|
||||
|
||||
def cmd_nessus_save(*args)
|
||||
#if we are logged in, save session details to nessus.yaml
|
||||
@nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml"
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_save")
|
||||
return
|
||||
end
|
||||
|
||||
if args[0]
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_save")
|
||||
return
|
||||
end
|
||||
|
||||
group = "default"
|
||||
|
||||
if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
config = Hash.new
|
||||
config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}}
|
||||
File.open("#{@nessus_yaml}", "w+") do |f|
|
||||
f.puts YAML.dump(config)
|
||||
end
|
||||
print_good("#{@nessus_yaml} created.")
|
||||
|
||||
else
|
||||
print_error("Missing username/password/server/port - relogin and then try again.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_db_scan(*args)
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_db_scan <policy id> <scan name>")
|
||||
print_status(" Example:> nessus_db_scan 1 \"My Scan\"")
|
||||
print_status()
|
||||
print_status("Creates a scan based on all the hosts listed in db_hosts.")
|
||||
print_status("use nessus_policy_list to list all available policies")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
pid = args[0].to_i
|
||||
name = args[1]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_db_scan <policy id> <scan name>")
|
||||
print_status(" use nessus_policy_list to list all available policies")
|
||||
return
|
||||
end
|
||||
|
||||
if check_policy(pid)
|
||||
print_error("That policy does not exist.")
|
||||
return
|
||||
end
|
||||
|
||||
tgts = ""
|
||||
framework.db.hosts(framework.db.workspace).each do |host|
|
||||
tgts << host.address
|
||||
tgts << ","
|
||||
end
|
||||
|
||||
tgts.chop!
|
||||
|
||||
print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning all hosts in workspace")
|
||||
|
||||
scan = @n.scan_new(pid, name, tgts)
|
||||
|
||||
if scan
|
||||
print_status("Scan started. uid is #{scan}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def cmd_nessus_logout
|
||||
@token = nil
|
||||
print_status("Logged out")
|
||||
system("rm #{@nessus_yaml}")
|
||||
print_good("#{@nessus_yaml} removed.")
|
||||
return
|
||||
end
|
||||
|
||||
def cmd_nessus_help(*args)
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
"Command",
|
||||
"Help Text"
|
||||
],
|
||||
'SortIndex' => -1
|
||||
)
|
||||
tbl << [ "Generic Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_connect", "Connect to a nessus server" ]
|
||||
tbl << [ "nessus_save", "Save nessus login info between sessions" ]
|
||||
tbl << [ "nessus_logout", "Logout from the nessus server" ]
|
||||
tbl << [ "nessus_help", "Listing of available nessus commands" ]
|
||||
tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ]
|
||||
tbl << [ "nessus_admin", "Checks if user is an admin" ]
|
||||
tbl << [ "nessus_server_feed", "Nessus Feed Type" ]
|
||||
tbl << [ "nessus_find_targets", "Try to find vulnerable targets from a report" ]
|
||||
tbl << [ "nessus_server_prefs", "Display Server Prefs" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "Reports Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_report_list", "List all Nessus reports" ]
|
||||
tbl << [ "nessus_report_get", "Import a report from the nessus server in Nessus v2 format" ]
|
||||
tbl << [ "nessus_report_vulns", "Get list of vulns from a report" ]
|
||||
tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ]
|
||||
tbl << [ "nessus_report_host_ports", "Get list of open ports from a host from a report" ]
|
||||
tbl << [ "nessus_report_host_detail", "Detail from a report item on a host" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "Scan Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_scan_new", "Create new Nessus Scan" ]
|
||||
tbl << [ "nessus_scan_status", "List all currently running Nessus scans" ]
|
||||
tbl << [ "nessus_scan_pause", "Pause a Nessus Scan" ]
|
||||
tbl << [ "nessus_scan_pause_all", "Pause all Nessus Scans" ]
|
||||
tbl << [ "nessus_scan_stop", "Stop a Nessus Scan" ]
|
||||
tbl << [ "nessus_scan_stop_all", "Stop all Nessus Scans" ]
|
||||
tbl << [ "nessus_scan_resume", "Resume a Nessus Scan" ]
|
||||
tbl << [ "nessus_scan_resume_all", "Resume all Nessus Scans" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "Plugin Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_plugin_list", "Displays each plugin family and the number of plugins" ]
|
||||
tbl << [ "nessus_plugin_family", "List plugins in a family" ]
|
||||
tbl << [ "nessus_plugin_details", "List details of a particular plugin" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "User Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_user_list", "Show Nessus Users" ]
|
||||
tbl << [ "nessus_user_add", "Add a new Nessus User" ]
|
||||
tbl << [ "nessus_user_del", "Delete a Nessus User" ]
|
||||
tbl << [ "nessus_user_passwd", "Change Nessus Users Password" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "Policy Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_policy_list", "List all polciies" ]
|
||||
tbl << [ "nessus_policy_del", "Delete a policy" ]
|
||||
print_status ""
|
||||
print_line tbl.to_s
|
||||
print_status ""
|
||||
end
|
||||
|
||||
def cmd_nessus_server_feed(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_server_feed")
|
||||
print_status(" Example:> nessus_server_feed")
|
||||
print_status()
|
||||
print_status("Returns information about the feed type and server version.")
|
||||
return
|
||||
end
|
||||
|
||||
if nessus_verify_token
|
||||
@feed, @version, @web_version = @n.feed
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Feed',
|
||||
'Nessus Version',
|
||||
'Nessus Web Version'
|
||||
])
|
||||
tbl << [@feed, @version, @web_version]
|
||||
print_good("Nessus Status")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def nessus_verify_token
|
||||
if @token.nil? or @token == ''
|
||||
ncusage
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def nessus_verify_db
|
||||
|
||||
if ! (framework.db and framework.db.active)
|
||||
print_error("No database has been configured, please use db_create/db_connect first")
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def ncusage
|
||||
print_status("%redYou must do this before any other commands.%clr")
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_connect username:password@hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect username@hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect 192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect")
|
||||
print_status(" Example:> nessus_connect")
|
||||
print_status("This only works after you have saved creds with nessus_save")
|
||||
return
|
||||
end
|
||||
|
||||
def cmd_nessus_connect(*args)
|
||||
# Check if config file exists and load it
|
||||
@nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml"
|
||||
if ! args[0]
|
||||
if File.exist?("#{@nessus_yaml}")
|
||||
lconfig = YAML.load_file("#{@nessus_yaml}")
|
||||
@user = lconfig['default']['username']
|
||||
@pass = lconfig['default']['password']
|
||||
@host = lconfig['default']['server']
|
||||
@port = lconfig['default']['port']
|
||||
nessus_login
|
||||
return
|
||||
else
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("%redYou must do this before any other commands.%clr")
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_connect username:password@hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect username@hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect 192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect")
|
||||
print_status(" Example:> nessus_connect")
|
||||
print_status("This only works after you have saved creds with nessus_save")
|
||||
print_status()
|
||||
print_status("%bldusername%clr and %bldpassword%clr are the ones you use to login to the nessus web front end")
|
||||
print_status("%bldhostname%clr can be an ip address or a dns name of the web front end.")
|
||||
print_status("%bldport%clr is the standard that the nessus web front end runs on : 8834. This is NOT 1241.")
|
||||
print_status("The \"ok\" on the end is important. It is a way of letting you")
|
||||
print_status("know that nessus used a self signed cert and the risk that presents.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! @token == ''
|
||||
print_error("You are already authenticated. Call nessus_logout before authing again")
|
||||
return
|
||||
end
|
||||
|
||||
if(args.length == 0 or args[0].empty?)
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
@user = @pass = @host = @port = @sslv = nil
|
||||
|
||||
case args.length
|
||||
when 1,2
|
||||
if args[0].include? "@"
|
||||
cred,targ = args[0].split('@', 2)
|
||||
@user,@pass = cred.split(':', 2)
|
||||
targ ||= '127.0.0.1:8834'
|
||||
@host,@port = targ.split(':', 2)
|
||||
@port ||= '8834'
|
||||
@sslv = args[1]
|
||||
else
|
||||
@host,@port = args[0].split(':', 2)
|
||||
@port ||= '8834'
|
||||
@sslv = args[1]
|
||||
end
|
||||
|
||||
when 3,4,5
|
||||
ncusage
|
||||
return
|
||||
else
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
if /\/\//.match(@host)
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok")
|
||||
print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker")
|
||||
print_error(" with the ability to man-in-the-middle the Nessus traffic to capture the Nessus")
|
||||
print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
|
||||
print_error(" as an additional parameter to this command.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! @user
|
||||
print_error("Missing Username")
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
if ! @pass
|
||||
print_error("Missing Password")
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
nessus_login
|
||||
end
|
||||
|
||||
def nessus_login
|
||||
|
||||
if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
print_status("You need to connect to a server first.")
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
@url = "https://#{@host}:#{@port}/"
|
||||
print_status("Connecting to #{@url} as #{@user}")
|
||||
@n=NessusXMLRPC::NessusXMLRPC.new(@url,@user,@pass)
|
||||
@token=@n.login(@user,@pass)
|
||||
if @n.logged_in
|
||||
print_status("Authenticated")
|
||||
else
|
||||
print_error("Error connecting/logging to the server!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_report_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_list")
|
||||
print_status(" Example:> nessus_report_list")
|
||||
print_status()
|
||||
print_status("Generates a list of all reports visable to your user.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
list=@n.report_list_hash
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'ID',
|
||||
'Name',
|
||||
'Status',
|
||||
'Date'
|
||||
])
|
||||
|
||||
list.each {|report|
|
||||
t = Time.at(report['timestamp'].to_i)
|
||||
tbl << [ report['id'], report['name'], report['status'], t.strftime("%H:%M %b %d %Y") ]
|
||||
}
|
||||
print_good("Nessus Report List")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s + "\n"
|
||||
print_status("You can:")
|
||||
print_status(" Get a list of hosts from the report: nessus_report_hosts <report id>")
|
||||
end
|
||||
|
||||
def check_scan(*args)
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_error("No Report ID Supplied")
|
||||
return
|
||||
end
|
||||
|
||||
scans = @n.scan_list_hash
|
||||
scans.each {|scan|
|
||||
if scan['id'] == rid
|
||||
return true
|
||||
end
|
||||
}
|
||||
return false
|
||||
end
|
||||
|
||||
def cmd_nessus_report_get(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_get <report id>")
|
||||
print_status(" Example:> nessus_report_get f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("This command pulls the provided report from the nessus server in the nessusv2 format")
|
||||
print_status("and parses it the same way db_import_nessus does. After it is parsed it will be")
|
||||
print_status("available to commands such as db_hosts, db_vulns, db_services and db_autopwn.")
|
||||
print_status("Use: nessus_report_list to obtain a list of report id's")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_db
|
||||
return
|
||||
end
|
||||
|
||||
if(args.length == 0 or args[0].empty? or args[0] == "-h")
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_get <report id> ")
|
||||
print_status(" use nessus_report_list to list all available reports for importing")
|
||||
return
|
||||
end
|
||||
|
||||
rid = nil
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_get <report id> ")
|
||||
print_status(" use nessus_report_list to list all available reports for importing")
|
||||
return
|
||||
end
|
||||
|
||||
if check_scan(rid)
|
||||
print_error("That scan is still running.")
|
||||
return
|
||||
end
|
||||
content = nil
|
||||
content=@n.report_file_download(rid)
|
||||
if content.nil?
|
||||
print_error("Failed, please reauthenticate")
|
||||
return
|
||||
end
|
||||
print_status("importing " + rid)
|
||||
framework.db.import({:data => content}) do |type,data|
|
||||
case type
|
||||
when :address
|
||||
print_line("%bld%blu[*]%clr %bld#{data}%clr")
|
||||
end
|
||||
end
|
||||
print_good("Done")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_status(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_status")
|
||||
print_status(" Example:> nessus_scan_status")
|
||||
print_status()
|
||||
print_status("Returns a list of information about currently running scans.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
list=@n.scan_list_hash
|
||||
if list.empty?
|
||||
print_status("No Scans Running.")
|
||||
print_status("You can:")
|
||||
print_status(" List of completed scans: nessus_report_list")
|
||||
print_status(" Create a scan: nessus_scan_new <policy id> <scan name> <target(s)>")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Scan ID',
|
||||
'Name',
|
||||
'Owner',
|
||||
'Started',
|
||||
'Status',
|
||||
'Current Hosts',
|
||||
'Total Hosts'
|
||||
])
|
||||
|
||||
list.each {|scan|
|
||||
t = Time.at(scan['start'].to_i)
|
||||
tbl << [ scan['id'], scan['name'], scan['owner'], t.strftime("%H:%M %b %d %Y"), scan['status'], scan['current'], scan['total'] ]
|
||||
}
|
||||
print_good("Running Scans")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
print_good "\n"
|
||||
print_status("You can:")
|
||||
print_good(" Import Nessus report to database : nessus_report_get <reportid>")
|
||||
print_good(" Pause a nessus scan : nessus_scan_pause <scanid>")
|
||||
end
|
||||
|
||||
def cmd_nessus_template_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_template_list")
|
||||
print_status(" Example:> nessus_template_list")
|
||||
print_status()
|
||||
print_status("Returns a list of information about the server templates..")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
list=@n.template_list_hash
|
||||
|
||||
if list.empty?
|
||||
print_status("No Templates Created.")
|
||||
print_status("You can:")
|
||||
print_status(" List of completed scans: nessus_report_list")
|
||||
print_status(" Create a template: nessus_template_new <policy id> <scan name> <target(s)>")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Template ID',
|
||||
'Policy ID',
|
||||
'Name',
|
||||
'Owner',
|
||||
'Target'
|
||||
])
|
||||
|
||||
list.each {|template|
|
||||
tbl << [ template['name'], template['pid'], template['rname'], template['owner'], template['target'] ]
|
||||
}
|
||||
print_good("Templates")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s + "\n"
|
||||
print_good "\n"
|
||||
print_status("You can:")
|
||||
print_good(" Import Nessus report to database : nessus_report_get <reportid>")
|
||||
end
|
||||
|
||||
def cmd_nessus_user_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_list")
|
||||
print_status(" Example:> nessus_user_list")
|
||||
print_status()
|
||||
print_status("Returns a list of the users on the Nessus server and their access level.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_status("Your Nessus user is not an admin")
|
||||
end
|
||||
|
||||
list=@n.users_list
|
||||
print_good("There are #{list.length} users")
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Name',
|
||||
'Is Admin?',
|
||||
'Last Login'
|
||||
])
|
||||
|
||||
list.each {|user|
|
||||
t = Time.at(user['lastlogin'].to_i)
|
||||
tbl << [ user['name'], user['admin'], t.strftime("%H:%M %b %d %Y") ]
|
||||
}
|
||||
print_good("Nessus users")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_server_status(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_server_status")
|
||||
print_status(" Example:> nessus_server_status")
|
||||
print_status()
|
||||
print_status("Returns some status items for the server..")
|
||||
return
|
||||
end
|
||||
#Auth
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
#Check if we are an admin
|
||||
if ! @n.is_admin
|
||||
print_status("You need to be an admin for this.")
|
||||
return
|
||||
end
|
||||
|
||||
#Versions
|
||||
cmd_nessus_server_feed
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Users',
|
||||
'Policies',
|
||||
'Running Scans',
|
||||
'Reports',
|
||||
'Plugins'
|
||||
])
|
||||
#Count how many users the server has.
|
||||
list=@n.users_list
|
||||
users = list.length
|
||||
|
||||
#Count how many policies
|
||||
list=@n.policy_list_hash
|
||||
policies = list.length
|
||||
|
||||
#Count how many running scans
|
||||
list=@n.scan_list_uids
|
||||
scans = list.length
|
||||
|
||||
#Count how many reports are available
|
||||
list=@n.report_list_hash
|
||||
reports = list.length
|
||||
|
||||
#Count how many plugins
|
||||
list=@n.plugins_list
|
||||
total = Array.new
|
||||
list.each {|plugin|
|
||||
total.push(plugin['num'].to_i)
|
||||
}
|
||||
plugins = total.sum
|
||||
tbl << [users, policies, scans, reports, plugins]
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_plugin_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_list")
|
||||
print_status(" Example:> nessus_plugin_list")
|
||||
print_status()
|
||||
print_status("Returns a list of the plugins on the server per family.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Family Name',
|
||||
'Total Plugins'
|
||||
])
|
||||
list=@n.plugins_list
|
||||
total = Array.new
|
||||
list.each {|plugin|
|
||||
total.push(plugin['num'].to_i)
|
||||
tbl << [ plugin['name'], plugin['num'] ]
|
||||
}
|
||||
plugins = total.sum
|
||||
tbl << [ '', '']
|
||||
tbl << [ 'Total Plugins', plugins ]
|
||||
print_good("Plugins By Family")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
print_status("List plugins for a family : nessus_plugin_family <family name>")
|
||||
end
|
||||
|
||||
def check_policy(*args)
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
pid = args[0]
|
||||
else
|
||||
print_error("No Policy ID supplied.")
|
||||
return
|
||||
end
|
||||
|
||||
pol = @n.policy_list_hash
|
||||
pol.each {|p|
|
||||
if p['id'].to_i == pid
|
||||
return false
|
||||
end
|
||||
}
|
||||
return true
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_new(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_new <policy id> <scan name> <targets>")
|
||||
print_status(" Example:> nessus_scan_new 1 \"My Scan\" 192.168.1.250")
|
||||
print_status()
|
||||
print_status("Creates a scan based on a policy id and targets.")
|
||||
print_status("use nessus_policy_list to list all available policies")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 3
|
||||
pid = args[0].to_i
|
||||
name = args[1]
|
||||
tgts = args[2]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_new <policy id> <scan name> <targets>")
|
||||
print_status(" use nessus_policy_list to list all available policies")
|
||||
return
|
||||
end
|
||||
|
||||
if check_policy(pid)
|
||||
print_error("That policy does not exist.")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning #{tgts}")
|
||||
|
||||
scan = @n.scan_new(pid, name, tgts)
|
||||
|
||||
if scan
|
||||
print_status("Scan started. uid is #{scan}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_pause(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_pause <scan id>")
|
||||
print_status(" Example:> nessus_scan_pause f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Pauses a running scan")
|
||||
print_status("use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
sid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_pause <scan id>")
|
||||
print_status(" use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_pause(sid)
|
||||
|
||||
print_status("#{sid} has been paused")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_resume(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_resume <scan id>")
|
||||
print_status(" Example:> nessus_scan_resume f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("resumes a running scan")
|
||||
print_status("use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
sid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_resume <scan id>")
|
||||
print_status(" use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
resume = @n.scan_resume(sid)
|
||||
|
||||
print_status("#{sid} has been resumed")
|
||||
end
|
||||
|
||||
def cmd_nessus_report_hosts(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_hosts <report id>")
|
||||
print_status(" Example:> nessus_report_hosts f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Returns all the hosts associated with a scan and details about their vulnerabilities")
|
||||
print_status("use nessus_report_list to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_hosts <report id>")
|
||||
print_status(" use nessus_report_list to list all available reports")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Hostname',
|
||||
'Severity',
|
||||
'Sev 0',
|
||||
'Sev 1',
|
||||
'Sev 2',
|
||||
'Sev 3',
|
||||
'Current Progress',
|
||||
'Total Progress'
|
||||
])
|
||||
hosts=@n.report_hosts(rid)
|
||||
hosts.each {|host|
|
||||
tbl << [ host['hostname'], host['severity'], host['sev0'], host['sev1'], host['sev2'], host['sev3'], host['current'], host['total'] ]
|
||||
}
|
||||
print_good("Report Info")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
print_status("You can:")
|
||||
print_status(" Get information from a particular host: nessus_report_host_ports <hostname> <report id>")
|
||||
end
|
||||
|
||||
def cmd_nessus_report_vulns(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_vulns <report id>")
|
||||
print_status(" Example:> nessus_report_vulns f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Returns all the vulns associated with a scan and details about hosts and their vulnerabilities")
|
||||
print_status("use nessus_report_list to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_vulns <report id>")
|
||||
print_status(" use nessus_report_vulns to list all available reports")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Hostname',
|
||||
'Port',
|
||||
'Proto',
|
||||
'Sev',
|
||||
'PluginID',
|
||||
'Plugin Name'
|
||||
])
|
||||
print_status("Grabbing all vulns for report #{rid}")
|
||||
hosts=@n.report_hosts(rid)
|
||||
hosts.each do |host|
|
||||
ports=@n.report_host_ports(rid, host['hostname'])
|
||||
ports.each do |port|
|
||||
details=@n.report_host_port_details(rid, host['hostname'], port['portnum'], port['protocol'])
|
||||
details.each do |detail|
|
||||
tbl << [host['hostname'],
|
||||
port['portnum'],
|
||||
port['protocol'],
|
||||
detail['severity'],
|
||||
detail['pluginID'],
|
||||
detail['pluginName']
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
print_good("Report Info")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_status("You can:")
|
||||
print_status(" Get information from a particular host: nessus_report_host_ports <hostname> <report id>")
|
||||
end
|
||||
|
||||
def cmd_nessus_report_host_ports(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_host_ports <hostname> <report id>")
|
||||
print_status(" Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Returns all the ports associated with a host and details about their vulnerabilities")
|
||||
print_status("use nessus_report_hosts to list all available hosts for a report")
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
host = args[0]
|
||||
rid = args[1]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_host_ports <hostname> <report id>")
|
||||
print_status(" use nessus_report_list to list all available reports")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Port',
|
||||
'Protocol',
|
||||
'Severity',
|
||||
'Service Name',
|
||||
'Sev 0',
|
||||
'Sev 1',
|
||||
'Sev 2',
|
||||
'Sev 3'
|
||||
])
|
||||
ports=@n.report_host_ports(rid, host)
|
||||
ports.each {|port|
|
||||
tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ]
|
||||
}
|
||||
print_good("Host Info")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
print_status("You can:")
|
||||
print_status(" Get detailed scan infromation about a specfic port: nessus_report_host_detail <hostname> <port> <protocol> <report id>")
|
||||
end
|
||||
|
||||
def cmd_nessus_report_host_detail(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>")
|
||||
print_status(" Example:> nessus_report_host_ports 192.168.1.250 445 tcp f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Returns all the vulns associated with a port for a specific host")
|
||||
print_status("use nessus_report_host_ports to list all available ports for a host")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 4
|
||||
host = args[0]
|
||||
port = args[1]
|
||||
prot = args[2]
|
||||
rid = args[3]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>")
|
||||
print_status(" use nessus_report_host_ports to list all available ports")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Port',
|
||||
'Severity',
|
||||
'PluginID',
|
||||
'Plugin Name',
|
||||
'CVSS2',
|
||||
'Exploit?',
|
||||
'CVE',
|
||||
'Risk Factor',
|
||||
'CVSS Vector'
|
||||
])
|
||||
details=@n.report_host_port_details(rid, host, port, prot)
|
||||
details.each {|detail|
|
||||
tbl << [
|
||||
detail['port'],
|
||||
detail['severity'],
|
||||
detail['pluginID'],
|
||||
detail['pluginName'],
|
||||
detail['cvss_base_score'] || 'none',
|
||||
detail['exploit_available'] || '.',
|
||||
detail['cve'] || '.',
|
||||
detail['risk_factor'] || '.',
|
||||
detail['cvss_vector'] || '.'
|
||||
]
|
||||
}
|
||||
print_good("Port Info")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_pause_all(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_pause_all")
|
||||
print_status(" Example:> nessus_scan_pause_all")
|
||||
print_status()
|
||||
print_status("Pauses all currently running scans")
|
||||
print_status("use nessus_scan_list to list all running scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_pause_all
|
||||
|
||||
print_status("All scans have been paused")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_stop(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_stop <scan id>")
|
||||
print_status(" Example:> nessus_scan_stop f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Stops a currently running scans")
|
||||
print_status("use nessus_scan_list to list all running scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
sid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_stop <scan id>")
|
||||
print_status(" use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_stop(sid)
|
||||
|
||||
print_status("#{sid} has been stopped")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_stop_all(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_stop_all")
|
||||
print_status(" Example:> nessus_scan_stop_all")
|
||||
print_status()
|
||||
print_status("stops all currently running scans")
|
||||
print_status("use nessus_scan_list to list all running scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_stop_all
|
||||
|
||||
print_status("All scans have been stopped")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_resume_all(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_resume_all")
|
||||
print_status(" Example:> nessus_scan_resume_all")
|
||||
print_status()
|
||||
print_status("resumes all currently running scans")
|
||||
print_status("use nessus_scan_list to list all running scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_resume_all
|
||||
|
||||
print_status("All scans have been resumed")
|
||||
end
|
||||
|
||||
def cmd_nessus_user_add(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_add <username> <password>")
|
||||
print_status(" Example:> nessus_user_add msf msf")
|
||||
print_status()
|
||||
print_status("Only adds non admin users. Must be an admin to add users.")
|
||||
print_status("use nessus_user_list to list all users")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
user = args[0]
|
||||
pass = args[1]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_add <username> <password>")
|
||||
print_status(" Only adds non admin users")
|
||||
return
|
||||
end
|
||||
|
||||
u = @n.users_list
|
||||
u.each { |stuff|
|
||||
if stuff['name'] == user
|
||||
print_error("That user exists")
|
||||
return
|
||||
end
|
||||
}
|
||||
add = @n.user_add(user,pass)
|
||||
status = add.root.elements['status'].text if add
|
||||
if status == "OK"
|
||||
print_good("#{user} has been added")
|
||||
else
|
||||
print_error("#{user} was not added")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_user_del(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_del <username>")
|
||||
print_status(" Example:> nessus_user_del msf")
|
||||
print_status()
|
||||
print_status("Only dels non admin users. Must be an admin to del users.")
|
||||
print_status("use nessus_user_list to list all users")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
user = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_del <username>")
|
||||
print_status(" Only dels non admin users")
|
||||
return
|
||||
end
|
||||
|
||||
del = @n.user_del(user)
|
||||
status = del.root.elements['status'].text
|
||||
if status == "OK"
|
||||
print_good("#{user} has been deleted")
|
||||
else
|
||||
print_error("#{user} was not deleted")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_user_passwd(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_passwd <username> <password>")
|
||||
print_status(" Example:> nessus_user_passwd msf newpassword")
|
||||
print_status()
|
||||
print_status("Changes the password of a user. Must be an admin to change passwords.")
|
||||
print_status("use nessus_user_list to list all users")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
user = args[0]
|
||||
pass = args[1]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_passwd <username> <password>")
|
||||
print_status(" User list from nessus_user_list")
|
||||
return
|
||||
end
|
||||
|
||||
pass = @n.user_pass(user,pass)
|
||||
status = pass.root.elements['status'].text
|
||||
if status == "OK"
|
||||
print_good("#{user}'s password has been changed")
|
||||
else
|
||||
print_error("#{user}'s password has not been changed")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_admin(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_admin")
|
||||
print_status(" Example:> nessus_admin")
|
||||
print_status()
|
||||
print_status("Checks to see if the current user is an admin")
|
||||
print_status("use nessus_user_list to list all users")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
else
|
||||
print_good("Your Nessus user is an admin")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_plugin_family(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_family <plugin family name>")
|
||||
print_status(" Example:> nessus_plugin_family \"Windows : Microsoft Bulletins\" ")
|
||||
print_status()
|
||||
print_status("Returns a list of all plugins in that family.")
|
||||
print_status("use nessus_plugin_list to list all plugins")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
fam = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_family <plugin family name>")
|
||||
print_status(" list all plugins from a Family from nessus_plugin_list")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Plugin ID',
|
||||
'Plugin Name',
|
||||
'Plugin File Name'
|
||||
])
|
||||
|
||||
family = @n.plugin_family(fam)
|
||||
|
||||
family.each {|plugin|
|
||||
tbl << [ plugin['id'], plugin['name'], plugin['filename'] ]
|
||||
}
|
||||
print_good("#{fam} Info")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_policy_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_policy_list")
|
||||
print_status(" Example:> nessus_policy_list")
|
||||
print_status()
|
||||
print_status("Lists all policies on the server")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'ID',
|
||||
'Name',
|
||||
'Comments'
|
||||
])
|
||||
list=@n.policy_list_hash
|
||||
list.each {|policy|
|
||||
tbl << [ policy['id'], policy['name'], policy['comments'] ]
|
||||
}
|
||||
print_good("Nessus Policy List")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_policy_del(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_policy_del <policy ID>")
|
||||
print_status(" Example:> nessus_policy_del 1")
|
||||
print_status()
|
||||
print_status("Must be an admin to del policies.")
|
||||
print_status("use nessus_policy_list to list all policies")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
pid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_policy_del <policy ID>")
|
||||
print_status(" nessus_policy_list to find the id.")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
del = @n.policy_del(pid)
|
||||
status = del.root.elements['status'].text
|
||||
if status == "OK"
|
||||
print_good("Policy number #{pid} has been deleted")
|
||||
else
|
||||
print_error("Policy number #{pid} was not deleted")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def cmd_nessus_plugin_details(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_details <plugin file name>")
|
||||
print_status(" Example:> nessus_plugin_details ping_host.nasl ")
|
||||
print_status()
|
||||
print_status("Returns details on a particular plugin.")
|
||||
print_status("use nessus_plugin_list to list all plugins")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
pname = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_policy_del <plugin file name>")
|
||||
print_status(" nessus_plugin_list and then nessus_plugin_family to find the plugin file name.")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'',
|
||||
''
|
||||
])
|
||||
|
||||
entry = @n.plugin_detail(pname)
|
||||
print_good("Plugin Details for #{entry['name']}")
|
||||
tbl << [ "Plugin ID", entry['id'] ]
|
||||
tbl << [ "Plugin Family", entry['family'] ]
|
||||
tbl << [ "CVSS Base Score", entry['cvss_base_score'] ]
|
||||
tbl << [ "CVSS Vector", entry['cvss_vector'] ]
|
||||
tbl << [ "CVSS Temporal Score", entry['cvss_temporal_score'] ]
|
||||
tbl << [ "CVSS Temporal Vector", entry['cvss_temporal_vector'] ]
|
||||
tbl << [ "Risk Factor", entry['risk_factor'] ]
|
||||
tbl << [ "Exploit Available", entry['exploit_available'] ]
|
||||
tbl << [ "Exploitability Ease", entry['exploit_ease'] ]
|
||||
tbl << [ "Synopsis", entry['synopsis'] ]
|
||||
tbl << [ "Description", entry['description'] ]
|
||||
tbl << [ "Solution", entry['solution'] ]
|
||||
tbl << [ "Plugin Pub Date", entry['plugin_publication_date'] ]
|
||||
tbl << [ "Plugin Modification Date", entry['plugin_modification_date'] ]
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_report_del(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_del <reportname>")
|
||||
print_status(" Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Must be an admin to del reports.")
|
||||
print_status("use nessus_report_list to list all reports")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_del <report ID>")
|
||||
print_status(" nessus_report_list to find the id.")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
del = @n.report_del(rid)
|
||||
status = del.root.elements['status'].text
|
||||
if status == "OK"
|
||||
print_good("Report #{rid} has been deleted")
|
||||
else
|
||||
print_error("Report #{rid} was not deleted")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_server_prefs(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_server_prefs")
|
||||
print_status(" Example:> nessus_server_prefs")
|
||||
print_status()
|
||||
print_status("Returns a long list of server prefs.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Name',
|
||||
'Value'
|
||||
])
|
||||
prefs = @n.server_prefs
|
||||
prefs.each {|pref|
|
||||
tbl << [ pref['name'], pref['value'] ]
|
||||
}
|
||||
print_good("Nessus Server Pref List")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s + "\n"
|
||||
|
||||
end
|
||||
|
||||
def cmd_nessus_plugin_prefs(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_prefs")
|
||||
print_status(" Example:> nessus_plugin_prefs")
|
||||
print_status()
|
||||
print_status("Returns a long list of plugin prefs.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Name',
|
||||
'Value',
|
||||
'Type'
|
||||
])
|
||||
prefs = @n.plugin_prefs
|
||||
prefs.each {|pref|
|
||||
tbl << [ pref['prefname'], pref['prefvalues'], pref['preftype'] ]
|
||||
}
|
||||
print_good("Nessus Plugins Pref List")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
add_console_dispatcher(ConsoleCommandDispatcher)
|
||||
@nbver = "1.1" # Nessus Plugin Version. Increments each time we commit to msf
|
||||
@xindex = "#{Msf::Config.get_config_root}/nessus_index" # location of the exploit index file used to speed up searching for valid exploits.
|
||||
@nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" #location of the nessus.yml containing saved nessus creds
|
||||
print_status("Nessus Bridge for Metasploit #{@nbver}")
|
||||
print_good("Type %bldnessus_help%clr for a command listing")
|
||||
#nessus_index
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('Nessus')
|
||||
end
|
||||
|
||||
def name
|
||||
"nessus"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Nessus Bridge for Metasploit #{@nbver}"
|
||||
end
|
||||
protected
|
||||
end
|
||||
class Plugin::Nessus < Msf::Plugin
|
||||
|
||||
#creates the index of exploit details to make searching for exploits much faster.
|
||||
def create_xindex
|
||||
start = Time.now
|
||||
print_status("Creating Exploit Search Index - (#{@xindex}) - this wont take long.")
|
||||
count = 0
|
||||
# use Msf::Config.get_config_root as the location.
|
||||
File.open("#{@xindex}", "w+") do |f|
|
||||
#need to add version line.
|
||||
f.puts(Msf::Framework::RepoRevision)
|
||||
framework.exploits.sort.each { |refname, mod|
|
||||
stuff = ""
|
||||
o = nil
|
||||
begin
|
||||
o = mod.new
|
||||
rescue ::Exception
|
||||
end
|
||||
stuff << "#{refname}|#{o.name}|#{o.platform_to_s}|#{o.arch_to_s}"
|
||||
next if not o
|
||||
o.references.map do |x|
|
||||
if !(x.ctx_id == "URL")
|
||||
if (x.ctx_id == "MSB")
|
||||
stuff << "|#{x.ctx_val}"
|
||||
else
|
||||
stuff << "|#{x.ctx_id}-#{x.ctx_val}"
|
||||
end
|
||||
end
|
||||
end
|
||||
stuff << "\n"
|
||||
f.puts(stuff)
|
||||
}
|
||||
end
|
||||
total = Time.now - start
|
||||
print_status("It has taken : #{total} seconds to build the exploits search index")
|
||||
end
|
||||
|
||||
def nessus_index
|
||||
if File.exist?("#{@xindex}")
|
||||
#check if it's version line matches current version.
|
||||
File.open("#{@xindex}") {|f|
|
||||
line = f.readline
|
||||
line.chomp!
|
||||
if line.to_i == Msf::Framework::RepoRevision
|
||||
print_good("Exploit Index - (#{@xindex}) - is valid.")
|
||||
else
|
||||
create_xindex
|
||||
end
|
||||
}
|
||||
else
|
||||
create_xindex
|
||||
end
|
||||
end
|
||||
|
||||
class ConsoleCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"Nessus"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
"nessus_connect" => "Connect to a nessus server: nconnect username:password@hostname:port <ssl ok>.",
|
||||
"nessus_admin" => "Checks if user is an admin.",
|
||||
"nessus_help" => "Get help on all commands.",
|
||||
"nessus_logout" => "Terminate the session.",
|
||||
"nessus_server_status" => "Check the status of your Nessus Server.",
|
||||
"nessus_server_feed" => "Nessus Feed Type.",
|
||||
"nessus_server_prefs" => "Display Server Prefs.",
|
||||
"nessus_report_list" => "List all Nessus reports.",
|
||||
"nessus_report_get" => "Import a report from the nessus server in Nessus v2 format.",
|
||||
"nessus_report_del" => "Delete a report.",
|
||||
"nessus_report_vulns" => "Get list of vulns from a report.",
|
||||
"nessus_report_hosts" => "Get list of hosts from a report.",
|
||||
"nessus_report_host_ports" => "Get list of open ports from a host from a report.",
|
||||
"nessus_report_host_detail" => "Detail from a report item on a host.",
|
||||
"nessus_scan_status" => "List all currently running Nessus scans.",
|
||||
"nessus_scan_new" => "Create new Nessus Scan.",
|
||||
"nessus_scan_pause" => "Pause a Nessus Scan.",
|
||||
"nessus_scan_pause_all" => "Pause all Nessus Scans.",
|
||||
"nessus_scan_stop" => "Stop a Nessus Scan.",
|
||||
"nessus_scan_stop_all" => "Stop all Nessus Scans.",
|
||||
"nessus_scan_resume" => "Resume a Nessus Scan.",
|
||||
"nessus_scan_resume_all" => "Resume all Nessus Scans.",
|
||||
"nessus_user_list" => "Show Nessus Users.",
|
||||
"nessus_user_add" => "Add a new Nessus User.",
|
||||
"nessus_user_del" => "Delete a Nessus User.",
|
||||
"nessus_user_passwd" => "Change Nessus Users Password.",
|
||||
"nessus_plugin_family" => "List plugins in a family.",
|
||||
"nessus_plugin_details" => "List details of a particular plugin.",
|
||||
"nessus_plugin_list" => "Displays each plugin family and the number of plugins.",
|
||||
"nessus_plugin_prefs" => "Display Plugin Prefs.",
|
||||
"nessus_policy_list" => "List all polciies.",
|
||||
"nessus_policy_del" => "Delete a policy.",
|
||||
"nessus_index" => "Manually generates a search index for exploits.",
|
||||
"nessus_template_list" => "List all the templates on the server.",
|
||||
"nessus_db_scan" => "Create a scan of all ips in db_hosts.",
|
||||
"nessus_save" => "Save username/passowrd/server/port details."
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_nessus_index
|
||||
Msf::Plugin::Nessus.nessus_index
|
||||
end
|
||||
|
||||
def cmd_nessus_save(*args)
|
||||
#if we are logged in, save session details to nessus.yaml
|
||||
@nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml"
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_save")
|
||||
return
|
||||
end
|
||||
|
||||
if args[0]
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_save")
|
||||
return
|
||||
end
|
||||
|
||||
group = "default"
|
||||
|
||||
if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
config = Hash.new
|
||||
config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}}
|
||||
File.open("#{@nessus_yaml}", "w+") do |f|
|
||||
f.puts YAML.dump(config)
|
||||
end
|
||||
print_good("#{@nessus_yaml} created.")
|
||||
|
||||
else
|
||||
print_error("Missing username/password/server/port - relogin and then try again.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_db_scan(*args)
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_db_scan <policy id> <scan name>")
|
||||
print_status(" Example:> nessus_db_scan 1 \"My Scan\"")
|
||||
print_status()
|
||||
print_status("Creates a scan based on all the hosts listed in db_hosts.")
|
||||
print_status("use nessus_policy_list to list all available policies")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
pid = args[0].to_i
|
||||
name = args[1]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_db_scan <policy id> <scan name>")
|
||||
print_status(" use nessus_policy_list to list all available policies")
|
||||
return
|
||||
end
|
||||
|
||||
if check_policy(pid)
|
||||
print_error("That policy does not exist.")
|
||||
return
|
||||
end
|
||||
|
||||
tgts = ""
|
||||
framework.db.hosts(framework.db.workspace).each do |host|
|
||||
tgts << host.address
|
||||
tgts << ","
|
||||
end
|
||||
|
||||
tgts.chop!
|
||||
|
||||
print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning all hosts in workspace")
|
||||
|
||||
scan = @n.scan_new(pid, name, tgts)
|
||||
|
||||
if scan
|
||||
print_status("Scan started. uid is #{scan}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def cmd_nessus_logout
|
||||
@token = nil
|
||||
print_status("Logged out")
|
||||
system("rm #{@nessus_yaml}")
|
||||
print_good("#{@nessus_yaml} removed.")
|
||||
return
|
||||
end
|
||||
|
||||
def cmd_nessus_help(*args)
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
"Command",
|
||||
"Help Text"
|
||||
],
|
||||
'SortIndex' => -1
|
||||
)
|
||||
tbl << [ "Generic Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_connect", "Connect to a nessus server" ]
|
||||
tbl << [ "nessus_save", "Save nessus login info between sessions" ]
|
||||
tbl << [ "nessus_logout", "Logout from the nessus server" ]
|
||||
tbl << [ "nessus_help", "Listing of available nessus commands" ]
|
||||
tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ]
|
||||
tbl << [ "nessus_admin", "Checks if user is an admin" ]
|
||||
tbl << [ "nessus_server_feed", "Nessus Feed Type" ]
|
||||
tbl << [ "nessus_find_targets", "Try to find vulnerable targets from a report" ]
|
||||
tbl << [ "nessus_server_prefs", "Display Server Prefs" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "Reports Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_report_list", "List all Nessus reports" ]
|
||||
tbl << [ "nessus_report_get", "Import a report from the nessus server in Nessus v2 format" ]
|
||||
tbl << [ "nessus_report_vulns", "Get list of vulns from a report" ]
|
||||
tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ]
|
||||
tbl << [ "nessus_report_host_ports", "Get list of open ports from a host from a report" ]
|
||||
tbl << [ "nessus_report_host_detail", "Detail from a report item on a host" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "Scan Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_scan_new", "Create new Nessus Scan" ]
|
||||
tbl << [ "nessus_scan_status", "List all currently running Nessus scans" ]
|
||||
tbl << [ "nessus_scan_pause", "Pause a Nessus Scan" ]
|
||||
tbl << [ "nessus_scan_pause_all", "Pause all Nessus Scans" ]
|
||||
tbl << [ "nessus_scan_stop", "Stop a Nessus Scan" ]
|
||||
tbl << [ "nessus_scan_stop_all", "Stop all Nessus Scans" ]
|
||||
tbl << [ "nessus_scan_resume", "Resume a Nessus Scan" ]
|
||||
tbl << [ "nessus_scan_resume_all", "Resume all Nessus Scans" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "Plugin Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_plugin_list", "Displays each plugin family and the number of plugins" ]
|
||||
tbl << [ "nessus_plugin_family", "List plugins in a family" ]
|
||||
tbl << [ "nessus_plugin_details", "List details of a particular plugin" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "User Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_user_list", "Show Nessus Users" ]
|
||||
tbl << [ "nessus_user_add", "Add a new Nessus User" ]
|
||||
tbl << [ "nessus_user_del", "Delete a Nessus User" ]
|
||||
tbl << [ "nessus_user_passwd", "Change Nessus Users Password" ]
|
||||
tbl << [ "", ""]
|
||||
tbl << [ "Policy Commands", "" ]
|
||||
tbl << [ "-----------------", "-----------------"]
|
||||
tbl << [ "nessus_policy_list", "List all polciies" ]
|
||||
tbl << [ "nessus_policy_del", "Delete a policy" ]
|
||||
print_status ""
|
||||
print_line tbl.to_s
|
||||
print_status ""
|
||||
end
|
||||
|
||||
def cmd_nessus_server_feed(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_server_feed")
|
||||
print_status(" Example:> nessus_server_feed")
|
||||
print_status()
|
||||
print_status("Returns information about the feed type and server version.")
|
||||
return
|
||||
end
|
||||
|
||||
if nessus_verify_token
|
||||
@feed, @version, @web_version = @n.feed
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Feed',
|
||||
'Nessus Version',
|
||||
'Nessus Web Version'
|
||||
])
|
||||
tbl << [@feed, @version, @web_version]
|
||||
print_good("Nessus Status")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def nessus_verify_token
|
||||
if @token.nil? or @token == ''
|
||||
ncusage
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def nessus_verify_db
|
||||
|
||||
if ! (framework.db and framework.db.active)
|
||||
print_error("No database has been configured, please use db_create/db_connect first")
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def ncusage
|
||||
print_status("%redYou must do this before any other commands.%clr")
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_connect username:password@hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect username@hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect 192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect")
|
||||
print_status(" Example:> nessus_connect")
|
||||
print_status("This only works after you have saved creds with nessus_save")
|
||||
return
|
||||
end
|
||||
|
||||
def cmd_nessus_connect(*args)
|
||||
# Check if config file exists and load it
|
||||
@nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml"
|
||||
if ! args[0]
|
||||
if File.exist?("#{@nessus_yaml}")
|
||||
lconfig = YAML.load_file("#{@nessus_yaml}")
|
||||
@user = lconfig['default']['username']
|
||||
@pass = lconfig['default']['password']
|
||||
@host = lconfig['default']['server']
|
||||
@port = lconfig['default']['port']
|
||||
nessus_login
|
||||
return
|
||||
else
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("%redYou must do this before any other commands.%clr")
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_connect username:password@hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect username@hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect hostname:port <ssl ok>")
|
||||
print_status(" Example:> nessus_connect 192.168.1.10:8834 ok")
|
||||
print_status(" OR")
|
||||
print_status(" nessus_connect")
|
||||
print_status(" Example:> nessus_connect")
|
||||
print_status("This only works after you have saved creds with nessus_save")
|
||||
print_status()
|
||||
print_status("%bldusername%clr and %bldpassword%clr are the ones you use to login to the nessus web front end")
|
||||
print_status("%bldhostname%clr can be an ip address or a dns name of the web front end.")
|
||||
print_status("%bldport%clr is the standard that the nessus web front end runs on : 8834. This is NOT 1241.")
|
||||
print_status("The \"ok\" on the end is important. It is a way of letting you")
|
||||
print_status("know that nessus used a self signed cert and the risk that presents.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! @token == ''
|
||||
print_error("You are already authenticated. Call nessus_logout before authing again")
|
||||
return
|
||||
end
|
||||
|
||||
if(args.length == 0 or args[0].empty?)
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
@user = @pass = @host = @port = @sslv = nil
|
||||
|
||||
case args.length
|
||||
when 1,2
|
||||
if args[0].include? "@"
|
||||
cred,targ = args[0].split('@', 2)
|
||||
@user,@pass = cred.split(':', 2)
|
||||
targ ||= '127.0.0.1:8834'
|
||||
@host,@port = targ.split(':', 2)
|
||||
@port ||= '8834'
|
||||
@sslv = args[1]
|
||||
else
|
||||
@host,@port = args[0].split(':', 2)
|
||||
@port ||= '8834'
|
||||
@sslv = args[1]
|
||||
end
|
||||
|
||||
when 3,4,5
|
||||
ncusage
|
||||
return
|
||||
else
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
if /\/\//.match(@host)
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok")
|
||||
print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker")
|
||||
print_error(" with the ability to man-in-the-middle the Nessus traffic to capture the Nessus")
|
||||
print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
|
||||
print_error(" as an additional parameter to this command.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! @user
|
||||
print_error("Missing Username")
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
if ! @pass
|
||||
print_error("Missing Password")
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
nessus_login
|
||||
end
|
||||
|
||||
def nessus_login
|
||||
|
||||
if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
print_status("You need to connect to a server first.")
|
||||
ncusage
|
||||
return
|
||||
end
|
||||
|
||||
@url = "https://#{@host}:#{@port}/"
|
||||
print_status("Connecting to #{@url} as #{@user}")
|
||||
@n=NessusXMLRPC::NessusXMLRPC.new(@url,@user,@pass)
|
||||
@token=@n.login(@user,@pass)
|
||||
if @n.logged_in
|
||||
print_status("Authenticated")
|
||||
else
|
||||
print_error("Error connecting/logging to the server!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_report_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_list")
|
||||
print_status(" Example:> nessus_report_list")
|
||||
print_status()
|
||||
print_status("Generates a list of all reports visable to your user.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
list=@n.report_list_hash
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'ID',
|
||||
'Name',
|
||||
'Status',
|
||||
'Date'
|
||||
])
|
||||
|
||||
list.each {|report|
|
||||
t = Time.at(report['timestamp'].to_i)
|
||||
tbl << [ report['id'], report['name'], report['status'], t.strftime("%H:%M %b %d %Y") ]
|
||||
}
|
||||
print_good("Nessus Report List")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s + "\n"
|
||||
print_status("You can:")
|
||||
print_status(" Get a list of hosts from the report: nessus_report_hosts <report id>")
|
||||
end
|
||||
|
||||
def check_scan(*args)
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_error("No Report ID Supplied")
|
||||
return
|
||||
end
|
||||
|
||||
scans = @n.scan_list_hash
|
||||
scans.each {|scan|
|
||||
if scan['id'] == rid
|
||||
return true
|
||||
end
|
||||
}
|
||||
return false
|
||||
end
|
||||
|
||||
def cmd_nessus_report_get(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_get <report id>")
|
||||
print_status(" Example:> nessus_report_get f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("This command pulls the provided report from the nessus server in the nessusv2 format")
|
||||
print_status("and parses it the same way db_import_nessus does. After it is parsed it will be")
|
||||
print_status("available to commands such as db_hosts, db_vulns, db_services and db_autopwn.")
|
||||
print_status("Use: nessus_report_list to obtain a list of report id's")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_db
|
||||
return
|
||||
end
|
||||
|
||||
if(args.length == 0 or args[0].empty? or args[0] == "-h")
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_get <report id> ")
|
||||
print_status(" use nessus_report_list to list all available reports for importing")
|
||||
return
|
||||
end
|
||||
|
||||
rid = nil
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_get <report id> ")
|
||||
print_status(" use nessus_report_list to list all available reports for importing")
|
||||
return
|
||||
end
|
||||
|
||||
if check_scan(rid)
|
||||
print_error("That scan is still running.")
|
||||
return
|
||||
end
|
||||
content = nil
|
||||
content=@n.report_file_download(rid)
|
||||
if content.nil?
|
||||
print_error("Failed, please reauthenticate")
|
||||
return
|
||||
end
|
||||
print_status("importing " + rid)
|
||||
framework.db.import({:data => content}) do |type,data|
|
||||
case type
|
||||
when :address
|
||||
print_line("%bld%blu[*]%clr %bld#{data}%clr")
|
||||
end
|
||||
end
|
||||
print_good("Done")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_status(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_status")
|
||||
print_status(" Example:> nessus_scan_status")
|
||||
print_status()
|
||||
print_status("Returns a list of information about currently running scans.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
list=@n.scan_list_hash
|
||||
if list.empty?
|
||||
print_status("No Scans Running.")
|
||||
print_status("You can:")
|
||||
print_status(" List of completed scans: nessus_report_list")
|
||||
print_status(" Create a scan: nessus_scan_new <policy id> <scan name> <target(s)>")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Scan ID',
|
||||
'Name',
|
||||
'Owner',
|
||||
'Started',
|
||||
'Status',
|
||||
'Current Hosts',
|
||||
'Total Hosts'
|
||||
])
|
||||
|
||||
list.each {|scan|
|
||||
t = Time.at(scan['start'].to_i)
|
||||
tbl << [ scan['id'], scan['name'], scan['owner'], t.strftime("%H:%M %b %d %Y"), scan['status'], scan['current'], scan['total'] ]
|
||||
}
|
||||
print_good("Running Scans")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
print_good "\n"
|
||||
print_status("You can:")
|
||||
print_good(" Import Nessus report to database : nessus_report_get <reportid>")
|
||||
print_good(" Pause a nessus scan : nessus_scan_pause <scanid>")
|
||||
end
|
||||
|
||||
def cmd_nessus_template_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_template_list")
|
||||
print_status(" Example:> nessus_template_list")
|
||||
print_status()
|
||||
print_status("Returns a list of information about the server templates..")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
list=@n.template_list_hash
|
||||
|
||||
if list.empty?
|
||||
print_status("No Templates Created.")
|
||||
print_status("You can:")
|
||||
print_status(" List of completed scans: nessus_report_list")
|
||||
print_status(" Create a template: nessus_template_new <policy id> <scan name> <target(s)>")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Template ID',
|
||||
'Policy ID',
|
||||
'Name',
|
||||
'Owner',
|
||||
'Target'
|
||||
])
|
||||
|
||||
list.each {|template|
|
||||
tbl << [ template['name'], template['pid'], template['rname'], template['owner'], template['target'] ]
|
||||
}
|
||||
print_good("Templates")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s + "\n"
|
||||
print_good "\n"
|
||||
print_status("You can:")
|
||||
print_good(" Import Nessus report to database : nessus_report_get <reportid>")
|
||||
end
|
||||
|
||||
def cmd_nessus_user_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_list")
|
||||
print_status(" Example:> nessus_user_list")
|
||||
print_status()
|
||||
print_status("Returns a list of the users on the Nessus server and their access level.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_status("Your Nessus user is not an admin")
|
||||
end
|
||||
|
||||
list=@n.users_list
|
||||
print_good("There are #{list.length} users")
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Name',
|
||||
'Is Admin?',
|
||||
'Last Login'
|
||||
])
|
||||
|
||||
list.each {|user|
|
||||
t = Time.at(user['lastlogin'].to_i)
|
||||
tbl << [ user['name'], user['admin'], t.strftime("%H:%M %b %d %Y") ]
|
||||
}
|
||||
print_good("Nessus users")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_server_status(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_server_status")
|
||||
print_status(" Example:> nessus_server_status")
|
||||
print_status()
|
||||
print_status("Returns some status items for the server..")
|
||||
return
|
||||
end
|
||||
#Auth
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
#Check if we are an admin
|
||||
if ! @n.is_admin
|
||||
print_status("You need to be an admin for this.")
|
||||
return
|
||||
end
|
||||
|
||||
#Versions
|
||||
cmd_nessus_server_feed
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Users',
|
||||
'Policies',
|
||||
'Running Scans',
|
||||
'Reports',
|
||||
'Plugins'
|
||||
])
|
||||
#Count how many users the server has.
|
||||
list=@n.users_list
|
||||
users = list.length
|
||||
|
||||
#Count how many policies
|
||||
list=@n.policy_list_hash
|
||||
policies = list.length
|
||||
|
||||
#Count how many running scans
|
||||
list=@n.scan_list_uids
|
||||
scans = list.length
|
||||
|
||||
#Count how many reports are available
|
||||
list=@n.report_list_hash
|
||||
reports = list.length
|
||||
|
||||
#Count how many plugins
|
||||
list=@n.plugins_list
|
||||
total = Array.new
|
||||
list.each {|plugin|
|
||||
total.push(plugin['num'].to_i)
|
||||
}
|
||||
plugins = total.sum
|
||||
tbl << [users, policies, scans, reports, plugins]
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_plugin_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_list")
|
||||
print_status(" Example:> nessus_plugin_list")
|
||||
print_status()
|
||||
print_status("Returns a list of the plugins on the server per family.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Family Name',
|
||||
'Total Plugins'
|
||||
])
|
||||
list=@n.plugins_list
|
||||
total = Array.new
|
||||
list.each {|plugin|
|
||||
total.push(plugin['num'].to_i)
|
||||
tbl << [ plugin['name'], plugin['num'] ]
|
||||
}
|
||||
plugins = total.sum
|
||||
tbl << [ '', '']
|
||||
tbl << [ 'Total Plugins', plugins ]
|
||||
print_good("Plugins By Family")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
print_status("List plugins for a family : nessus_plugin_family <family name>")
|
||||
end
|
||||
|
||||
def check_policy(*args)
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
pid = args[0]
|
||||
else
|
||||
print_error("No Policy ID supplied.")
|
||||
return
|
||||
end
|
||||
|
||||
pol = @n.policy_list_hash
|
||||
pol.each {|p|
|
||||
if p['id'].to_i == pid
|
||||
return false
|
||||
end
|
||||
}
|
||||
return true
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_new(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_new <policy id> <scan name> <targets>")
|
||||
print_status(" Example:> nessus_scan_new 1 \"My Scan\" 192.168.1.250")
|
||||
print_status()
|
||||
print_status("Creates a scan based on a policy id and targets.")
|
||||
print_status("use nessus_policy_list to list all available policies")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 3
|
||||
pid = args[0].to_i
|
||||
name = args[1]
|
||||
tgts = args[2]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_new <policy id> <scan name> <targets>")
|
||||
print_status(" use nessus_policy_list to list all available policies")
|
||||
return
|
||||
end
|
||||
|
||||
if check_policy(pid)
|
||||
print_error("That policy does not exist.")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning #{tgts}")
|
||||
|
||||
scan = @n.scan_new(pid, name, tgts)
|
||||
|
||||
if scan
|
||||
print_status("Scan started. uid is #{scan}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_pause(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_pause <scan id>")
|
||||
print_status(" Example:> nessus_scan_pause f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Pauses a running scan")
|
||||
print_status("use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
sid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_pause <scan id>")
|
||||
print_status(" use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_pause(sid)
|
||||
|
||||
print_status("#{sid} has been paused")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_resume(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_resume <scan id>")
|
||||
print_status(" Example:> nessus_scan_resume f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("resumes a running scan")
|
||||
print_status("use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
sid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_resume <scan id>")
|
||||
print_status(" use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
resume = @n.scan_resume(sid)
|
||||
|
||||
print_status("#{sid} has been resumed")
|
||||
end
|
||||
|
||||
def cmd_nessus_report_hosts(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_hosts <report id>")
|
||||
print_status(" Example:> nessus_report_hosts f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Returns all the hosts associated with a scan and details about their vulnerabilities")
|
||||
print_status("use nessus_report_list to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_hosts <report id>")
|
||||
print_status(" use nessus_report_list to list all available reports")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Hostname',
|
||||
'Severity',
|
||||
'Sev 0',
|
||||
'Sev 1',
|
||||
'Sev 2',
|
||||
'Sev 3',
|
||||
'Current Progress',
|
||||
'Total Progress'
|
||||
])
|
||||
hosts=@n.report_hosts(rid)
|
||||
hosts.each {|host|
|
||||
tbl << [ host['hostname'], host['severity'], host['sev0'], host['sev1'], host['sev2'], host['sev3'], host['current'], host['total'] ]
|
||||
}
|
||||
print_good("Report Info")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
print_status("You can:")
|
||||
print_status(" Get information from a particular host: nessus_report_host_ports <hostname> <report id>")
|
||||
end
|
||||
|
||||
def cmd_nessus_report_vulns(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_vulns <report id>")
|
||||
print_status(" Example:> nessus_report_vulns f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Returns all the vulns associated with a scan and details about hosts and their vulnerabilities")
|
||||
print_status("use nessus_report_list to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_vulns <report id>")
|
||||
print_status(" use nessus_report_vulns to list all available reports")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Hostname',
|
||||
'Port',
|
||||
'Proto',
|
||||
'Sev',
|
||||
'PluginID',
|
||||
'Plugin Name'
|
||||
])
|
||||
print_status("Grabbing all vulns for report #{rid}")
|
||||
hosts=@n.report_hosts(rid)
|
||||
hosts.each do |host|
|
||||
ports=@n.report_host_ports(rid, host['hostname'])
|
||||
ports.each do |port|
|
||||
details=@n.report_host_port_details(rid, host['hostname'], port['portnum'], port['protocol'])
|
||||
details.each do |detail|
|
||||
tbl << [host['hostname'],
|
||||
port['portnum'],
|
||||
port['protocol'],
|
||||
detail['severity'],
|
||||
detail['pluginID'],
|
||||
detail['pluginName']
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
print_good("Report Info")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_status("You can:")
|
||||
print_status(" Get information from a particular host: nessus_report_host_ports <hostname> <report id>")
|
||||
end
|
||||
|
||||
def cmd_nessus_report_host_ports(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_host_ports <hostname> <report id>")
|
||||
print_status(" Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Returns all the ports associated with a host and details about their vulnerabilities")
|
||||
print_status("use nessus_report_hosts to list all available hosts for a report")
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
host = args[0]
|
||||
rid = args[1]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_host_ports <hostname> <report id>")
|
||||
print_status(" use nessus_report_list to list all available reports")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Port',
|
||||
'Protocol',
|
||||
'Severity',
|
||||
'Service Name',
|
||||
'Sev 0',
|
||||
'Sev 1',
|
||||
'Sev 2',
|
||||
'Sev 3'
|
||||
])
|
||||
ports=@n.report_host_ports(rid, host)
|
||||
ports.each {|port|
|
||||
tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ]
|
||||
}
|
||||
print_good("Host Info")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
print_status("You can:")
|
||||
print_status(" Get detailed scan infromation about a specfic port: nessus_report_host_detail <hostname> <port> <protocol> <report id>")
|
||||
end
|
||||
|
||||
def cmd_nessus_report_host_detail(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>")
|
||||
print_status(" Example:> nessus_report_host_ports 192.168.1.250 445 tcp f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Returns all the vulns associated with a port for a specific host")
|
||||
print_status("use nessus_report_host_ports to list all available ports for a host")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 4
|
||||
host = args[0]
|
||||
port = args[1]
|
||||
prot = args[2]
|
||||
rid = args[3]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>")
|
||||
print_status(" use nessus_report_host_ports to list all available ports")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Port',
|
||||
'Severity',
|
||||
'PluginID',
|
||||
'Plugin Name',
|
||||
'CVSS2',
|
||||
'Exploit?',
|
||||
'CVE',
|
||||
'Risk Factor',
|
||||
'CVSS Vector'
|
||||
])
|
||||
details=@n.report_host_port_details(rid, host, port, prot)
|
||||
details.each {|detail|
|
||||
tbl << [
|
||||
detail['port'],
|
||||
detail['severity'],
|
||||
detail['pluginID'],
|
||||
detail['pluginName'],
|
||||
detail['cvss_base_score'] || 'none',
|
||||
detail['exploit_available'] || '.',
|
||||
detail['cve'] || '.',
|
||||
detail['risk_factor'] || '.',
|
||||
detail['cvss_vector'] || '.'
|
||||
]
|
||||
}
|
||||
print_good("Port Info")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_pause_all(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_pause_all")
|
||||
print_status(" Example:> nessus_scan_pause_all")
|
||||
print_status()
|
||||
print_status("Pauses all currently running scans")
|
||||
print_status("use nessus_scan_list to list all running scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_pause_all
|
||||
|
||||
print_status("All scans have been paused")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_stop(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_stop <scan id>")
|
||||
print_status(" Example:> nessus_scan_stop f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Stops a currently running scans")
|
||||
print_status("use nessus_scan_list to list all running scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
sid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_stop <scan id>")
|
||||
print_status(" use nessus_scan_status to list all available scans")
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_stop(sid)
|
||||
|
||||
print_status("#{sid} has been stopped")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_stop_all(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_stop_all")
|
||||
print_status(" Example:> nessus_scan_stop_all")
|
||||
print_status()
|
||||
print_status("stops all currently running scans")
|
||||
print_status("use nessus_scan_list to list all running scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_stop_all
|
||||
|
||||
print_status("All scans have been stopped")
|
||||
end
|
||||
|
||||
def cmd_nessus_scan_resume_all(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_scan_resume_all")
|
||||
print_status(" Example:> nessus_scan_resume_all")
|
||||
print_status()
|
||||
print_status("resumes all currently running scans")
|
||||
print_status("use nessus_scan_list to list all running scans")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
pause = @n.scan_resume_all
|
||||
|
||||
print_status("All scans have been resumed")
|
||||
end
|
||||
|
||||
def cmd_nessus_user_add(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_add <username> <password>")
|
||||
print_status(" Example:> nessus_user_add msf msf")
|
||||
print_status()
|
||||
print_status("Only adds non admin users. Must be an admin to add users.")
|
||||
print_status("use nessus_user_list to list all users")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
user = args[0]
|
||||
pass = args[1]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_add <username> <password>")
|
||||
print_status(" Only adds non admin users")
|
||||
return
|
||||
end
|
||||
|
||||
u = @n.users_list
|
||||
u.each { |stuff|
|
||||
if stuff['name'] == user
|
||||
print_error("That user exists")
|
||||
return
|
||||
end
|
||||
}
|
||||
add = @n.user_add(user,pass)
|
||||
status = add.root.elements['status'].text if add
|
||||
if status == "OK"
|
||||
print_good("#{user} has been added")
|
||||
else
|
||||
print_error("#{user} was not added")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_user_del(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_del <username>")
|
||||
print_status(" Example:> nessus_user_del msf")
|
||||
print_status()
|
||||
print_status("Only dels non admin users. Must be an admin to del users.")
|
||||
print_status("use nessus_user_list to list all users")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
user = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_del <username>")
|
||||
print_status(" Only dels non admin users")
|
||||
return
|
||||
end
|
||||
|
||||
del = @n.user_del(user)
|
||||
status = del.root.elements['status'].text
|
||||
if status == "OK"
|
||||
print_good("#{user} has been deleted")
|
||||
else
|
||||
print_error("#{user} was not deleted")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_user_passwd(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_passwd <username> <password>")
|
||||
print_status(" Example:> nessus_user_passwd msf newpassword")
|
||||
print_status()
|
||||
print_status("Changes the password of a user. Must be an admin to change passwords.")
|
||||
print_status("use nessus_user_list to list all users")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
user = args[0]
|
||||
pass = args[1]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_user_passwd <username> <password>")
|
||||
print_status(" User list from nessus_user_list")
|
||||
return
|
||||
end
|
||||
|
||||
pass = @n.user_pass(user,pass)
|
||||
status = pass.root.elements['status'].text
|
||||
if status == "OK"
|
||||
print_good("#{user}'s password has been changed")
|
||||
else
|
||||
print_error("#{user}'s password has not been changed")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_admin(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_admin")
|
||||
print_status(" Example:> nessus_admin")
|
||||
print_status()
|
||||
print_status("Checks to see if the current user is an admin")
|
||||
print_status("use nessus_user_list to list all users")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
else
|
||||
print_good("Your Nessus user is an admin")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_plugin_family(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_family <plugin family name>")
|
||||
print_status(" Example:> nessus_plugin_family \"Windows : Microsoft Bulletins\" ")
|
||||
print_status()
|
||||
print_status("Returns a list of all plugins in that family.")
|
||||
print_status("use nessus_plugin_list to list all plugins")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
fam = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_family <plugin family name>")
|
||||
print_status(" list all plugins from a Family from nessus_plugin_list")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Plugin ID',
|
||||
'Plugin Name',
|
||||
'Plugin File Name'
|
||||
])
|
||||
|
||||
family = @n.plugin_family(fam)
|
||||
|
||||
family.each {|plugin|
|
||||
tbl << [ plugin['id'], plugin['name'], plugin['filename'] ]
|
||||
}
|
||||
print_good("#{fam} Info")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_policy_list(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_policy_list")
|
||||
print_status(" Example:> nessus_policy_list")
|
||||
print_status()
|
||||
print_status("Lists all policies on the server")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'ID',
|
||||
'Name',
|
||||
'Comments'
|
||||
])
|
||||
list=@n.policy_list_hash
|
||||
list.each {|policy|
|
||||
tbl << [ policy['id'], policy['name'], policy['comments'] ]
|
||||
}
|
||||
print_good("Nessus Policy List")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_policy_del(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_policy_del <policy ID>")
|
||||
print_status(" Example:> nessus_policy_del 1")
|
||||
print_status()
|
||||
print_status("Must be an admin to del policies.")
|
||||
print_status("use nessus_policy_list to list all policies")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
pid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_policy_del <policy ID>")
|
||||
print_status(" nessus_policy_list to find the id.")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
del = @n.policy_del(pid)
|
||||
status = del.root.elements['status'].text
|
||||
if status == "OK"
|
||||
print_good("Policy number #{pid} has been deleted")
|
||||
else
|
||||
print_error("Policy number #{pid} was not deleted")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def cmd_nessus_plugin_details(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_details <plugin file name>")
|
||||
print_status(" Example:> nessus_plugin_details ping_host.nasl ")
|
||||
print_status()
|
||||
print_status("Returns details on a particular plugin.")
|
||||
print_status("use nessus_plugin_list to list all plugins")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
pname = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_policy_del <plugin file name>")
|
||||
print_status(" nessus_plugin_list and then nessus_plugin_family to find the plugin file name.")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'',
|
||||
''
|
||||
])
|
||||
|
||||
entry = @n.plugin_detail(pname)
|
||||
print_good("Plugin Details for #{entry['name']}")
|
||||
tbl << [ "Plugin ID", entry['id'] ]
|
||||
tbl << [ "Plugin Family", entry['family'] ]
|
||||
tbl << [ "CVSS Base Score", entry['cvss_base_score'] ]
|
||||
tbl << [ "CVSS Vector", entry['cvss_vector'] ]
|
||||
tbl << [ "CVSS Temporal Score", entry['cvss_temporal_score'] ]
|
||||
tbl << [ "CVSS Temporal Vector", entry['cvss_temporal_vector'] ]
|
||||
tbl << [ "Risk Factor", entry['risk_factor'] ]
|
||||
tbl << [ "Exploit Available", entry['exploit_available'] ]
|
||||
tbl << [ "Exploitability Ease", entry['exploit_ease'] ]
|
||||
tbl << [ "Synopsis", entry['synopsis'] ]
|
||||
tbl << [ "Description", entry['description'] ]
|
||||
tbl << [ "Solution", entry['solution'] ]
|
||||
tbl << [ "Plugin Pub Date", entry['plugin_publication_date'] ]
|
||||
tbl << [ "Plugin Modification Date", entry['plugin_modification_date'] ]
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def cmd_nessus_report_del(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_del <reportname>")
|
||||
print_status(" Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca")
|
||||
print_status()
|
||||
print_status("Must be an admin to del reports.")
|
||||
print_status("use nessus_report_list to list all reports")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
case args.length
|
||||
when 1
|
||||
rid = args[0]
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_report_del <report ID>")
|
||||
print_status(" nessus_report_list to find the id.")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
del = @n.report_del(rid)
|
||||
status = del.root.elements['status'].text
|
||||
if status == "OK"
|
||||
print_good("Report #{rid} has been deleted")
|
||||
else
|
||||
print_error("Report #{rid} was not deleted")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nessus_server_prefs(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_server_prefs")
|
||||
print_status(" Example:> nessus_server_prefs")
|
||||
print_status()
|
||||
print_status("Returns a long list of server prefs.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Name',
|
||||
'Value'
|
||||
])
|
||||
prefs = @n.server_prefs
|
||||
prefs.each {|pref|
|
||||
tbl << [ pref['name'], pref['value'] ]
|
||||
}
|
||||
print_good("Nessus Server Pref List")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s + "\n"
|
||||
|
||||
end
|
||||
|
||||
def cmd_nessus_plugin_prefs(*args)
|
||||
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nessus_plugin_prefs")
|
||||
print_status(" Example:> nessus_plugin_prefs")
|
||||
print_status()
|
||||
print_status("Returns a long list of plugin prefs.")
|
||||
return
|
||||
end
|
||||
|
||||
if ! nessus_verify_token
|
||||
return
|
||||
end
|
||||
|
||||
if ! @n.is_admin
|
||||
print_error("Your Nessus user is not an admin")
|
||||
return
|
||||
end
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [
|
||||
'Name',
|
||||
'Value',
|
||||
'Type'
|
||||
])
|
||||
prefs = @n.plugin_prefs
|
||||
prefs.each {|pref|
|
||||
tbl << [ pref['prefname'], pref['prefvalues'], pref['preftype'] ]
|
||||
}
|
||||
print_good("Nessus Plugins Pref List")
|
||||
print_good "\n"
|
||||
print_line tbl.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
add_console_dispatcher(ConsoleCommandDispatcher)
|
||||
@nbver = "1.1" # Nessus Plugin Version. Increments each time we commit to msf
|
||||
@xindex = "#{Msf::Config.get_config_root}/nessus_index" # location of the exploit index file used to speed up searching for valid exploits.
|
||||
@nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" #location of the nessus.yml containing saved nessus creds
|
||||
print_status("Nessus Bridge for Metasploit #{@nbver}")
|
||||
print_good("Type %bldnessus_help%clr for a command listing")
|
||||
#nessus_index
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('Nessus')
|
||||
end
|
||||
|
||||
def name
|
||||
"nessus"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Nessus Bridge for Metasploit #{@nbver}"
|
||||
end
|
||||
protected
|
||||
end
|
||||
end
|
||||
|
||||
+654
-654
@@ -10,661 +10,661 @@
|
||||
require 'rapid7/nexpose'
|
||||
|
||||
module Msf
|
||||
Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds
|
||||
Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds
|
||||
|
||||
class Plugin::Nexpose < Msf::Plugin
|
||||
class NexposeCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"Nexpose"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
'nexpose_connect' => "Connect to a running Nexpose instance ( user:pass@host[:port] )",
|
||||
'nexpose_save' => "Save credentials to a Nexpose instance",
|
||||
'nexpose_activity' => "Display any active scan jobs on the Nexpose instance",
|
||||
|
||||
'nexpose_scan' => "Launch a Nexpose scan against a specific IP range and import the results",
|
||||
'nexpose_discover' => "Launch a scan but only perform host and minimal service discovery",
|
||||
'nexpose_exhaustive' => "Launch a scan covering all TCP ports and all authorized safe checks",
|
||||
'nexpose_dos' => "Launch a scan that includes checks that can crash services and devices (caution)",
|
||||
|
||||
'nexpose_disconnect' => "Disconnect from an active Nexpose instance",
|
||||
|
||||
'nexpose_sites' => "List all defined sites",
|
||||
'nexpose_site_devices' => "List all discovered devices within a site",
|
||||
'nexpose_site_import' => "Import data from the specified site ID",
|
||||
'nexpose_report_templates' => "List all available report templates",
|
||||
'nexpose_command' => "Execute a console command on the Nexpose instance",
|
||||
'nexpose_sysinfo' => "Display detailed system information about the Nexpose instance",
|
||||
|
||||
# TODO:
|
||||
# nexpose_stop_scan
|
||||
}
|
||||
end
|
||||
|
||||
def nexpose_verify_db
|
||||
if ! (framework.db and framework.db.usable and framework.db.active)
|
||||
print_error("No database has been configured, please use db_create/db_connect first")
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def nexpose_verify
|
||||
return false if not nexpose_verify_db
|
||||
|
||||
if ! @nsc
|
||||
print_error("No active Nexpose instance has been configured, please use 'nexpose_connect'")
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def cmd_nexpose_save(*args)
|
||||
#if we are logged in, save session details to nexpose.yaml
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_save")
|
||||
return
|
||||
end
|
||||
|
||||
if args[0]
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_save")
|
||||
return
|
||||
end
|
||||
|
||||
group = "default"
|
||||
|
||||
if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}}
|
||||
::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) }
|
||||
print_good("#{Nexpose_yaml} created.")
|
||||
else
|
||||
print_error("Missing username/password/server/port - relogin and then try again.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_connect(*args)
|
||||
return if not nexpose_verify_db
|
||||
|
||||
if ! args[0]
|
||||
if ::File.readable?("#{Nexpose_yaml}")
|
||||
lconfig = YAML.load_file("#{Nexpose_yaml}")
|
||||
@user = lconfig['default']['username']
|
||||
@pass = lconfig['default']['password']
|
||||
@host = lconfig['default']['server']
|
||||
@port = lconfig['default']['port']
|
||||
@sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning...
|
||||
nexpose_login
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if(args.length == 0 or args[0].empty? or args[0] == "-h")
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
return
|
||||
end
|
||||
|
||||
@user = @pass = @host = @port = @sslv = nil
|
||||
|
||||
case args.length
|
||||
when 1,2
|
||||
cred,targ = args[0].split('@', 2)
|
||||
@user,@pass = cred.split(':', 2)
|
||||
targ ||= '127.0.0.1:3780'
|
||||
@host,@port = targ.split(':', 2)
|
||||
port ||= '3780'
|
||||
@sslv = args[1]
|
||||
when 4,5
|
||||
@user,@pass,@host,@port,@sslv = args
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
return
|
||||
end
|
||||
nexpose_login
|
||||
end
|
||||
|
||||
def nexpose_login
|
||||
|
||||
if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
return
|
||||
end
|
||||
|
||||
if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok")
|
||||
print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker")
|
||||
print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose")
|
||||
print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
|
||||
print_error(" as an additional parameter to this command.")
|
||||
return
|
||||
end
|
||||
|
||||
# Wrap this so a duplicate session doesnt prevent a new login
|
||||
begin
|
||||
cmd_nexpose_disconnect
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
begin
|
||||
print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...")
|
||||
nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port)
|
||||
nsc.login
|
||||
rescue ::Nexpose::APIError => e
|
||||
print_error("Connection failed: #{e.reason}")
|
||||
return
|
||||
end
|
||||
|
||||
@nsc = nsc
|
||||
nexpose_compatibility_check
|
||||
nsc
|
||||
end
|
||||
|
||||
def cmd_nexpose_activity(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
scans = @nsc.scan_activity || []
|
||||
case scans.length
|
||||
when 0
|
||||
print_status("There are currently no active scan jobs on this Nexpose instance")
|
||||
when 1
|
||||
print_status("There is 1 active scan job on this Nexpose instance")
|
||||
else
|
||||
print_status("There are currently #{scans.length} active scan jobs on this Nexpose instance")
|
||||
end
|
||||
|
||||
scans.each do |scan|
|
||||
print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_sites(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
sites = @nsc.site_listing || []
|
||||
case sites.length
|
||||
when 0
|
||||
print_status("There are currently no active sites on this Nexpose instance")
|
||||
end
|
||||
|
||||
sites.each do |site|
|
||||
print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_site_devices(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
site_id = args.shift
|
||||
if not site_id
|
||||
print_error("No site ID was specified")
|
||||
return
|
||||
end
|
||||
|
||||
devices = @nsc.site_device_listing(site_id) || []
|
||||
case devices.length
|
||||
when 0
|
||||
print_status("There are currently no devices within this site")
|
||||
end
|
||||
|
||||
devices.each do |device|
|
||||
print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_report_templates(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
res = @nsc.report_template_listing || []
|
||||
|
||||
res.each do |report|
|
||||
print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_command(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
if args.length == 0
|
||||
print_error("No command was specified")
|
||||
return
|
||||
end
|
||||
|
||||
res = @nsc.console_command(args.join(" ")) || ""
|
||||
|
||||
print_status("Command Output")
|
||||
print_line(res)
|
||||
print_line("")
|
||||
|
||||
end
|
||||
|
||||
def cmd_nexpose_sysinfo(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
res = @nsc.system_information
|
||||
|
||||
print_status("System Information")
|
||||
res.each_pair do |k,v|
|
||||
print_status(" #{k}: #{v}")
|
||||
end
|
||||
end
|
||||
|
||||
def nexpose_compatibility_check
|
||||
res = @nsc.console_command("ver")
|
||||
if res !~ /^(NSC|Console) Version ID:\s*4[89]0\s*$/m
|
||||
print_error("")
|
||||
print_error("Warning: This version of Nexpose has not been tested with Metasploit!")
|
||||
print_error("")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_site_import(*args)
|
||||
site_id = args.shift
|
||||
if not site_id
|
||||
print_error("No site ID was specified")
|
||||
return
|
||||
end
|
||||
|
||||
msfid = Time.now.to_i
|
||||
|
||||
report_formats = ["raw-xml-v2", "ns-xml"]
|
||||
report_format = report_formats.shift
|
||||
|
||||
report = Nexpose::ReportConfig.new(@nsc)
|
||||
report.set_name("Metasploit Export #{msfid}")
|
||||
report.set_template_id("pentest-audit")
|
||||
|
||||
report.addFilter("SiteFilter", site_id)
|
||||
report.set_generate_after_scan(0)
|
||||
report.set_storeOnServer(1)
|
||||
|
||||
begin
|
||||
report.set_format(report_format)
|
||||
report.saveReport()
|
||||
rescue ::Exception => e
|
||||
report_format = report_formats.shift
|
||||
if report_format
|
||||
retry
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
print_status("Generating the export data file...")
|
||||
url = nil
|
||||
while(! url)
|
||||
url = @nsc.report_last(report.config_id)
|
||||
select(nil, nil, nil, 1.0)
|
||||
end
|
||||
|
||||
print_status("Downloading the export data...")
|
||||
data = @nsc.download(url)
|
||||
|
||||
# Delete the temporary report ID
|
||||
@nsc.report_config_delete(report.config_id)
|
||||
|
||||
print_status("Importing Nexpose data...")
|
||||
process_nexpose_data(report_format, data)
|
||||
|
||||
end
|
||||
|
||||
def cmd_nexpose_discover(*args)
|
||||
args << "-h" if args.length == 0
|
||||
args << "-t"
|
||||
args << "aggressive-discovery"
|
||||
cmd_nexpose_scan(*args)
|
||||
end
|
||||
|
||||
def cmd_nexpose_exhaustive(*args)
|
||||
args << "-h" if args.length == 0
|
||||
args << "-t"
|
||||
args << "exhaustive-audit"
|
||||
cmd_nexpose_scan(*args)
|
||||
end
|
||||
|
||||
def cmd_nexpose_dos(*args)
|
||||
args << "-h" if args.length == 0
|
||||
args << "-t"
|
||||
args << "dos-audit"
|
||||
cmd_nexpose_scan(*args)
|
||||
end
|
||||
|
||||
def cmd_nexpose_scan(*args)
|
||||
|
||||
opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "This help menu"],
|
||||
"-t" => [ true, "The scan template to use (default:pentest-audit options:full-audit,exhaustive-audit,discovery,aggressive-discovery,dos-audit)"],
|
||||
"-c" => [ true, "Specify credentials to use against these targets (format is type:user:pass"],
|
||||
"-n" => [ true, "The maximum number of IPs to scan at a time (default is 32)"],
|
||||
"-s" => [ true, "The directory to store the raw XML files from the Nexpose instance (optional)"],
|
||||
"-P" => [ false, "Leave the scan data on the server when it completes (this counts against the maximum licensed IPs)"],
|
||||
"-v" => [ false, "Display diagnostic information about the scanning process"],
|
||||
"-d" => [ false, "Scan hosts based on the contents of the existing database"],
|
||||
"-I" => [ true, "Only scan systems with an address within the specified range"],
|
||||
"-E" => [ true, "Exclude hosts in the specified range from the scan"]
|
||||
)
|
||||
|
||||
opt_template = "pentest-audit"
|
||||
opt_maxaddrs = 32
|
||||
opt_monitor = false
|
||||
opt_verbose = false
|
||||
opt_savexml = nil
|
||||
opt_preserve = false
|
||||
opt_rescandb = false
|
||||
opt_addrinc = nil
|
||||
opt_addrexc = nil
|
||||
opt_scanned = []
|
||||
opt_credentials = []
|
||||
|
||||
opt_ranges = []
|
||||
|
||||
|
||||
opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print_line("Usage: nexpose_scan [options] <Target IP Ranges>")
|
||||
print_line(opts.usage)
|
||||
return
|
||||
when "-t"
|
||||
opt_template = val
|
||||
when "-n"
|
||||
opt_maxaddrs = val.to_i
|
||||
when "-s"
|
||||
opt_savexml = val
|
||||
when "-c"
|
||||
if (val =~ /^([^:]+):([^:]+):(.+)/)
|
||||
type, user, pass = [ $1, $2, $3 ]
|
||||
newcreds = Nexpose::AdminCredentials.new
|
||||
newcreds.setCredentials(type, nil, nil, user, pass, nil)
|
||||
opt_credentials << newcreds
|
||||
else
|
||||
print_error("Unrecognized Nexpose scan credentials: #{val}")
|
||||
return
|
||||
end
|
||||
when "-v"
|
||||
opt_verbose = true
|
||||
when "-P"
|
||||
opt_preserve = true
|
||||
when "-d"
|
||||
opt_rescandb = true
|
||||
when '-I'
|
||||
opt_addrinc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val)
|
||||
when '-E'
|
||||
opt_addrexc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val)
|
||||
else
|
||||
opt_ranges << val
|
||||
end
|
||||
end
|
||||
|
||||
return if not nexpose_verify
|
||||
|
||||
# Include all database hosts as scan targets if specified
|
||||
if(opt_rescandb)
|
||||
print_status("Loading scan targets from the active database...") if opt_verbose
|
||||
framework.db.hosts.each do |host|
|
||||
next if host.state != ::Msf::HostState::Alive
|
||||
opt_ranges << host.address
|
||||
end
|
||||
end
|
||||
|
||||
possible_files = opt_ranges # don't allow DOS by circular reference
|
||||
possible_files.each do |file|
|
||||
if ::File.readable? file
|
||||
print_status "Parsing ranges from #{file}"
|
||||
range_list = ::File.open(file,"rb") {|f| f.read f.stat.size}
|
||||
range_list.each_line { |subrange| opt_ranges << subrange}
|
||||
opt_ranges.delete(file)
|
||||
end
|
||||
end
|
||||
|
||||
opt_ranges = opt_ranges.join(' ')
|
||||
|
||||
if(opt_ranges.strip.empty?)
|
||||
print_line("Usage: nexpose_scan [options] <Target IP Ranges>")
|
||||
print_line(opts.usage)
|
||||
return
|
||||
end
|
||||
|
||||
if(opt_verbose)
|
||||
print_status("Creating a new scan using template #{opt_template} and #{opt_maxaddrs} concurrent IPs against #{opt_ranges}")
|
||||
end
|
||||
|
||||
range_inp = ::Msf::OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(opt_ranges)
|
||||
range = ::Rex::Socket::RangeWalker.new(range_inp)
|
||||
include_range = opt_addrinc ? ::Rex::Socket::RangeWalker.new(opt_addrinc) : nil
|
||||
exclude_range = opt_addrexc ? ::Rex::Socket::RangeWalker.new(opt_addrexc) : nil
|
||||
|
||||
completed = 0
|
||||
total = range.num_ips
|
||||
count = 0
|
||||
|
||||
print_status("Scanning #{total} addresses with template #{opt_template} in sets of #{opt_maxaddrs}")
|
||||
|
||||
while(completed < total)
|
||||
count += 1
|
||||
queue = []
|
||||
|
||||
while(ip = range.next_ip and queue.length < opt_maxaddrs)
|
||||
|
||||
if(exclude_range and exclude_range.include?(ip))
|
||||
print_status(" >> Skipping host #{ip} due to exclusion") if opt_verbose
|
||||
next
|
||||
end
|
||||
|
||||
if(include_range and ! include_range.include?(ip))
|
||||
print_status(" >> Skipping host #{ip} due to inclusion filter") if opt_verbose
|
||||
next
|
||||
end
|
||||
|
||||
opt_scanned << ip
|
||||
queue << ip
|
||||
end
|
||||
|
||||
break if queue.empty?
|
||||
print_status("Scanning #{queue[0]}-#{queue[-1]}...") if opt_verbose
|
||||
|
||||
msfid = Time.now.to_i
|
||||
|
||||
# Create a temporary site
|
||||
site = Nexpose::Site.new(@nsc)
|
||||
site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework")
|
||||
queue.each do |ip|
|
||||
site.site_config.addHost(Nexpose::IPRange.new(ip))
|
||||
end
|
||||
site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template))
|
||||
opt_credentials.each do |c|
|
||||
site.site_config.addCredentials(c)
|
||||
end
|
||||
site.saveSite()
|
||||
|
||||
print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose
|
||||
|
||||
report_formats = ["raw-xml-v2", "ns-xml"]
|
||||
report_format = report_formats.shift
|
||||
|
||||
report = Nexpose::ReportConfig.new(@nsc)
|
||||
report.set_name("Metasploit Export #{msfid}")
|
||||
report.set_template_id(opt_template)
|
||||
|
||||
report.addFilter("SiteFilter", site.site_id)
|
||||
report.set_generate_after_scan(1)
|
||||
report.set_storeOnServer(1)
|
||||
|
||||
begin
|
||||
report.set_format(report_format)
|
||||
report.saveReport()
|
||||
rescue ::Exception => e
|
||||
report_format = report_formats.shift
|
||||
if report_format
|
||||
retry
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose
|
||||
|
||||
# Run the scan
|
||||
res = site.scanSite()
|
||||
sid = res[:scan_id]
|
||||
|
||||
print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose
|
||||
|
||||
rep = true
|
||||
begin
|
||||
prev = nil
|
||||
while(true)
|
||||
info = @nsc.scan_statistics(sid)
|
||||
break if info[:summary]['status'] != "running"
|
||||
stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive"
|
||||
if(stat != prev)
|
||||
print_status(" >> #{stat}") if opt_verbose
|
||||
end
|
||||
prev = stat
|
||||
select(nil, nil, nil, 5.0)
|
||||
end
|
||||
print_status(" >> Scan has been completed with ID ##{sid}") if opt_verbose
|
||||
rescue ::Interrupt
|
||||
rep = false
|
||||
print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose
|
||||
@nsc.scan_stop(sid)
|
||||
break
|
||||
end
|
||||
|
||||
# Wait for the automatic report generation to complete
|
||||
if(rep)
|
||||
print_status(" >> Waiting on the report to generate...") if opt_verbose
|
||||
url = nil
|
||||
while(! url)
|
||||
url = @nsc.report_last(report.config_id)
|
||||
select(nil, nil, nil, 1.0)
|
||||
end
|
||||
|
||||
print_status(" >> Downloading the report data from Nexpose...") if opt_verbose
|
||||
data = @nsc.download(url)
|
||||
|
||||
if(opt_savexml)
|
||||
::FileUtils.mkdir_p(opt_savexml)
|
||||
path = ::File.join(opt_savexml, "nexpose-#{msfid}-#{count}.xml")
|
||||
print_status(" >> Saving scan data into #{path}") if opt_verbose
|
||||
::File.open(path, "wb") { |fd| fd.write(data) }
|
||||
end
|
||||
|
||||
process_nexpose_data(report_format, data)
|
||||
end
|
||||
|
||||
if ! opt_preserve
|
||||
print_status(" >> Deleting the temporary site and report...") if opt_verbose
|
||||
@nsc.site_delete(site.site_id)
|
||||
end
|
||||
end
|
||||
|
||||
print_status("Completed the scan of #{total} addresses")
|
||||
end
|
||||
|
||||
def cmd_nexpose_disconnect(*args)
|
||||
@nsc.logout if @nsc
|
||||
@nsc = nil
|
||||
end
|
||||
|
||||
def process_nexpose_data(fmt, data)
|
||||
case fmt
|
||||
when 'raw-xml-v2'
|
||||
framework.db.import({:data => data})
|
||||
when 'ns-xml'
|
||||
framework.db.import({:data => data})
|
||||
else
|
||||
print_error("Unsupported Nexpose data format: #{fmt}")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Nexpose vuln lookup
|
||||
#
|
||||
def nexpose_vuln_lookup(doc, vid, refs, host, serv=nil)
|
||||
doc.elements.each("/NexposeReport/VulnerabilityDefinitions/vulnerability[@id = '#{vid}']]") do |vulndef|
|
||||
|
||||
title = vulndef.attributes['title']
|
||||
pciSeverity = vulndef.attributes['pciSeverity']
|
||||
cvss_score = vulndef.attributes['cvssScore']
|
||||
cvss_vector = vulndef.attributes['cvssVector']
|
||||
|
||||
vulndef.elements['references'].elements.each('reference') do |ref|
|
||||
if ref.attributes['source'] == 'BID'
|
||||
refs[ 'BID-' + ref.text ] = true
|
||||
elsif ref.attributes['source'] == 'CVE'
|
||||
# ref.text is CVE-$ID
|
||||
refs[ ref.text ] = true
|
||||
elsif ref.attributes['source'] == 'MS'
|
||||
refs[ 'MSB-MS-' + ref.text ] = true
|
||||
end
|
||||
end
|
||||
|
||||
refs[ 'NEXPOSE-' + vid.downcase ] = true
|
||||
|
||||
vuln = framework.db.find_or_create_vuln(
|
||||
:host => host,
|
||||
:service => serv,
|
||||
:name => 'NEXPOSE-' + vid.downcase,
|
||||
:data => title)
|
||||
|
||||
rids = []
|
||||
refs.keys.each do |r|
|
||||
rids << framework.db.find_or_create_ref(:name => r)
|
||||
end
|
||||
|
||||
vuln.refs << (rids - vuln.refs)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Plugin initialization
|
||||
#
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
add_console_dispatcher(NexposeCommandDispatcher)
|
||||
banner = ["0a205f5f5f5f202020202020202020202020205f20202020205f205f5f5f5f5f2020205f2020205f20202020205f5f20205f5f2020202020202020202020202020202020202020200a7c20205f205c205f5f205f205f205f5f20285f29205f5f7c207c5f5f5f20207c207c205c207c207c205f5f5f5c205c2f202f5f205f5f2020205f5f5f20205f5f5f20205f5f5f200a7c207c5f29202f205f60207c20275f205c7c207c2f205f60207c20202f202f20207c20205c7c207c2f205f205c5c20202f7c20275f205c202f205f205c2f205f5f7c2f205f205c0a7c20205f203c20285f7c207c207c5f29207c207c20285f7c207c202f202f2020207c207c5c20207c20205f5f2f2f20205c7c207c5f29207c20285f29205c5f5f205c20205f5f2f0a7c5f7c205c5f5c5f5f2c5f7c202e5f5f2f7c5f7c5c5f5f2c5f7c2f5f2f202020207c5f7c205c5f7c5c5f5f5f2f5f2f5c5f5c202e5f5f2f205c5f5f5f2f7c5f5f5f2f5c5f5f5f7c0a20202020202020202020207c5f7c20202020202020202020202020202020202020202020202020202020202020202020207c5f7c202020202020202020202020202020202020200a0a0a"].pack("H*")
|
||||
|
||||
# Do not use this UTF-8 encoded high-ascii art for non-UTF-8 or windows consoles
|
||||
lang = Rex::Compat.getenv("LANG")
|
||||
if (lang and lang =~ /UTF-8/)
|
||||
# Cygwin/Windows should not be reporting UTF-8 either...
|
||||
# (! (Rex::Compat.is_windows or Rex::Compat.is_cygwin))
|
||||
banner = ["202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29684e29684e29684202020e29684e29684202020202020202020202020e29684e29684e296842020e29684e29684e2968420202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29688202020e29688e2968820202020202020202020202020e29688e2968820e29684e29688e296882020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29680e296882020e29688e29688202020e29684e29688e29688e29688e29688e296842020202020e29688e29688e29688e2968820202020e29688e29688e29684e29688e29688e29688e2968420202020e29684e29688e29688e29688e29688e29684202020e29684e29684e29688e29688e29688e29688e29688e29684202020e29684e29688e29688e29688e29688e2968420200a20e29688e2968820e29688e2968820e29688e296882020e29688e29688e29684e29684e29684e29684e29688e296882020202020e29688e296882020202020e29688e29688e296802020e29680e29688e296882020e29688e29688e296802020e29680e29688e296882020e29688e29688e29684e29684e29684e2968420e296802020e29688e29688e29684e29684e29684e29684e29688e29688200a20e29688e296882020e29688e29684e29688e296882020e29688e29688e29680e29680e29680e29680e29680e2968020202020e29688e29688e29688e2968820202020e29688e2968820202020e29688e296882020e29688e2968820202020e29688e29688202020e29680e29680e29680e29680e29688e29688e296842020e29688e29688e29680e29680e29680e29680e29680e29680200a20e29688e29688202020e29688e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688202020e29688e296882020e29688e29688202020e29688e29688e29688e29684e29684e29688e29688e296802020e29680e29688e29688e29684e29684e29688e29688e296802020e29688e29684e29684e29684e29684e29684e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688200a20e29680e29680202020e29680e29680e2968020202020e29680e29680e29680e29680e29680202020e29680e29680e296802020e29680e29680e296802020e29688e2968820e29680e29680e29680202020202020e29680e29680e29680e296802020202020e29680e29680e29680e29680e29680e296802020202020e29680e29680e29680e29680e2968020200a20202020202020202020202020202020202020202020202020202020202020e29688e29688202020202020202020202020202020202020202020202020202020202020202020202020200a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a"].pack("H*")
|
||||
end
|
||||
print(banner)
|
||||
print_status("Nexpose integration has been activated")
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('Nexpose')
|
||||
end
|
||||
|
||||
def name
|
||||
"nexpose"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Integrates with the Rapid7 Nexpose vulnerability management product"
|
||||
end
|
||||
class NexposeCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"Nexpose"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
'nexpose_connect' => "Connect to a running Nexpose instance ( user:pass@host[:port] )",
|
||||
'nexpose_save' => "Save credentials to a Nexpose instance",
|
||||
'nexpose_activity' => "Display any active scan jobs on the Nexpose instance",
|
||||
|
||||
'nexpose_scan' => "Launch a Nexpose scan against a specific IP range and import the results",
|
||||
'nexpose_discover' => "Launch a scan but only perform host and minimal service discovery",
|
||||
'nexpose_exhaustive' => "Launch a scan covering all TCP ports and all authorized safe checks",
|
||||
'nexpose_dos' => "Launch a scan that includes checks that can crash services and devices (caution)",
|
||||
|
||||
'nexpose_disconnect' => "Disconnect from an active Nexpose instance",
|
||||
|
||||
'nexpose_sites' => "List all defined sites",
|
||||
'nexpose_site_devices' => "List all discovered devices within a site",
|
||||
'nexpose_site_import' => "Import data from the specified site ID",
|
||||
'nexpose_report_templates' => "List all available report templates",
|
||||
'nexpose_command' => "Execute a console command on the Nexpose instance",
|
||||
'nexpose_sysinfo' => "Display detailed system information about the Nexpose instance",
|
||||
|
||||
# TODO:
|
||||
# nexpose_stop_scan
|
||||
}
|
||||
end
|
||||
|
||||
def nexpose_verify_db
|
||||
if ! (framework.db and framework.db.usable and framework.db.active)
|
||||
print_error("No database has been configured, please use db_create/db_connect first")
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def nexpose_verify
|
||||
return false if not nexpose_verify_db
|
||||
|
||||
if ! @nsc
|
||||
print_error("No active Nexpose instance has been configured, please use 'nexpose_connect'")
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def cmd_nexpose_save(*args)
|
||||
#if we are logged in, save session details to nexpose.yaml
|
||||
if args[0] == "-h"
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_save")
|
||||
return
|
||||
end
|
||||
|
||||
if args[0]
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_save")
|
||||
return
|
||||
end
|
||||
|
||||
group = "default"
|
||||
|
||||
if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}}
|
||||
::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) }
|
||||
print_good("#{Nexpose_yaml} created.")
|
||||
else
|
||||
print_error("Missing username/password/server/port - relogin and then try again.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_connect(*args)
|
||||
return if not nexpose_verify_db
|
||||
|
||||
if ! args[0]
|
||||
if ::File.readable?("#{Nexpose_yaml}")
|
||||
lconfig = YAML.load_file("#{Nexpose_yaml}")
|
||||
@user = lconfig['default']['username']
|
||||
@pass = lconfig['default']['password']
|
||||
@host = lconfig['default']['server']
|
||||
@port = lconfig['default']['port']
|
||||
@sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning...
|
||||
nexpose_login
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if(args.length == 0 or args[0].empty? or args[0] == "-h")
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
return
|
||||
end
|
||||
|
||||
@user = @pass = @host = @port = @sslv = nil
|
||||
|
||||
case args.length
|
||||
when 1,2
|
||||
cred,targ = args[0].split('@', 2)
|
||||
@user,@pass = cred.split(':', 2)
|
||||
targ ||= '127.0.0.1:3780'
|
||||
@host,@port = targ.split(':', 2)
|
||||
port ||= '3780'
|
||||
@sslv = args[1]
|
||||
when 4,5
|
||||
@user,@pass,@host,@port,@sslv = args
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
return
|
||||
end
|
||||
nexpose_login
|
||||
end
|
||||
|
||||
def nexpose_login
|
||||
|
||||
if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0))
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
return
|
||||
end
|
||||
|
||||
if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok")
|
||||
print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker")
|
||||
print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose")
|
||||
print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
|
||||
print_error(" as an additional parameter to this command.")
|
||||
return
|
||||
end
|
||||
|
||||
# Wrap this so a duplicate session doesnt prevent a new login
|
||||
begin
|
||||
cmd_nexpose_disconnect
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
begin
|
||||
print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...")
|
||||
nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port)
|
||||
nsc.login
|
||||
rescue ::Nexpose::APIError => e
|
||||
print_error("Connection failed: #{e.reason}")
|
||||
return
|
||||
end
|
||||
|
||||
@nsc = nsc
|
||||
nexpose_compatibility_check
|
||||
nsc
|
||||
end
|
||||
|
||||
def cmd_nexpose_activity(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
scans = @nsc.scan_activity || []
|
||||
case scans.length
|
||||
when 0
|
||||
print_status("There are currently no active scan jobs on this Nexpose instance")
|
||||
when 1
|
||||
print_status("There is 1 active scan job on this Nexpose instance")
|
||||
else
|
||||
print_status("There are currently #{scans.length} active scan jobs on this Nexpose instance")
|
||||
end
|
||||
|
||||
scans.each do |scan|
|
||||
print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_sites(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
sites = @nsc.site_listing || []
|
||||
case sites.length
|
||||
when 0
|
||||
print_status("There are currently no active sites on this Nexpose instance")
|
||||
end
|
||||
|
||||
sites.each do |site|
|
||||
print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_site_devices(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
site_id = args.shift
|
||||
if not site_id
|
||||
print_error("No site ID was specified")
|
||||
return
|
||||
end
|
||||
|
||||
devices = @nsc.site_device_listing(site_id) || []
|
||||
case devices.length
|
||||
when 0
|
||||
print_status("There are currently no devices within this site")
|
||||
end
|
||||
|
||||
devices.each do |device|
|
||||
print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_report_templates(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
res = @nsc.report_template_listing || []
|
||||
|
||||
res.each do |report|
|
||||
print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_command(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
if args.length == 0
|
||||
print_error("No command was specified")
|
||||
return
|
||||
end
|
||||
|
||||
res = @nsc.console_command(args.join(" ")) || ""
|
||||
|
||||
print_status("Command Output")
|
||||
print_line(res)
|
||||
print_line("")
|
||||
|
||||
end
|
||||
|
||||
def cmd_nexpose_sysinfo(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
res = @nsc.system_information
|
||||
|
||||
print_status("System Information")
|
||||
res.each_pair do |k,v|
|
||||
print_status(" #{k}: #{v}")
|
||||
end
|
||||
end
|
||||
|
||||
def nexpose_compatibility_check
|
||||
res = @nsc.console_command("ver")
|
||||
if res !~ /^(NSC|Console) Version ID:\s*4[89]0\s*$/m
|
||||
print_error("")
|
||||
print_error("Warning: This version of Nexpose has not been tested with Metasploit!")
|
||||
print_error("")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_site_import(*args)
|
||||
site_id = args.shift
|
||||
if not site_id
|
||||
print_error("No site ID was specified")
|
||||
return
|
||||
end
|
||||
|
||||
msfid = Time.now.to_i
|
||||
|
||||
report_formats = ["raw-xml-v2", "ns-xml"]
|
||||
report_format = report_formats.shift
|
||||
|
||||
report = Nexpose::ReportConfig.new(@nsc)
|
||||
report.set_name("Metasploit Export #{msfid}")
|
||||
report.set_template_id("pentest-audit")
|
||||
|
||||
report.addFilter("SiteFilter", site_id)
|
||||
report.set_generate_after_scan(0)
|
||||
report.set_storeOnServer(1)
|
||||
|
||||
begin
|
||||
report.set_format(report_format)
|
||||
report.saveReport()
|
||||
rescue ::Exception => e
|
||||
report_format = report_formats.shift
|
||||
if report_format
|
||||
retry
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
print_status("Generating the export data file...")
|
||||
url = nil
|
||||
while(! url)
|
||||
url = @nsc.report_last(report.config_id)
|
||||
select(nil, nil, nil, 1.0)
|
||||
end
|
||||
|
||||
print_status("Downloading the export data...")
|
||||
data = @nsc.download(url)
|
||||
|
||||
# Delete the temporary report ID
|
||||
@nsc.report_config_delete(report.config_id)
|
||||
|
||||
print_status("Importing Nexpose data...")
|
||||
process_nexpose_data(report_format, data)
|
||||
|
||||
end
|
||||
|
||||
def cmd_nexpose_discover(*args)
|
||||
args << "-h" if args.length == 0
|
||||
args << "-t"
|
||||
args << "aggressive-discovery"
|
||||
cmd_nexpose_scan(*args)
|
||||
end
|
||||
|
||||
def cmd_nexpose_exhaustive(*args)
|
||||
args << "-h" if args.length == 0
|
||||
args << "-t"
|
||||
args << "exhaustive-audit"
|
||||
cmd_nexpose_scan(*args)
|
||||
end
|
||||
|
||||
def cmd_nexpose_dos(*args)
|
||||
args << "-h" if args.length == 0
|
||||
args << "-t"
|
||||
args << "dos-audit"
|
||||
cmd_nexpose_scan(*args)
|
||||
end
|
||||
|
||||
def cmd_nexpose_scan(*args)
|
||||
|
||||
opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "This help menu"],
|
||||
"-t" => [ true, "The scan template to use (default:pentest-audit options:full-audit,exhaustive-audit,discovery,aggressive-discovery,dos-audit)"],
|
||||
"-c" => [ true, "Specify credentials to use against these targets (format is type:user:pass"],
|
||||
"-n" => [ true, "The maximum number of IPs to scan at a time (default is 32)"],
|
||||
"-s" => [ true, "The directory to store the raw XML files from the Nexpose instance (optional)"],
|
||||
"-P" => [ false, "Leave the scan data on the server when it completes (this counts against the maximum licensed IPs)"],
|
||||
"-v" => [ false, "Display diagnostic information about the scanning process"],
|
||||
"-d" => [ false, "Scan hosts based on the contents of the existing database"],
|
||||
"-I" => [ true, "Only scan systems with an address within the specified range"],
|
||||
"-E" => [ true, "Exclude hosts in the specified range from the scan"]
|
||||
)
|
||||
|
||||
opt_template = "pentest-audit"
|
||||
opt_maxaddrs = 32
|
||||
opt_monitor = false
|
||||
opt_verbose = false
|
||||
opt_savexml = nil
|
||||
opt_preserve = false
|
||||
opt_rescandb = false
|
||||
opt_addrinc = nil
|
||||
opt_addrexc = nil
|
||||
opt_scanned = []
|
||||
opt_credentials = []
|
||||
|
||||
opt_ranges = []
|
||||
|
||||
|
||||
opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print_line("Usage: nexpose_scan [options] <Target IP Ranges>")
|
||||
print_line(opts.usage)
|
||||
return
|
||||
when "-t"
|
||||
opt_template = val
|
||||
when "-n"
|
||||
opt_maxaddrs = val.to_i
|
||||
when "-s"
|
||||
opt_savexml = val
|
||||
when "-c"
|
||||
if (val =~ /^([^:]+):([^:]+):(.+)/)
|
||||
type, user, pass = [ $1, $2, $3 ]
|
||||
newcreds = Nexpose::AdminCredentials.new
|
||||
newcreds.setCredentials(type, nil, nil, user, pass, nil)
|
||||
opt_credentials << newcreds
|
||||
else
|
||||
print_error("Unrecognized Nexpose scan credentials: #{val}")
|
||||
return
|
||||
end
|
||||
when "-v"
|
||||
opt_verbose = true
|
||||
when "-P"
|
||||
opt_preserve = true
|
||||
when "-d"
|
||||
opt_rescandb = true
|
||||
when '-I'
|
||||
opt_addrinc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val)
|
||||
when '-E'
|
||||
opt_addrexc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val)
|
||||
else
|
||||
opt_ranges << val
|
||||
end
|
||||
end
|
||||
|
||||
return if not nexpose_verify
|
||||
|
||||
# Include all database hosts as scan targets if specified
|
||||
if(opt_rescandb)
|
||||
print_status("Loading scan targets from the active database...") if opt_verbose
|
||||
framework.db.hosts.each do |host|
|
||||
next if host.state != ::Msf::HostState::Alive
|
||||
opt_ranges << host.address
|
||||
end
|
||||
end
|
||||
|
||||
possible_files = opt_ranges # don't allow DOS by circular reference
|
||||
possible_files.each do |file|
|
||||
if ::File.readable? file
|
||||
print_status "Parsing ranges from #{file}"
|
||||
range_list = ::File.open(file,"rb") {|f| f.read f.stat.size}
|
||||
range_list.each_line { |subrange| opt_ranges << subrange}
|
||||
opt_ranges.delete(file)
|
||||
end
|
||||
end
|
||||
|
||||
opt_ranges = opt_ranges.join(' ')
|
||||
|
||||
if(opt_ranges.strip.empty?)
|
||||
print_line("Usage: nexpose_scan [options] <Target IP Ranges>")
|
||||
print_line(opts.usage)
|
||||
return
|
||||
end
|
||||
|
||||
if(opt_verbose)
|
||||
print_status("Creating a new scan using template #{opt_template} and #{opt_maxaddrs} concurrent IPs against #{opt_ranges}")
|
||||
end
|
||||
|
||||
range_inp = ::Msf::OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(opt_ranges)
|
||||
range = ::Rex::Socket::RangeWalker.new(range_inp)
|
||||
include_range = opt_addrinc ? ::Rex::Socket::RangeWalker.new(opt_addrinc) : nil
|
||||
exclude_range = opt_addrexc ? ::Rex::Socket::RangeWalker.new(opt_addrexc) : nil
|
||||
|
||||
completed = 0
|
||||
total = range.num_ips
|
||||
count = 0
|
||||
|
||||
print_status("Scanning #{total} addresses with template #{opt_template} in sets of #{opt_maxaddrs}")
|
||||
|
||||
while(completed < total)
|
||||
count += 1
|
||||
queue = []
|
||||
|
||||
while(ip = range.next_ip and queue.length < opt_maxaddrs)
|
||||
|
||||
if(exclude_range and exclude_range.include?(ip))
|
||||
print_status(" >> Skipping host #{ip} due to exclusion") if opt_verbose
|
||||
next
|
||||
end
|
||||
|
||||
if(include_range and ! include_range.include?(ip))
|
||||
print_status(" >> Skipping host #{ip} due to inclusion filter") if opt_verbose
|
||||
next
|
||||
end
|
||||
|
||||
opt_scanned << ip
|
||||
queue << ip
|
||||
end
|
||||
|
||||
break if queue.empty?
|
||||
print_status("Scanning #{queue[0]}-#{queue[-1]}...") if opt_verbose
|
||||
|
||||
msfid = Time.now.to_i
|
||||
|
||||
# Create a temporary site
|
||||
site = Nexpose::Site.new(@nsc)
|
||||
site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework")
|
||||
queue.each do |ip|
|
||||
site.site_config.addHost(Nexpose::IPRange.new(ip))
|
||||
end
|
||||
site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template))
|
||||
opt_credentials.each do |c|
|
||||
site.site_config.addCredentials(c)
|
||||
end
|
||||
site.saveSite()
|
||||
|
||||
print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose
|
||||
|
||||
report_formats = ["raw-xml-v2", "ns-xml"]
|
||||
report_format = report_formats.shift
|
||||
|
||||
report = Nexpose::ReportConfig.new(@nsc)
|
||||
report.set_name("Metasploit Export #{msfid}")
|
||||
report.set_template_id(opt_template)
|
||||
|
||||
report.addFilter("SiteFilter", site.site_id)
|
||||
report.set_generate_after_scan(1)
|
||||
report.set_storeOnServer(1)
|
||||
|
||||
begin
|
||||
report.set_format(report_format)
|
||||
report.saveReport()
|
||||
rescue ::Exception => e
|
||||
report_format = report_formats.shift
|
||||
if report_format
|
||||
retry
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose
|
||||
|
||||
# Run the scan
|
||||
res = site.scanSite()
|
||||
sid = res[:scan_id]
|
||||
|
||||
print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose
|
||||
|
||||
rep = true
|
||||
begin
|
||||
prev = nil
|
||||
while(true)
|
||||
info = @nsc.scan_statistics(sid)
|
||||
break if info[:summary]['status'] != "running"
|
||||
stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive"
|
||||
if(stat != prev)
|
||||
print_status(" >> #{stat}") if opt_verbose
|
||||
end
|
||||
prev = stat
|
||||
select(nil, nil, nil, 5.0)
|
||||
end
|
||||
print_status(" >> Scan has been completed with ID ##{sid}") if opt_verbose
|
||||
rescue ::Interrupt
|
||||
rep = false
|
||||
print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose
|
||||
@nsc.scan_stop(sid)
|
||||
break
|
||||
end
|
||||
|
||||
# Wait for the automatic report generation to complete
|
||||
if(rep)
|
||||
print_status(" >> Waiting on the report to generate...") if opt_verbose
|
||||
url = nil
|
||||
while(! url)
|
||||
url = @nsc.report_last(report.config_id)
|
||||
select(nil, nil, nil, 1.0)
|
||||
end
|
||||
|
||||
print_status(" >> Downloading the report data from Nexpose...") if opt_verbose
|
||||
data = @nsc.download(url)
|
||||
|
||||
if(opt_savexml)
|
||||
::FileUtils.mkdir_p(opt_savexml)
|
||||
path = ::File.join(opt_savexml, "nexpose-#{msfid}-#{count}.xml")
|
||||
print_status(" >> Saving scan data into #{path}") if opt_verbose
|
||||
::File.open(path, "wb") { |fd| fd.write(data) }
|
||||
end
|
||||
|
||||
process_nexpose_data(report_format, data)
|
||||
end
|
||||
|
||||
if ! opt_preserve
|
||||
print_status(" >> Deleting the temporary site and report...") if opt_verbose
|
||||
@nsc.site_delete(site.site_id)
|
||||
end
|
||||
end
|
||||
|
||||
print_status("Completed the scan of #{total} addresses")
|
||||
end
|
||||
|
||||
def cmd_nexpose_disconnect(*args)
|
||||
@nsc.logout if @nsc
|
||||
@nsc = nil
|
||||
end
|
||||
|
||||
def process_nexpose_data(fmt, data)
|
||||
case fmt
|
||||
when 'raw-xml-v2'
|
||||
framework.db.import({:data => data})
|
||||
when 'ns-xml'
|
||||
framework.db.import({:data => data})
|
||||
else
|
||||
print_error("Unsupported Nexpose data format: #{fmt}")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Nexpose vuln lookup
|
||||
#
|
||||
def nexpose_vuln_lookup(doc, vid, refs, host, serv=nil)
|
||||
doc.elements.each("/NexposeReport/VulnerabilityDefinitions/vulnerability[@id = '#{vid}']]") do |vulndef|
|
||||
|
||||
title = vulndef.attributes['title']
|
||||
pciSeverity = vulndef.attributes['pciSeverity']
|
||||
cvss_score = vulndef.attributes['cvssScore']
|
||||
cvss_vector = vulndef.attributes['cvssVector']
|
||||
|
||||
vulndef.elements['references'].elements.each('reference') do |ref|
|
||||
if ref.attributes['source'] == 'BID'
|
||||
refs[ 'BID-' + ref.text ] = true
|
||||
elsif ref.attributes['source'] == 'CVE'
|
||||
# ref.text is CVE-$ID
|
||||
refs[ ref.text ] = true
|
||||
elsif ref.attributes['source'] == 'MS'
|
||||
refs[ 'MSB-MS-' + ref.text ] = true
|
||||
end
|
||||
end
|
||||
|
||||
refs[ 'NEXPOSE-' + vid.downcase ] = true
|
||||
|
||||
vuln = framework.db.find_or_create_vuln(
|
||||
:host => host,
|
||||
:service => serv,
|
||||
:name => 'NEXPOSE-' + vid.downcase,
|
||||
:data => title)
|
||||
|
||||
rids = []
|
||||
refs.keys.each do |r|
|
||||
rids << framework.db.find_or_create_ref(:name => r)
|
||||
end
|
||||
|
||||
vuln.refs << (rids - vuln.refs)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Plugin initialization
|
||||
#
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
add_console_dispatcher(NexposeCommandDispatcher)
|
||||
banner = ["0a205f5f5f5f202020202020202020202020205f20202020205f205f5f5f5f5f2020205f2020205f20202020205f5f20205f5f2020202020202020202020202020202020202020200a7c20205f205c205f5f205f205f205f5f20285f29205f5f7c207c5f5f5f20207c207c205c207c207c205f5f5f5c205c2f202f5f205f5f2020205f5f5f20205f5f5f20205f5f5f200a7c207c5f29202f205f60207c20275f205c7c207c2f205f60207c20202f202f20207c20205c7c207c2f205f205c5c20202f7c20275f205c202f205f205c2f205f5f7c2f205f205c0a7c20205f203c20285f7c207c207c5f29207c207c20285f7c207c202f202f2020207c207c5c20207c20205f5f2f2f20205c7c207c5f29207c20285f29205c5f5f205c20205f5f2f0a7c5f7c205c5f5c5f5f2c5f7c202e5f5f2f7c5f7c5c5f5f2c5f7c2f5f2f202020207c5f7c205c5f7c5c5f5f5f2f5f2f5c5f5c202e5f5f2f205c5f5f5f2f7c5f5f5f2f5c5f5f5f7c0a20202020202020202020207c5f7c20202020202020202020202020202020202020202020202020202020202020202020207c5f7c202020202020202020202020202020202020200a0a0a"].pack("H*")
|
||||
|
||||
# Do not use this UTF-8 encoded high-ascii art for non-UTF-8 or windows consoles
|
||||
lang = Rex::Compat.getenv("LANG")
|
||||
if (lang and lang =~ /UTF-8/)
|
||||
# Cygwin/Windows should not be reporting UTF-8 either...
|
||||
# (! (Rex::Compat.is_windows or Rex::Compat.is_cygwin))
|
||||
banner = ["202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29684e29684e29684202020e29684e29684202020202020202020202020e29684e29684e296842020e29684e29684e2968420202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29688202020e29688e2968820202020202020202020202020e29688e2968820e29684e29688e296882020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29680e296882020e29688e29688202020e29684e29688e29688e29688e29688e296842020202020e29688e29688e29688e2968820202020e29688e29688e29684e29688e29688e29688e2968420202020e29684e29688e29688e29688e29688e29684202020e29684e29684e29688e29688e29688e29688e29688e29684202020e29684e29688e29688e29688e29688e2968420200a20e29688e2968820e29688e2968820e29688e296882020e29688e29688e29684e29684e29684e29684e29688e296882020202020e29688e296882020202020e29688e29688e296802020e29680e29688e296882020e29688e29688e296802020e29680e29688e296882020e29688e29688e29684e29684e29684e2968420e296802020e29688e29688e29684e29684e29684e29684e29688e29688200a20e29688e296882020e29688e29684e29688e296882020e29688e29688e29680e29680e29680e29680e29680e2968020202020e29688e29688e29688e2968820202020e29688e2968820202020e29688e296882020e29688e2968820202020e29688e29688202020e29680e29680e29680e29680e29688e29688e296842020e29688e29688e29680e29680e29680e29680e29680e29680200a20e29688e29688202020e29688e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688202020e29688e296882020e29688e29688202020e29688e29688e29688e29684e29684e29688e29688e296802020e29680e29688e29688e29684e29684e29688e29688e296802020e29688e29684e29684e29684e29684e29684e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688200a20e29680e29680202020e29680e29680e2968020202020e29680e29680e29680e29680e29680202020e29680e29680e296802020e29680e29680e296802020e29688e2968820e29680e29680e29680202020202020e29680e29680e29680e296802020202020e29680e29680e29680e29680e29680e296802020202020e29680e29680e29680e29680e2968020200a20202020202020202020202020202020202020202020202020202020202020e29688e29688202020202020202020202020202020202020202020202020202020202020202020202020200a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a"].pack("H*")
|
||||
end
|
||||
print(banner)
|
||||
print_status("Nexpose integration has been activated")
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('Nexpose')
|
||||
end
|
||||
|
||||
def name
|
||||
"nexpose"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Integrates with the Rapid7 Nexpose vulnerability management product"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+542
-542
@@ -14,555 +14,555 @@ require 'openvas/openvas-omp'
|
||||
|
||||
module Msf
|
||||
class Plugin::OpenVAS < Msf::Plugin
|
||||
class OpenVASCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"OpenVAS"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
'openvas_help' => "Displays help",
|
||||
'openvas_version' => "Display the version of the OpenVAS server",
|
||||
'openvas_debug' => "Enable/Disable debugging",
|
||||
'openvas_connect' => "Connect to an OpenVAS manager using OMP",
|
||||
'openvas_disconnect' => "Disconnect from OpenVAS manager",
|
||||
|
||||
'openvas_task_create' => "Create a task (name, comment, target, config)",
|
||||
'openvas_task_delete' => "Delete task by ID",
|
||||
'openvas_task_list' => "Display list of tasks",
|
||||
'openvas_task_start' => "Start task by ID",
|
||||
'openvas_task_stop' => "Stop task by ID",
|
||||
'openvas_task_pause' => "Pause task by ID",
|
||||
'openvas_task_resume' => "Resume task by ID",
|
||||
'openvas_task_resume_or_start' => "Resume task or start task by ID",
|
||||
|
||||
'openvas_target_create' => "Create target (name, hosts, comment)",
|
||||
'openvas_target_delete' => "Delete target by ID",
|
||||
'openvas_target_list' => "Display list of targets",
|
||||
|
||||
'openvas_config_list' => "Quickly display list of configs",
|
||||
|
||||
'openvas_format_list' => "Display list of available report formats",
|
||||
|
||||
'openvas_report_list' => "Display a list of available report formats",
|
||||
'openvas_report_delete' => "Delete a report specified by ID",
|
||||
'openvas_report_download' => "Save a report to disk",
|
||||
'openvas_report_import' => "Import report specified by ID into framework",
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_openvas_help()
|
||||
print_status("openvas_help Display this help")
|
||||
print_status("openvas_debug Enable/Disable debugging")
|
||||
print_status("openvas_version Display the version of the OpenVAS server")
|
||||
print_status
|
||||
print_status("CONNECTION")
|
||||
print_status("==========")
|
||||
print_status("openvas_connect Connects to OpenVAS")
|
||||
print_status("openvas_disconnect Disconnects from OpenVAS")
|
||||
print_status
|
||||
print_status("TARGETS")
|
||||
print_status("=======")
|
||||
print_status("openvas_target_create Create target")
|
||||
print_status("openvas_target_delete Deletes target specified by ID")
|
||||
print_status("openvas_target_list Lists targets")
|
||||
print_status
|
||||
print_status("TASKS")
|
||||
print_status("=====")
|
||||
print_status("openvas_task_create Create task")
|
||||
print_status("openvas_task_delete Delete a task and all associated reports")
|
||||
print_status("openvas_task_list Lists tasks")
|
||||
print_status("openvas_task_start Starts task specified by ID")
|
||||
print_status("openvas_task_stop Stops task specified by ID")
|
||||
print_status("openvas_task_pause Pauses task specified by ID")
|
||||
print_status("openvas_task_resume Resumes task specified by ID")
|
||||
print_status("openvas_task_resume_or_start Resumes or starts task specified by ID")
|
||||
print_status
|
||||
print_status("CONFIGS")
|
||||
print_status("=======")
|
||||
print_status("openvas_config_list Lists scan configurations")
|
||||
print_status
|
||||
print_status("FORMATS")
|
||||
print_status("=======")
|
||||
print_status("openvas_format_list Lists available report formats")
|
||||
print_status
|
||||
print_status("REPORTS")
|
||||
print_status("=======")
|
||||
print_status("openvas_report_list Lists available reports")
|
||||
print_status("openvas_report_delete Delete a report specified by ID")
|
||||
print_status("openvas_report_import Imports an OpenVAS report specified by ID")
|
||||
print_status("openvas_report_download Downloads an OpenVAS report specified by ID")
|
||||
end
|
||||
|
||||
# Verify the database is connected and usable
|
||||
def database?
|
||||
if !(framework.db and framework.db.usable)
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
# Verify there is an active OpenVAS connection
|
||||
def openvas?
|
||||
if @ov
|
||||
return true
|
||||
else
|
||||
print_error("No OpenVAS connection available. Please use openvas_connect.")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# Verify correct number of arguments and verify -h was not given. Return
|
||||
# true if correct number of arguments and help was not requested.
|
||||
def args?(args, min=1, max=nil)
|
||||
if not max then max = min end
|
||||
if (args.length < min or args.length > max or args[0] == "-h")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Basic Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_debug(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.debug(args[0].to_i)
|
||||
print_good(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage:")
|
||||
print_status("openvas_debug integer")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_version()
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
ver = @ov.get_version
|
||||
print_good("Using OMP version #{ver}")
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#--------------------------
|
||||
# Connection Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_connect(*args)
|
||||
# Is the database configured?
|
||||
if not database?
|
||||
print_error("No database has been configured.")
|
||||
return
|
||||
end
|
||||
|
||||
# Don't allow duplicate sessions
|
||||
if @ov then
|
||||
print_error("Session already open, please use openvas_disconnect first.")
|
||||
return
|
||||
end
|
||||
|
||||
# Make sure the correct number of arguments are present.
|
||||
if args?(args, 4, 5)
|
||||
|
||||
user, pass, host, port, sslv = args
|
||||
|
||||
# SSL warning. User is required to confirm.
|
||||
if(host != "localhost" and host != "127.0.0.1" and sslv != "ok")
|
||||
print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker")
|
||||
print_error(" with the ability to man-in-the-middle the OpenVAS traffic to capture the OpenVAS")
|
||||
print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
|
||||
print_error(" as an additional parameter to this command.")
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
print_status("Connecting to OpenVAS instance at #{host}:#{port} with username #{user}...")
|
||||
ov = OpenVASOMP::OpenVASOMP.new(user, pass, host, port)
|
||||
rescue OpenVASOMP::OMPAuthError => e
|
||||
print_error("Authentication failed: #{e.reason}")
|
||||
return
|
||||
rescue OpenVASOMP::OMPConnectionError => e
|
||||
print_error("Connection failed: #{e.reason}")
|
||||
return
|
||||
end
|
||||
print_good("OpenVAS connection successful")
|
||||
@ov = ov
|
||||
|
||||
else
|
||||
print_status("Usage:")
|
||||
print_status("openvas_connect username password host port <ssl-confirm>")
|
||||
end
|
||||
end
|
||||
|
||||
# Disconnect from an OpenVAS manager
|
||||
def cmd_openvas_disconnect()
|
||||
return unless openvas?
|
||||
@ov.logout
|
||||
@ov = nil
|
||||
end
|
||||
|
||||
|
||||
#--------------------------
|
||||
# Target Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_target_create(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 3)
|
||||
begin
|
||||
resp = @ov.target_create(args[0], args[1], args[2])
|
||||
print_status(resp)
|
||||
cmd_openvas_target_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
|
||||
else
|
||||
print_status("Usage: openvas_target_create <name> <hosts> <comment>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_target_delete(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.target_delete(args[0])
|
||||
print_status(resp)
|
||||
cmd_openvas_target_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_target_delete <target_id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_target_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ["ID", "Name", "Hosts", "Max Hosts", "In Use", "Comment"])
|
||||
id = 0
|
||||
@ov.target_get_all().each do |target|
|
||||
tbl << [ id, target["name"], target["hosts"], target["max_hosts"],
|
||||
target["in_use"], target["comment"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of targets")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Task Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_task_create(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 4)
|
||||
begin
|
||||
resp = @ov.task_create(args[0], args[1], args[2], args[3])
|
||||
print_status(resp)
|
||||
cmd_openvas_task_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
|
||||
else
|
||||
print_status("Usage: openvas_task_create <name> <comment> <config_id> <target_id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_delete(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 2)
|
||||
|
||||
# User is required to confirm before deleting task.
|
||||
if(args[1] != "ok")
|
||||
print_error("Warning: Deleting a task will also delete all reports associated with the ")
|
||||
print_error("task, please pass in 'ok' as an additional parameter to this command.")
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
resp = @ov.task_delete(args[0])
|
||||
print_status(resp)
|
||||
cmd_openvas_task_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_delete <id> ok")
|
||||
print_error("This will delete the task and all associated reports.")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ["ID", "Name", "Comment", "Status", "Progress"])
|
||||
id = 0
|
||||
@ov.task_get_all().each do |task|
|
||||
tbl << [ id, task["name"], task["comment"], task["status"], task["progress"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of tasks")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_start(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_start(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_start <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_stop(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_stop(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_stop <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_pause(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_pause(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_pause <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_resume(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_resume_paused(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_resume <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_resume_or_start(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_resume_or_start(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_resume_or_start <id>")
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Config Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_config_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [ "ID", "Name" ])
|
||||
|
||||
id = 0
|
||||
@ov.configs.each do |config|
|
||||
tbl << [ id, config["name"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of configs")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Format Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_format_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ["ID", "Name", "Extension", "Summary"])
|
||||
id = 0
|
||||
@ov.formats.each do |format|
|
||||
tbl << [ id, format["name"], format["extension"], format["summary"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of report formats")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Report Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_report_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ["ID", "Task Name", "Start Time", "Stop Time"])
|
||||
id = 0
|
||||
@ov.report_get_all().each do |report|
|
||||
tbl << [ id, report["task"], report["start_time"], report["stop_time"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of reports")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_report_delete(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.report_delete(args[0])
|
||||
print_status(resp)
|
||||
cmd_openvas_report_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_report_delete <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_report_download(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 4)
|
||||
begin
|
||||
report = @ov.report_get_by_id(args[0], args[1])
|
||||
::FileUtils.mkdir_p(args[2])
|
||||
name = ::File.join(args[2], args[3])
|
||||
print_status("Saving report to #{name}")
|
||||
output = ::File.new(name, "w")
|
||||
output.puts(report)
|
||||
output.close
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_report_download <report_id> <format_id> <path> <report_name>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_report_import(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 2)
|
||||
begin
|
||||
report = @ov.report_get_by_id(args[0], args[1])
|
||||
print_status("Importing report to database.")
|
||||
framework.db.import({:data => report})
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_report_import <report_id> <format_id>")
|
||||
print_status("Only the NBE and XML formats are supported for importing.")
|
||||
end
|
||||
end
|
||||
|
||||
end # End OpenVAS class
|
||||
class OpenVASCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"OpenVAS"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
'openvas_help' => "Displays help",
|
||||
'openvas_version' => "Display the version of the OpenVAS server",
|
||||
'openvas_debug' => "Enable/Disable debugging",
|
||||
'openvas_connect' => "Connect to an OpenVAS manager using OMP",
|
||||
'openvas_disconnect' => "Disconnect from OpenVAS manager",
|
||||
|
||||
'openvas_task_create' => "Create a task (name, comment, target, config)",
|
||||
'openvas_task_delete' => "Delete task by ID",
|
||||
'openvas_task_list' => "Display list of tasks",
|
||||
'openvas_task_start' => "Start task by ID",
|
||||
'openvas_task_stop' => "Stop task by ID",
|
||||
'openvas_task_pause' => "Pause task by ID",
|
||||
'openvas_task_resume' => "Resume task by ID",
|
||||
'openvas_task_resume_or_start' => "Resume task or start task by ID",
|
||||
|
||||
'openvas_target_create' => "Create target (name, hosts, comment)",
|
||||
'openvas_target_delete' => "Delete target by ID",
|
||||
'openvas_target_list' => "Display list of targets",
|
||||
|
||||
'openvas_config_list' => "Quickly display list of configs",
|
||||
|
||||
'openvas_format_list' => "Display list of available report formats",
|
||||
|
||||
'openvas_report_list' => "Display a list of available report formats",
|
||||
'openvas_report_delete' => "Delete a report specified by ID",
|
||||
'openvas_report_download' => "Save a report to disk",
|
||||
'openvas_report_import' => "Import report specified by ID into framework",
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_openvas_help()
|
||||
print_status("openvas_help Display this help")
|
||||
print_status("openvas_debug Enable/Disable debugging")
|
||||
print_status("openvas_version Display the version of the OpenVAS server")
|
||||
print_status
|
||||
print_status("CONNECTION")
|
||||
print_status("==========")
|
||||
print_status("openvas_connect Connects to OpenVAS")
|
||||
print_status("openvas_disconnect Disconnects from OpenVAS")
|
||||
print_status
|
||||
print_status("TARGETS")
|
||||
print_status("=======")
|
||||
print_status("openvas_target_create Create target")
|
||||
print_status("openvas_target_delete Deletes target specified by ID")
|
||||
print_status("openvas_target_list Lists targets")
|
||||
print_status
|
||||
print_status("TASKS")
|
||||
print_status("=====")
|
||||
print_status("openvas_task_create Create task")
|
||||
print_status("openvas_task_delete Delete a task and all associated reports")
|
||||
print_status("openvas_task_list Lists tasks")
|
||||
print_status("openvas_task_start Starts task specified by ID")
|
||||
print_status("openvas_task_stop Stops task specified by ID")
|
||||
print_status("openvas_task_pause Pauses task specified by ID")
|
||||
print_status("openvas_task_resume Resumes task specified by ID")
|
||||
print_status("openvas_task_resume_or_start Resumes or starts task specified by ID")
|
||||
print_status
|
||||
print_status("CONFIGS")
|
||||
print_status("=======")
|
||||
print_status("openvas_config_list Lists scan configurations")
|
||||
print_status
|
||||
print_status("FORMATS")
|
||||
print_status("=======")
|
||||
print_status("openvas_format_list Lists available report formats")
|
||||
print_status
|
||||
print_status("REPORTS")
|
||||
print_status("=======")
|
||||
print_status("openvas_report_list Lists available reports")
|
||||
print_status("openvas_report_delete Delete a report specified by ID")
|
||||
print_status("openvas_report_import Imports an OpenVAS report specified by ID")
|
||||
print_status("openvas_report_download Downloads an OpenVAS report specified by ID")
|
||||
end
|
||||
|
||||
# Verify the database is connected and usable
|
||||
def database?
|
||||
if !(framework.db and framework.db.usable)
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
# Verify there is an active OpenVAS connection
|
||||
def openvas?
|
||||
if @ov
|
||||
return true
|
||||
else
|
||||
print_error("No OpenVAS connection available. Please use openvas_connect.")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# Verify correct number of arguments and verify -h was not given. Return
|
||||
# true if correct number of arguments and help was not requested.
|
||||
def args?(args, min=1, max=nil)
|
||||
if not max then max = min end
|
||||
if (args.length < min or args.length > max or args[0] == "-h")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Basic Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_debug(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.debug(args[0].to_i)
|
||||
print_good(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage:")
|
||||
print_status("openvas_debug integer")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_version()
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
ver = @ov.get_version
|
||||
print_good("Using OMP version #{ver}")
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#--------------------------
|
||||
# Connection Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_connect(*args)
|
||||
# Is the database configured?
|
||||
if not database?
|
||||
print_error("No database has been configured.")
|
||||
return
|
||||
end
|
||||
|
||||
# Don't allow duplicate sessions
|
||||
if @ov then
|
||||
print_error("Session already open, please use openvas_disconnect first.")
|
||||
return
|
||||
end
|
||||
|
||||
# Make sure the correct number of arguments are present.
|
||||
if args?(args, 4, 5)
|
||||
|
||||
user, pass, host, port, sslv = args
|
||||
|
||||
# SSL warning. User is required to confirm.
|
||||
if(host != "localhost" and host != "127.0.0.1" and sslv != "ok")
|
||||
print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker")
|
||||
print_error(" with the ability to man-in-the-middle the OpenVAS traffic to capture the OpenVAS")
|
||||
print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
|
||||
print_error(" as an additional parameter to this command.")
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
print_status("Connecting to OpenVAS instance at #{host}:#{port} with username #{user}...")
|
||||
ov = OpenVASOMP::OpenVASOMP.new(user, pass, host, port)
|
||||
rescue OpenVASOMP::OMPAuthError => e
|
||||
print_error("Authentication failed: #{e.reason}")
|
||||
return
|
||||
rescue OpenVASOMP::OMPConnectionError => e
|
||||
print_error("Connection failed: #{e.reason}")
|
||||
return
|
||||
end
|
||||
print_good("OpenVAS connection successful")
|
||||
@ov = ov
|
||||
|
||||
else
|
||||
print_status("Usage:")
|
||||
print_status("openvas_connect username password host port <ssl-confirm>")
|
||||
end
|
||||
end
|
||||
|
||||
# Disconnect from an OpenVAS manager
|
||||
def cmd_openvas_disconnect()
|
||||
return unless openvas?
|
||||
@ov.logout
|
||||
@ov = nil
|
||||
end
|
||||
|
||||
|
||||
#--------------------------
|
||||
# Target Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_target_create(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 3)
|
||||
begin
|
||||
resp = @ov.target_create(args[0], args[1], args[2])
|
||||
print_status(resp)
|
||||
cmd_openvas_target_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
|
||||
else
|
||||
print_status("Usage: openvas_target_create <name> <hosts> <comment>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_target_delete(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.target_delete(args[0])
|
||||
print_status(resp)
|
||||
cmd_openvas_target_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_target_delete <target_id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_target_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ["ID", "Name", "Hosts", "Max Hosts", "In Use", "Comment"])
|
||||
id = 0
|
||||
@ov.target_get_all().each do |target|
|
||||
tbl << [ id, target["name"], target["hosts"], target["max_hosts"],
|
||||
target["in_use"], target["comment"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of targets")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Task Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_task_create(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 4)
|
||||
begin
|
||||
resp = @ov.task_create(args[0], args[1], args[2], args[3])
|
||||
print_status(resp)
|
||||
cmd_openvas_task_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
|
||||
else
|
||||
print_status("Usage: openvas_task_create <name> <comment> <config_id> <target_id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_delete(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 2)
|
||||
|
||||
# User is required to confirm before deleting task.
|
||||
if(args[1] != "ok")
|
||||
print_error("Warning: Deleting a task will also delete all reports associated with the ")
|
||||
print_error("task, please pass in 'ok' as an additional parameter to this command.")
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
resp = @ov.task_delete(args[0])
|
||||
print_status(resp)
|
||||
cmd_openvas_task_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_delete <id> ok")
|
||||
print_error("This will delete the task and all associated reports.")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ["ID", "Name", "Comment", "Status", "Progress"])
|
||||
id = 0
|
||||
@ov.task_get_all().each do |task|
|
||||
tbl << [ id, task["name"], task["comment"], task["status"], task["progress"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of tasks")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_start(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_start(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_start <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_stop(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_stop(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_stop <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_pause(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_pause(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_pause <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_resume(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_resume_paused(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_resume <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_task_resume_or_start(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.task_resume_or_start(args[0])
|
||||
print_status(resp)
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_task_resume_or_start <id>")
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Config Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_config_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => [ "ID", "Name" ])
|
||||
|
||||
id = 0
|
||||
@ov.configs.each do |config|
|
||||
tbl << [ id, config["name"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of configs")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Format Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_format_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ["ID", "Name", "Extension", "Summary"])
|
||||
id = 0
|
||||
@ov.formats.each do |format|
|
||||
tbl << [ id, format["name"], format["extension"], format["summary"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of report formats")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------
|
||||
# Report Functions
|
||||
#--------------------------
|
||||
def cmd_openvas_report_list(*args)
|
||||
return unless openvas?
|
||||
|
||||
begin
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ["ID", "Task Name", "Start Time", "Stop Time"])
|
||||
id = 0
|
||||
@ov.report_get_all().each do |report|
|
||||
tbl << [ id, report["task"], report["start_time"], report["stop_time"] ]
|
||||
id += 1
|
||||
end
|
||||
print_good("OpenVAS list of reports")
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_report_delete(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args)
|
||||
begin
|
||||
resp = @ov.report_delete(args[0])
|
||||
print_status(resp)
|
||||
cmd_openvas_report_list
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_report_delete <id>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_report_download(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 4)
|
||||
begin
|
||||
report = @ov.report_get_by_id(args[0], args[1])
|
||||
::FileUtils.mkdir_p(args[2])
|
||||
name = ::File.join(args[2], args[3])
|
||||
print_status("Saving report to #{name}")
|
||||
output = ::File.new(name, "w")
|
||||
output.puts(report)
|
||||
output.close
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_report_download <report_id> <format_id> <path> <report_name>")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_openvas_report_import(*args)
|
||||
return unless openvas?
|
||||
|
||||
if args?(args, 2)
|
||||
begin
|
||||
report = @ov.report_get_by_id(args[0], args[1])
|
||||
print_status("Importing report to database.")
|
||||
framework.db.import({:data => report})
|
||||
rescue OpenVASOMP::OMPError => e
|
||||
print_error(e.to_s)
|
||||
end
|
||||
else
|
||||
print_status("Usage: openvas_report_import <report_id> <format_id>")
|
||||
print_status("Only the NBE and XML formats are supported for importing.")
|
||||
end
|
||||
end
|
||||
|
||||
end # End OpenVAS class
|
||||
|
||||
#------------------------------
|
||||
# Plugin initialization
|
||||
#------------------------------
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
add_console_dispatcher(OpenVASCommandDispatcher)
|
||||
print_status("Welcome to OpenVAS integration by kost and averagesecurityguy.")
|
||||
print_status
|
||||
print_status("OpenVAS integration requires a database connection. Once the ")
|
||||
print_status("database is ready, connect to the OpenVAS server using openvas_connect.")
|
||||
print_status("For additional commands use openvas_help.")
|
||||
print_status
|
||||
@ov = nil
|
||||
@formats = nil
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
add_console_dispatcher(OpenVASCommandDispatcher)
|
||||
print_status("Welcome to OpenVAS integration by kost and averagesecurityguy.")
|
||||
print_status
|
||||
print_status("OpenVAS integration requires a database connection. Once the ")
|
||||
print_status("database is ready, connect to the OpenVAS server using openvas_connect.")
|
||||
print_status("For additional commands use openvas_help.")
|
||||
print_status
|
||||
@ov = nil
|
||||
@formats = nil
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('OpenVAS')
|
||||
end
|
||||
def cleanup
|
||||
remove_console_dispatcher('OpenVAS')
|
||||
end
|
||||
|
||||
def name
|
||||
"OpenVAS"
|
||||
end
|
||||
def name
|
||||
"OpenVAS"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Integrates with the OpenVAS - open source vulnerability management"
|
||||
end
|
||||
def desc
|
||||
"Integrates with the OpenVAS - open source vulnerability management"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+153
-153
@@ -15,188 +15,188 @@ module Msf
|
||||
|
||||
class Plugin::PcapLog < Msf::Plugin
|
||||
|
||||
# Only little-endian is supported in this implementation.
|
||||
PCAP_FILE_HEADER = "\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00"
|
||||
# Only little-endian is supported in this implementation.
|
||||
PCAP_FILE_HEADER = "\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00"
|
||||
|
||||
#
|
||||
# Implements a pcap console command dispatcher.
|
||||
#
|
||||
class PcapLogDispatcher
|
||||
#
|
||||
# Implements a pcap console command dispatcher.
|
||||
#
|
||||
class PcapLogDispatcher
|
||||
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"PcapLog"
|
||||
end
|
||||
def name
|
||||
"PcapLog"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
"pcap_filter" => "Set/Get a BPF-style packet filter",
|
||||
"pcap_dir" => "Set/Get a directory to log pcaps to",
|
||||
"pcap_prefix" => "Set/Get a filename prefix to log pcaps to",
|
||||
"pcap_iface" => "Set/Get an interface to capture from",
|
||||
"pcap_start" => "Start a capture",
|
||||
"pcap_stop" => "Stop a running capture",
|
||||
def commands
|
||||
{
|
||||
"pcap_filter" => "Set/Get a BPF-style packet filter",
|
||||
"pcap_dir" => "Set/Get a directory to log pcaps to",
|
||||
"pcap_prefix" => "Set/Get a filename prefix to log pcaps to",
|
||||
"pcap_iface" => "Set/Get an interface to capture from",
|
||||
"pcap_start" => "Start a capture",
|
||||
"pcap_stop" => "Stop a running capture",
|
||||
|
||||
"pcap_show_config" => "Show the current PcapLog configuration"
|
||||
}
|
||||
end
|
||||
"pcap_show_config" => "Show the current PcapLog configuration"
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_pcap_filter(*args)
|
||||
@filter = args.join(' ') || @filter
|
||||
print_line "#{self.name} BPF filter: #{@filter}"
|
||||
end
|
||||
def cmd_pcap_filter(*args)
|
||||
@filter = args.join(' ') || @filter
|
||||
print_line "#{self.name} BPF filter: #{@filter}"
|
||||
end
|
||||
|
||||
def cmd_pcap_prefix(*args)
|
||||
@prefix = args[0] || @prefix || "msf3-session"
|
||||
print_line "#{self.name} prefix: #{@prefix}"
|
||||
end
|
||||
def cmd_pcap_prefix(*args)
|
||||
@prefix = args[0] || @prefix || "msf3-session"
|
||||
print_line "#{self.name} prefix: #{@prefix}"
|
||||
end
|
||||
|
||||
def cmd_pcap_dir(*args)
|
||||
@dir = args[0] || @dir || "/tmp"
|
||||
print_line "#{self.name} Directory: #{@dir}"
|
||||
end
|
||||
def cmd_pcap_dir(*args)
|
||||
@dir = args[0] || @dir || "/tmp"
|
||||
print_line "#{self.name} Directory: #{@dir}"
|
||||
end
|
||||
|
||||
def cmd_pcap_iface(*args)
|
||||
@iface = args[0] || @iface
|
||||
print_line "#{self.name} Interface: #{@iface}"
|
||||
end
|
||||
def cmd_pcap_iface(*args)
|
||||
@iface = args[0] || @iface
|
||||
print_line "#{self.name} Interface: #{@iface}"
|
||||
end
|
||||
|
||||
def cmd_pcap_start(*args)
|
||||
def cmd_pcap_start(*args)
|
||||
|
||||
unless @pcaprub_loaded
|
||||
print_error("Pcap module not available")
|
||||
return false
|
||||
end
|
||||
unless @pcaprub_loaded
|
||||
print_error("Pcap module not available")
|
||||
return false
|
||||
end
|
||||
|
||||
if @capture_thread && @capture_thread.alive?
|
||||
print_error "Capture already started."
|
||||
return false
|
||||
end
|
||||
if @capture_thread && @capture_thread.alive?
|
||||
print_error "Capture already started."
|
||||
return false
|
||||
end
|
||||
|
||||
gen_fname
|
||||
print_line "Starting packet capture from #{@iface} to #{@fname}"
|
||||
okay,msg = validate_options
|
||||
unless okay
|
||||
print_error msg
|
||||
return false
|
||||
end
|
||||
dev = (@iface || ::Pcap.lookupdev)
|
||||
@capture_file.write(PCAP_FILE_HEADER)
|
||||
@capture_file.flush
|
||||
@pcap = ::Pcap.open_live(dev, 65535, true, 1)
|
||||
@pcap.setfilter(@filter) if @filter
|
||||
@capture_thread = Thread.new {
|
||||
@pcap.each do |pkt|
|
||||
@capture_file.write(convert_to_pcap(pkt))
|
||||
@capture_file.flush
|
||||
end
|
||||
}
|
||||
end
|
||||
gen_fname
|
||||
print_line "Starting packet capture from #{@iface} to #{@fname}"
|
||||
okay,msg = validate_options
|
||||
unless okay
|
||||
print_error msg
|
||||
return false
|
||||
end
|
||||
dev = (@iface || ::Pcap.lookupdev)
|
||||
@capture_file.write(PCAP_FILE_HEADER)
|
||||
@capture_file.flush
|
||||
@pcap = ::Pcap.open_live(dev, 65535, true, 1)
|
||||
@pcap.setfilter(@filter) if @filter
|
||||
@capture_thread = Thread.new {
|
||||
@pcap.each do |pkt|
|
||||
@capture_file.write(convert_to_pcap(pkt))
|
||||
@capture_file.flush
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_pcap_stop(*args)
|
||||
if @capture_thread && @capture_thread.alive?
|
||||
print_line "Stopping packet capture from #{@iface} to #{@fname}"
|
||||
print_line "Capture Stats: #{@pcap.stats.inspect}"
|
||||
@pcap = nil
|
||||
@capture_file.close if @capture_file.respond_to? :close
|
||||
@capture_thread.kill
|
||||
@capture_thread = nil
|
||||
else
|
||||
print_error "No capture running."
|
||||
end
|
||||
end
|
||||
def cmd_pcap_stop(*args)
|
||||
if @capture_thread && @capture_thread.alive?
|
||||
print_line "Stopping packet capture from #{@iface} to #{@fname}"
|
||||
print_line "Capture Stats: #{@pcap.stats.inspect}"
|
||||
@pcap = nil
|
||||
@capture_file.close if @capture_file.respond_to? :close
|
||||
@capture_thread.kill
|
||||
@capture_thread = nil
|
||||
else
|
||||
print_error "No capture running."
|
||||
end
|
||||
end
|
||||
|
||||
def convert_to_pcap(packet)
|
||||
t = Time.now
|
||||
sz = packet.size
|
||||
[t.to_i, t.usec, sz, sz, packet].pack("V4A*")
|
||||
end
|
||||
def convert_to_pcap(packet)
|
||||
t = Time.now
|
||||
sz = packet.size
|
||||
[t.to_i, t.usec, sz, sz, packet].pack("V4A*")
|
||||
end
|
||||
|
||||
def gen_fname
|
||||
t = Time.now
|
||||
file_part = "%s_%04d-%02d-%02d_%02d-%02d-%02d.pcap" % [
|
||||
@prefix, t.year, t.month, t.mday, t.hour, t.min, t.sec
|
||||
]
|
||||
@fname = File.join(@dir, file_part)
|
||||
end
|
||||
def gen_fname
|
||||
t = Time.now
|
||||
file_part = "%s_%04d-%02d-%02d_%02d-%02d-%02d.pcap" % [
|
||||
@prefix, t.year, t.month, t.mday, t.hour, t.min, t.sec
|
||||
]
|
||||
@fname = File.join(@dir, file_part)
|
||||
end
|
||||
|
||||
# Check for euid 0 and check for a valid place to write files
|
||||
def validate_options
|
||||
# Check for euid 0 and check for a valid place to write files
|
||||
def validate_options
|
||||
|
||||
# Check for root.
|
||||
unless Process.euid.zero?
|
||||
msg = "You must run as root in order to capture packets."
|
||||
return [false, msg]
|
||||
end
|
||||
# Check for root.
|
||||
unless Process.euid.zero?
|
||||
msg = "You must run as root in order to capture packets."
|
||||
return [false, msg]
|
||||
end
|
||||
|
||||
# Check directory suitability.
|
||||
unless File.directory? @dir
|
||||
msg = "Invalid pcap directory specified: '#{@dir}'"
|
||||
return [false, msg]
|
||||
end
|
||||
# Check directory suitability.
|
||||
unless File.directory? @dir
|
||||
msg = "Invalid pcap directory specified: '#{@dir}'"
|
||||
return [false, msg]
|
||||
end
|
||||
|
||||
unless File.writable? @dir
|
||||
msg = "No write permission to directory: '#{@dir}'"
|
||||
return [false, msg]
|
||||
end
|
||||
unless File.writable? @dir
|
||||
msg = "No write permission to directory: '#{@dir}'"
|
||||
return [false, msg]
|
||||
end
|
||||
|
||||
@capture_file = File.open(@fname, "ab")
|
||||
unless File.writable? @fname
|
||||
msg = "Cannot write to file: '#{@fname}'"
|
||||
return [false, msg]
|
||||
end
|
||||
@capture_file = File.open(@fname, "ab")
|
||||
unless File.writable? @fname
|
||||
msg = "Cannot write to file: '#{@fname}'"
|
||||
return [false, msg]
|
||||
end
|
||||
|
||||
# If you got this far, you're golden.
|
||||
msg = "We're good!"
|
||||
return [true, msg]
|
||||
end
|
||||
# If you got this far, you're golden.
|
||||
msg = "We're good!"
|
||||
return [true, msg]
|
||||
end
|
||||
|
||||
# Need to pretend to have a datastore for Exploit::Capture to
|
||||
# function.
|
||||
def datastore
|
||||
{}
|
||||
end
|
||||
# Need to pretend to have a datastore for Exploit::Capture to
|
||||
# function.
|
||||
def datastore
|
||||
{}
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
@dir = File.join(Msf::Config.config_directory, 'logs')
|
||||
@prefix = "msf3-session"
|
||||
@filter = nil
|
||||
@pcaprub_loaded = false
|
||||
begin
|
||||
require 'pcaprub'
|
||||
@pcaprub_loaded = true
|
||||
@iface = ::Pcap.lookupdev
|
||||
rescue ::Exception => e
|
||||
print_error "#{e.class}: #{e}"
|
||||
@pcaprub_loaded = false
|
||||
@pcaprub_error = e
|
||||
end
|
||||
end
|
||||
def initialize(*args)
|
||||
super
|
||||
@dir = File.join(Msf::Config.config_directory, 'logs')
|
||||
@prefix = "msf3-session"
|
||||
@filter = nil
|
||||
@pcaprub_loaded = false
|
||||
begin
|
||||
require 'pcaprub'
|
||||
@pcaprub_loaded = true
|
||||
@iface = ::Pcap.lookupdev
|
||||
rescue ::Exception => e
|
||||
print_error "#{e.class}: #{e}"
|
||||
@pcaprub_loaded = false
|
||||
@pcaprub_error = e
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
add_console_dispatcher(PcapLogDispatcher)
|
||||
print_status "PcapLog plugin loaded."
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
add_console_dispatcher(PcapLogDispatcher)
|
||||
print_status "PcapLog plugin loaded."
|
||||
end
|
||||
|
||||
# Kill the background thread
|
||||
def cleanup
|
||||
@capture_thread.kill if @capture_thread && @capture_thread.alive?
|
||||
@capture_file.close if @capture_file.respond_to? :close
|
||||
remove_console_dispatcher('PcapLog')
|
||||
end
|
||||
# Kill the background thread
|
||||
def cleanup
|
||||
@capture_thread.kill if @capture_thread && @capture_thread.alive?
|
||||
@capture_file.close if @capture_file.respond_to? :close
|
||||
remove_console_dispatcher('PcapLog')
|
||||
end
|
||||
|
||||
def name
|
||||
"pcap_log"
|
||||
end
|
||||
def name
|
||||
"pcap_log"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Logs all socket operations to pcaps (in /tmp by default)"
|
||||
end
|
||||
def desc
|
||||
"Logs all socket operations to pcaps (in /tmp by default)"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
+66
-66
@@ -15,81 +15,81 @@ module Msf
|
||||
###
|
||||
class Plugin::Sample < Msf::Plugin
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a sample console command dispatcher.
|
||||
#
|
||||
###
|
||||
class ConsoleCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
###
|
||||
#
|
||||
# This class implements a sample console command dispatcher.
|
||||
#
|
||||
###
|
||||
class ConsoleCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
def name
|
||||
"Sample"
|
||||
end
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
def name
|
||||
"Sample"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"sample" => "A sample command added by the sample plugin"
|
||||
}
|
||||
end
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"sample" => "A sample command added by the sample plugin"
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# This method handles the sample command.
|
||||
#
|
||||
def cmd_sample(*args)
|
||||
print_line("You passed: #{args.join(' ')}")
|
||||
end
|
||||
end
|
||||
#
|
||||
# This method handles the sample command.
|
||||
#
|
||||
def cmd_sample(*args)
|
||||
print_line("You passed: #{args.join(' ')}")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The constructor is called when an instance of the plugin is created. The
|
||||
# framework instance that the plugin is being associated with is passed in
|
||||
# the framework parameter. Plugins should call the parent constructor when
|
||||
# inheriting from Msf::Plugin to ensure that the framework attribute on
|
||||
# their instance gets set.
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
#
|
||||
# The constructor is called when an instance of the plugin is created. The
|
||||
# framework instance that the plugin is being associated with is passed in
|
||||
# the framework parameter. Plugins should call the parent constructor when
|
||||
# inheriting from Msf::Plugin to ensure that the framework attribute on
|
||||
# their instance gets set.
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
# If this plugin is being loaded in the context of a console application
|
||||
# that uses the framework's console user interface driver, register
|
||||
# console dispatcher commands.
|
||||
add_console_dispatcher(ConsoleCommandDispatcher)
|
||||
# If this plugin is being loaded in the context of a console application
|
||||
# that uses the framework's console user interface driver, register
|
||||
# console dispatcher commands.
|
||||
add_console_dispatcher(ConsoleCommandDispatcher)
|
||||
|
||||
print_status("Sample plugin loaded.")
|
||||
end
|
||||
print_status("Sample plugin loaded.")
|
||||
end
|
||||
|
||||
#
|
||||
# The cleanup routine for plugins gives them a chance to undo any actions
|
||||
# they may have done to the framework. For instance, if a console
|
||||
# dispatcher was added, then it should be removed in the cleanup routine.
|
||||
#
|
||||
def cleanup
|
||||
# If we had previously registered a console dispatcher with the console,
|
||||
# deregister it now.
|
||||
remove_console_dispatcher('Sample')
|
||||
end
|
||||
#
|
||||
# The cleanup routine for plugins gives them a chance to undo any actions
|
||||
# they may have done to the framework. For instance, if a console
|
||||
# dispatcher was added, then it should be removed in the cleanup routine.
|
||||
#
|
||||
def cleanup
|
||||
# If we had previously registered a console dispatcher with the console,
|
||||
# deregister it now.
|
||||
remove_console_dispatcher('Sample')
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a short, friendly name for the plugin.
|
||||
#
|
||||
def name
|
||||
"sample"
|
||||
end
|
||||
#
|
||||
# This method returns a short, friendly name for the plugin.
|
||||
#
|
||||
def name
|
||||
"sample"
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a brief description of the plugin. It should be no
|
||||
# more than 60 characters, but there are no hard limits.
|
||||
#
|
||||
def desc
|
||||
"Demonstrates using framework plugins"
|
||||
end
|
||||
#
|
||||
# This method returns a brief description of the plugin. It should be no
|
||||
# more than 60 characters, but there are no hard limits.
|
||||
#
|
||||
def desc
|
||||
"Demonstrates using framework plugins"
|
||||
end
|
||||
|
||||
protected
|
||||
end
|
||||
|
||||
+28
-28
@@ -15,43 +15,43 @@ module Msf
|
||||
class Plugin::SessionTagger < Msf::Plugin
|
||||
|
||||
|
||||
include Msf::SessionEvent
|
||||
include Msf::SessionEvent
|
||||
|
||||
def on_session_open(session)
|
||||
print_status("Hooked session #{session.sid} / #{session.session_host}")
|
||||
def on_session_open(session)
|
||||
print_status("Hooked session #{session.sid} / #{session.session_host}")
|
||||
|
||||
# XXX: Determine what type of session this is before writing to it
|
||||
# XXX: Determine what type of session this is before writing to it
|
||||
|
||||
if (session.interactive?)
|
||||
session.shell_write("MKDIR C:\\TaggedBy#{ENV['USER']}\n")
|
||||
session.shell_write("mkdir /tmp/TaggedBy#{ENV['USER']}\n")
|
||||
end
|
||||
if (session.interactive?)
|
||||
session.shell_write("MKDIR C:\\TaggedBy#{ENV['USER']}\n")
|
||||
session.shell_write("mkdir /tmp/TaggedBy#{ENV['USER']}\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Read output with session.shell_read()
|
||||
#
|
||||
end
|
||||
#
|
||||
# Read output with session.shell_read()
|
||||
#
|
||||
end
|
||||
|
||||
def on_session_close(session,reason='')
|
||||
print_status("Hooked session #{session.sid} is shutting down")
|
||||
end
|
||||
def on_session_close(session,reason='')
|
||||
print_status("Hooked session #{session.sid} is shutting down")
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
self.framework.events.add_session_subscriber(self)
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
self.framework.events.add_session_subscriber(self)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
self.framework.events.remove_session_subscriber(self)
|
||||
end
|
||||
def cleanup
|
||||
self.framework.events.remove_session_subscriber(self)
|
||||
end
|
||||
|
||||
def name
|
||||
"session_tagger"
|
||||
end
|
||||
def name
|
||||
"session_tagger"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Automatically interacts with new sessions"
|
||||
end
|
||||
def desc
|
||||
"Automatically interacts with new sessions"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
+68
-68
@@ -13,55 +13,55 @@ module Msf
|
||||
|
||||
class Plugin::SocketLogger < Msf::Plugin
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a socket communication logger
|
||||
#
|
||||
###
|
||||
class MySocketEventHandler
|
||||
include Rex::Socket::Comm::Events
|
||||
###
|
||||
#
|
||||
# This class implements a socket communication logger
|
||||
#
|
||||
###
|
||||
class MySocketEventHandler
|
||||
include Rex::Socket::Comm::Events
|
||||
|
||||
def initialize(path, prefix)
|
||||
@path = path
|
||||
@prefix = prefix
|
||||
end
|
||||
def initialize(path, prefix)
|
||||
@path = path
|
||||
@prefix = prefix
|
||||
end
|
||||
|
||||
def on_before_socket_create(comm, param)
|
||||
end
|
||||
def on_before_socket_create(comm, param)
|
||||
end
|
||||
|
||||
def on_socket_created(comm, sock, param)
|
||||
# Sockets created by the exploit have MsfExploit set and MsfPayload not set
|
||||
if (param.context['MsfExploit'] and (! param.context['MsfPayload'] ))
|
||||
sock.extend(SocketLogger::SocketTracer)
|
||||
sock.context = param.context
|
||||
sock.params = param
|
||||
sock.initlog(@path, @prefix)
|
||||
def on_socket_created(comm, sock, param)
|
||||
# Sockets created by the exploit have MsfExploit set and MsfPayload not set
|
||||
if (param.context['MsfExploit'] and (! param.context['MsfPayload'] ))
|
||||
sock.extend(SocketLogger::SocketTracer)
|
||||
sock.context = param.context
|
||||
sock.params = param
|
||||
sock.initlog(@path, @prefix)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(framework, opts)
|
||||
log_path = opts['path'] || "/tmp"
|
||||
log_prefix = opts['prefix'] || "socket_"
|
||||
def initialize(framework, opts)
|
||||
log_path = opts['path'] || "/tmp"
|
||||
log_prefix = opts['prefix'] || "socket_"
|
||||
|
||||
super
|
||||
@eh = MySocketEventHandler.new(log_path, log_prefix)
|
||||
Rex::Socket::Comm::Local.register_event_handler(@eh)
|
||||
end
|
||||
super
|
||||
@eh = MySocketEventHandler.new(log_path, log_prefix)
|
||||
Rex::Socket::Comm::Local.register_event_handler(@eh)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
Rex::Socket::Comm::Local.deregister_event_handler(@eh)
|
||||
end
|
||||
def cleanup
|
||||
Rex::Socket::Comm::Local.deregister_event_handler(@eh)
|
||||
end
|
||||
|
||||
def name
|
||||
"socket_logger"
|
||||
end
|
||||
def name
|
||||
"socket_logger"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Logs all socket operations to hex dumps in /tmp"
|
||||
end
|
||||
def desc
|
||||
"Logs all socket operations to hex dumps in /tmp"
|
||||
end
|
||||
|
||||
protected
|
||||
end
|
||||
@@ -72,41 +72,41 @@ end
|
||||
module SocketLogger
|
||||
module SocketTracer
|
||||
|
||||
@@last_id = 0
|
||||
@@last_id = 0
|
||||
|
||||
attr_accessor :context, :params
|
||||
attr_accessor :context, :params
|
||||
|
||||
# Hook the write method
|
||||
def write(buf, opts = {})
|
||||
@fd.puts "WRITE (#{buf.length} bytes)"
|
||||
@fd.puts Rex::Text.to_hex_dump(buf)
|
||||
super(buf, opts)
|
||||
end
|
||||
# Hook the write method
|
||||
def write(buf, opts = {})
|
||||
@fd.puts "WRITE (#{buf.length} bytes)"
|
||||
@fd.puts Rex::Text.to_hex_dump(buf)
|
||||
super(buf, opts)
|
||||
end
|
||||
|
||||
# Hook the read method
|
||||
def read(length = nil, opts = {})
|
||||
r = super(length, opts)
|
||||
# Hook the read method
|
||||
def read(length = nil, opts = {})
|
||||
r = super(length, opts)
|
||||
|
||||
@fd.puts "READ (#{r.length} bytes)"
|
||||
@fd.puts Rex::Text.to_hex_dump(r)
|
||||
return r
|
||||
end
|
||||
@fd.puts "READ (#{r.length} bytes)"
|
||||
@fd.puts Rex::Text.to_hex_dump(r)
|
||||
return r
|
||||
end
|
||||
|
||||
def close(*args)
|
||||
super(*args)
|
||||
@fd.close
|
||||
end
|
||||
def close(*args)
|
||||
super(*args)
|
||||
@fd.close
|
||||
end
|
||||
|
||||
def initlog(path, prefix)
|
||||
@log_path = path
|
||||
@log_prefix = prefix
|
||||
@log_id = @@last_id
|
||||
@@last_id += 1
|
||||
@fd = File.open(File.join(@log_path, "#{@log_prefix}#{@log_id}.log"), "w")
|
||||
@fd.puts "Socket created at #{Time.now}"
|
||||
@fd.puts "Info: #{params.proto} #{params.localhost}:#{params.localport} -> #{params.peerhost}:#{params.peerport}"
|
||||
@fd.puts ""
|
||||
end
|
||||
def initlog(path, prefix)
|
||||
@log_path = path
|
||||
@log_prefix = prefix
|
||||
@log_id = @@last_id
|
||||
@@last_id += 1
|
||||
@fd = File.open(File.join(@log_path, "#{@log_prefix}#{@log_id}.log"), "w")
|
||||
@fd.puts "Socket created at #{Time.now}"
|
||||
@fd.puts "Info: #{params.proto} #{params.localhost}:#{params.localport} -> #{params.peerhost}:#{params.peerport}"
|
||||
@fd.puts ""
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
+67
-67
@@ -14,88 +14,88 @@ module Msf
|
||||
class Plugin::EventSounds < Msf::Plugin
|
||||
|
||||
|
||||
attr_accessor :theme, :base, :queue, :queue_thread
|
||||
attr_accessor :theme, :base, :queue, :queue_thread
|
||||
|
||||
include Msf::SessionEvent
|
||||
include Msf::SessionEvent
|
||||
|
||||
def play_sound(event)
|
||||
self.queue.push(event)
|
||||
end
|
||||
def play_sound(event)
|
||||
self.queue.push(event)
|
||||
end
|
||||
|
||||
def on_session_open(session)
|
||||
event = 'session_open_' + session.type
|
||||
play_sound(event)
|
||||
end
|
||||
def on_session_open(session)
|
||||
event = 'session_open_' + session.type
|
||||
play_sound(event)
|
||||
end
|
||||
|
||||
def on_session_close(session, reason='')
|
||||
sid = session.sid.to_s
|
||||
play_sound('session')
|
||||
sid.unpack("C*").each do |c|
|
||||
play_sound("num" + [c].pack("C"))
|
||||
end
|
||||
play_sound('closed')
|
||||
end
|
||||
def on_session_close(session, reason='')
|
||||
sid = session.sid.to_s
|
||||
play_sound('session')
|
||||
sid.unpack("C*").each do |c|
|
||||
play_sound("num" + [c].pack("C"))
|
||||
end
|
||||
play_sound('closed')
|
||||
end
|
||||
|
||||
def on_plugin_load
|
||||
play_sound('plugin_load')
|
||||
end
|
||||
def on_plugin_load
|
||||
play_sound('plugin_load')
|
||||
end
|
||||
|
||||
def on_plugin_unload
|
||||
play_sound('plugin_unload')
|
||||
end
|
||||
def on_plugin_unload
|
||||
play_sound('plugin_unload')
|
||||
end
|
||||
|
||||
def start_sound_queue
|
||||
self.queue_thread = Thread.new do
|
||||
begin
|
||||
while(true)
|
||||
while(event = self.queue.shift)
|
||||
path = ::File.join(self.base, self.theme, "#{event}.wav")
|
||||
if(::File.exists?(path))
|
||||
Rex::Compat.play_sound(path)
|
||||
else
|
||||
print_status("Warning: sound file not found: #{path}")
|
||||
end
|
||||
end
|
||||
select(nil, nil, nil, 0.25)
|
||||
end
|
||||
rescue ::Exception => e
|
||||
print_status("Sound plugin: fatal error #{e} #{e.backtrace}")
|
||||
end
|
||||
end
|
||||
end
|
||||
def start_sound_queue
|
||||
self.queue_thread = Thread.new do
|
||||
begin
|
||||
while(true)
|
||||
while(event = self.queue.shift)
|
||||
path = ::File.join(self.base, self.theme, "#{event}.wav")
|
||||
if(::File.exists?(path))
|
||||
Rex::Compat.play_sound(path)
|
||||
else
|
||||
print_status("Warning: sound file not found: #{path}")
|
||||
end
|
||||
end
|
||||
select(nil, nil, nil, 0.25)
|
||||
end
|
||||
rescue ::Exception => e
|
||||
print_status("Sound plugin: fatal error #{e} #{e.backtrace}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def stop_sound_queue
|
||||
self.queue_thread.kill if self.queue_thread
|
||||
self.queue_thread = nil
|
||||
self.queue = []
|
||||
end
|
||||
def stop_sound_queue
|
||||
self.queue_thread.kill if self.queue_thread
|
||||
self.queue_thread = nil
|
||||
self.queue = []
|
||||
end
|
||||
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
self.queue = []
|
||||
self.theme = opts['theme'] || 'default'
|
||||
self.base = File.join(Msf::Config.install_root, "data", "sounds")
|
||||
self.framework.events.add_session_subscriber(self)
|
||||
start_sound_queue
|
||||
self.queue = []
|
||||
self.theme = opts['theme'] || 'default'
|
||||
self.base = File.join(Msf::Config.install_root, "data", "sounds")
|
||||
self.framework.events.add_session_subscriber(self)
|
||||
start_sound_queue
|
||||
|
||||
self.on_plugin_load
|
||||
end
|
||||
self.on_plugin_load
|
||||
end
|
||||
|
||||
def cleanup
|
||||
self.on_plugin_unload
|
||||
self.framework.events.remove_session_subscriber(self)
|
||||
stop_sound_queue
|
||||
end
|
||||
def cleanup
|
||||
self.on_plugin_unload
|
||||
self.framework.events.remove_session_subscriber(self)
|
||||
stop_sound_queue
|
||||
end
|
||||
|
||||
def name
|
||||
"sounds"
|
||||
end
|
||||
def name
|
||||
"sounds"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Automatically plays a sound when various framework events occur"
|
||||
end
|
||||
def desc
|
||||
"Automatically plays a sound when various framework events occur"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
+99
-99
@@ -15,121 +15,121 @@ module Msf
|
||||
###
|
||||
class Plugin::ThreadTest < Msf::Plugin
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a sample console command dispatcher.
|
||||
#
|
||||
###
|
||||
class ConsoleCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
###
|
||||
#
|
||||
# This class implements a sample console command dispatcher.
|
||||
#
|
||||
###
|
||||
class ConsoleCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
def name
|
||||
"ThreadTest"
|
||||
end
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
def name
|
||||
"ThreadTest"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"start_thread" => "Start a background thread that writes to the console",
|
||||
"stop_thread" => "Stop a background thread",
|
||||
"list_thread" => "List running threads"
|
||||
}
|
||||
end
|
||||
#
|
||||
# Returns the hash of commands supported by this dispatcher.
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"start_thread" => "Start a background thread that writes to the console",
|
||||
"stop_thread" => "Stop a background thread",
|
||||
"list_thread" => "List running threads"
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_start_thread(*args)
|
||||
if (@mythread)
|
||||
print_line("Test thread is already running")
|
||||
return
|
||||
end
|
||||
def cmd_start_thread(*args)
|
||||
if (@mythread)
|
||||
print_line("Test thread is already running")
|
||||
return
|
||||
end
|
||||
|
||||
@mythread = ::Thread.new {
|
||||
while(true)
|
||||
print_line("--- test thread ---")
|
||||
select(nil, nil, nil, 5)
|
||||
end
|
||||
}
|
||||
print_line("Test thread created")
|
||||
end
|
||||
@mythread = ::Thread.new {
|
||||
while(true)
|
||||
print_line("--- test thread ---")
|
||||
select(nil, nil, nil, 5)
|
||||
end
|
||||
}
|
||||
print_line("Test thread created")
|
||||
end
|
||||
|
||||
def cmd_stop_thread(*args)
|
||||
if (! @mythread)
|
||||
print_line("No test thread is running")
|
||||
return
|
||||
end
|
||||
def cmd_stop_thread(*args)
|
||||
if (! @mythread)
|
||||
print_line("No test thread is running")
|
||||
return
|
||||
end
|
||||
|
||||
@mythread.kill
|
||||
@mythread = nil
|
||||
print_line("Test thread stopped")
|
||||
end
|
||||
@mythread.kill
|
||||
@mythread = nil
|
||||
print_line("Test thread stopped")
|
||||
end
|
||||
|
||||
def cmd_list_thread(*args)
|
||||
Thread.list.each do |t|
|
||||
print_line(sprintf("Thread: 0x%.8x (%s/%d) (%s)", t.object_id, t.status, t.priority, t.tsource))
|
||||
print_line("")
|
||||
end
|
||||
end
|
||||
end
|
||||
def cmd_list_thread(*args)
|
||||
Thread.list.each do |t|
|
||||
print_line(sprintf("Thread: 0x%.8x (%s/%d) (%s)", t.object_id, t.status, t.priority, t.tsource))
|
||||
print_line("")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The constructor is called when an instance of the plugin is created. The
|
||||
# framework instance that the plugin is being associated with is passed in
|
||||
# the framework parameter. Plugins should call the parent constructor when
|
||||
# inheriting from Msf::Plugin to ensure that the framework attribute on
|
||||
# their instance gets set.
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
#
|
||||
# The constructor is called when an instance of the plugin is created. The
|
||||
# framework instance that the plugin is being associated with is passed in
|
||||
# the framework parameter. Plugins should call the parent constructor when
|
||||
# inheriting from Msf::Plugin to ensure that the framework attribute on
|
||||
# their instance gets set.
|
||||
#
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
# If this plugin is being loaded in the context of a console application
|
||||
# that uses the framework's console user interface driver, register
|
||||
# console dispatcher commands.
|
||||
add_console_dispatcher(ConsoleCommandDispatcher)
|
||||
# If this plugin is being loaded in the context of a console application
|
||||
# that uses the framework's console user interface driver, register
|
||||
# console dispatcher commands.
|
||||
add_console_dispatcher(ConsoleCommandDispatcher)
|
||||
|
||||
# Extend the thread to track the calling source
|
||||
Thread.class_eval("
|
||||
attr_accessor :tsource
|
||||
# Extend the thread to track the calling source
|
||||
Thread.class_eval("
|
||||
attr_accessor :tsource
|
||||
|
||||
alias initialize_old initialize
|
||||
alias initialize_old initialize
|
||||
|
||||
def initialize(&block)
|
||||
self.tsource = caller(1)
|
||||
initialize_old(&block)
|
||||
end
|
||||
")
|
||||
def initialize(&block)
|
||||
self.tsource = caller(1)
|
||||
initialize_old(&block)
|
||||
end
|
||||
")
|
||||
|
||||
print_status("ThreadTest plugin loaded.")
|
||||
end
|
||||
print_status("ThreadTest plugin loaded.")
|
||||
end
|
||||
|
||||
#
|
||||
# The cleanup routine for plugins gives them a chance to undo any actions
|
||||
# they may have done to the framework. For instance, if a console
|
||||
# dispatcher was added, then it should be removed in the cleanup routine.
|
||||
#
|
||||
def cleanup
|
||||
# If we had previously registered a console dispatcher with the console,
|
||||
# deregister it now.
|
||||
remove_console_dispatcher('ThreadTest')
|
||||
end
|
||||
#
|
||||
# The cleanup routine for plugins gives them a chance to undo any actions
|
||||
# they may have done to the framework. For instance, if a console
|
||||
# dispatcher was added, then it should be removed in the cleanup routine.
|
||||
#
|
||||
def cleanup
|
||||
# If we had previously registered a console dispatcher with the console,
|
||||
# deregister it now.
|
||||
remove_console_dispatcher('ThreadTest')
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a short, friendly name for the plugin.
|
||||
#
|
||||
def name
|
||||
"threadtest"
|
||||
end
|
||||
#
|
||||
# This method returns a short, friendly name for the plugin.
|
||||
#
|
||||
def name
|
||||
"threadtest"
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns a brief description of the plugin. It should be no
|
||||
# more than 60 characters, but there are no hard limits.
|
||||
#
|
||||
def desc
|
||||
"Thread testing plugin"
|
||||
end
|
||||
#
|
||||
# This method returns a brief description of the plugin. It should be no
|
||||
# more than 60 characters, but there are no hard limits.
|
||||
#
|
||||
def desc
|
||||
"Thread testing plugin"
|
||||
end
|
||||
|
||||
protected
|
||||
end
|
||||
|
||||
+80
-80
@@ -15,103 +15,103 @@ module Msf
|
||||
|
||||
class Plugin::TokenAdduser < Msf::Plugin
|
||||
|
||||
class TokenCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
class TokenCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"Token Adduser"
|
||||
end
|
||||
def name
|
||||
"Token Adduser"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
'token_adduser' => "Attempt to add an account using all connected meterpreter session tokens"
|
||||
}
|
||||
end
|
||||
def commands
|
||||
{
|
||||
'token_adduser' => "Attempt to add an account using all connected meterpreter session tokens"
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_token_adduser(*args)
|
||||
def cmd_token_adduser(*args)
|
||||
|
||||
opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ true, "Add account to host"],
|
||||
)
|
||||
opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ true, "Add account to host"],
|
||||
)
|
||||
|
||||
# This is ugly.
|
||||
if (args.length == 0)
|
||||
print_line("Usage: token_adduser [options] <username> <password>")
|
||||
print_line(opts.usage)
|
||||
return
|
||||
end
|
||||
|
||||
opt_user_pass = []
|
||||
username = nil
|
||||
password = nil
|
||||
host = nil
|
||||
opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
host = val
|
||||
# This is ugly.
|
||||
if (args.length == 0)
|
||||
print_line("Usage: token_adduser [options] <username> <password>")
|
||||
print_line(opts.usage)
|
||||
return
|
||||
end
|
||||
|
||||
opt_user_pass = []
|
||||
username = nil
|
||||
password = nil
|
||||
host = nil
|
||||
opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
host = val
|
||||
|
||||
else
|
||||
# Excuse my weak ruby skills. I'm sure there's a better way to get username and password
|
||||
# from the args.
|
||||
opt_user_pass << val
|
||||
end
|
||||
end
|
||||
else
|
||||
# Excuse my weak ruby skills. I'm sure there's a better way to get username and password
|
||||
# from the args.
|
||||
opt_user_pass << val
|
||||
end
|
||||
end
|
||||
|
||||
# Again, I'm sure there's a better way to do this.
|
||||
username = opt_user_pass[0]
|
||||
password = opt_user_pass[1]
|
||||
# Again, I'm sure there's a better way to do this.
|
||||
username = opt_user_pass[0]
|
||||
password = opt_user_pass[1]
|
||||
|
||||
tokens_del = {}
|
||||
tokens_imp = {}
|
||||
tokens_del = {}
|
||||
tokens_imp = {}
|
||||
|
||||
framework.sessions.each_key do |sid|
|
||||
session = framework.sessions[sid]
|
||||
next unless session.type == "meterpreter"
|
||||
framework.sessions.each_key do |sid|
|
||||
session = framework.sessions[sid]
|
||||
next unless session.type == "meterpreter"
|
||||
|
||||
print_status(">> Opening session #{session.sid} / #{session.session_host}")
|
||||
print_status(">> Opening session #{session.sid} / #{session.session_host}")
|
||||
|
||||
unless session.incognito
|
||||
session.core.use("incognito")
|
||||
end
|
||||
unless session.incognito
|
||||
session.core.use("incognito")
|
||||
end
|
||||
|
||||
unless session.incognito
|
||||
print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}")
|
||||
next
|
||||
end
|
||||
#print "DEBUG #{username} #{password}\n"
|
||||
res = session.incognito.incognito_add_user(host,username,password)
|
||||
if(res)
|
||||
print "#{res}\n"
|
||||
unless session.incognito
|
||||
print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}")
|
||||
next
|
||||
end
|
||||
#print "DEBUG #{username} #{password}\n"
|
||||
res = session.incognito.incognito_add_user(host,username,password)
|
||||
if(res)
|
||||
print "#{res}\n"
|
||||
|
||||
# Currently only stops on success if a user is trying to be added to a specific
|
||||
# host. I can't think of a good reason to stop on success (or even make it an option)
|
||||
# when trying to add a user to local sessions.
|
||||
if (host)
|
||||
if res =~ /\[\+\] Successfully|\[\-\] Password does not meet complexity requirements|\[\-\] User already exists/
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# Currently only stops on success if a user is trying to be added to a specific
|
||||
# host. I can't think of a good reason to stop on success (or even make it an option)
|
||||
# when trying to add a user to local sessions.
|
||||
if (host)
|
||||
if res =~ /\[\+\] Successfully|\[\-\] Password does not meet complexity requirements|\[\-\] User already exists/
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
add_console_dispatcher(TokenCommandDispatcher)
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
add_console_dispatcher(TokenCommandDispatcher)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('Token Adduser')
|
||||
end
|
||||
def cleanup
|
||||
remove_console_dispatcher('Token Adduser')
|
||||
end
|
||||
|
||||
def name
|
||||
"token_adduser"
|
||||
end
|
||||
def name
|
||||
"token_adduser"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Attempt to add an account using all connected meterpreter session tokens"
|
||||
end
|
||||
def desc
|
||||
"Attempt to add an account using all connected meterpreter session tokens"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+110
-110
@@ -7,147 +7,147 @@ module Msf
|
||||
|
||||
class Plugin::TokenHunter < Msf::Plugin
|
||||
|
||||
class TokenCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
class TokenCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"Token Hunter"
|
||||
end
|
||||
def name
|
||||
"Token Hunter"
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
'token_hunt_user' => "Scan all connected meterpreter sessions for active tokens corresponding to one or more users"
|
||||
}
|
||||
end
|
||||
def commands
|
||||
{
|
||||
'token_hunt_user' => "Scan all connected meterpreter sessions for active tokens corresponding to one or more users"
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_token_hunt_user(*args)
|
||||
def cmd_token_hunt_user(*args)
|
||||
|
||||
opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "This help menu"],
|
||||
"-f" => [ true, "A file containing a list of users to search for (one per line)"]
|
||||
)
|
||||
opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "This help menu"],
|
||||
"-f" => [ true, "A file containing a list of users to search for (one per line)"]
|
||||
)
|
||||
|
||||
opt_userfile = nil
|
||||
opt_users = []
|
||||
opt_userfile = nil
|
||||
opt_users = []
|
||||
|
||||
opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print_line("Usage: token_hunt_user [options] <username> [username] .. [username]")
|
||||
print_line(opts.usage)
|
||||
return
|
||||
when "-f"
|
||||
opt_userfile = val
|
||||
else
|
||||
opt_users << val
|
||||
end
|
||||
end
|
||||
opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print_line("Usage: token_hunt_user [options] <username> [username] .. [username]")
|
||||
print_line(opts.usage)
|
||||
return
|
||||
when "-f"
|
||||
opt_userfile = val
|
||||
else
|
||||
opt_users << val
|
||||
end
|
||||
end
|
||||
|
||||
if(opt_userfile)
|
||||
::File.open(opt_userfile, "rb") do |fd|
|
||||
fd.each_line do |line|
|
||||
line.strip!
|
||||
next if line.empty?
|
||||
next if line =~ /^#/
|
||||
opt_users << line
|
||||
end
|
||||
end
|
||||
end
|
||||
if(opt_userfile)
|
||||
::File.open(opt_userfile, "rb") do |fd|
|
||||
fd.each_line do |line|
|
||||
line.strip!
|
||||
next if line.empty?
|
||||
next if line =~ /^#/
|
||||
opt_users << line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
opt_users.uniq!
|
||||
opt_users.uniq!
|
||||
|
||||
tokens_del = {}
|
||||
tokens_imp = {}
|
||||
tokens_del = {}
|
||||
tokens_imp = {}
|
||||
|
||||
framework.sessions.each_key do |sid|
|
||||
session = framework.sessions[sid]
|
||||
next if session.type != "meterpreter"
|
||||
framework.sessions.each_key do |sid|
|
||||
session = framework.sessions[sid]
|
||||
next if session.type != "meterpreter"
|
||||
|
||||
print_status(">> Scanning session #{session.sid} / #{session.session_host}")
|
||||
print_status(">> Scanning session #{session.sid} / #{session.session_host}")
|
||||
|
||||
if(! session.incognito)
|
||||
session.core.use("incognito")
|
||||
end
|
||||
if(! session.incognito)
|
||||
session.core.use("incognito")
|
||||
end
|
||||
|
||||
if(! session.incognito)
|
||||
print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}")
|
||||
next
|
||||
end
|
||||
if(! session.incognito)
|
||||
print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}")
|
||||
next
|
||||
end
|
||||
|
||||
res = session.incognito.incognito_list_tokens(0)
|
||||
if(res)
|
||||
res["delegation"].split("\n").each do |user|
|
||||
res = session.incognito.incognito_list_tokens(0)
|
||||
if(res)
|
||||
res["delegation"].split("\n").each do |user|
|
||||
|
||||
opt_users.each do |needle|
|
||||
opt_users.each do |needle|
|
||||
|
||||
ndom,nusr = needle.split("\\")
|
||||
if(not nusr)
|
||||
nusr = ndom
|
||||
ndom = nil
|
||||
end
|
||||
ndom,nusr = needle.split("\\")
|
||||
if(not nusr)
|
||||
nusr = ndom
|
||||
ndom = nil
|
||||
end
|
||||
|
||||
if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase)
|
||||
print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)")
|
||||
next
|
||||
end
|
||||
if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase)
|
||||
print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)")
|
||||
next
|
||||
end
|
||||
|
||||
fdom,fusr = user.split("\\")
|
||||
fdom,fusr = user.split("\\")
|
||||
|
||||
if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase)
|
||||
print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)")
|
||||
end
|
||||
end
|
||||
if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase)
|
||||
print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)")
|
||||
end
|
||||
end
|
||||
|
||||
tokens_del[user] ||= []
|
||||
tokens_del[user] << session.sid
|
||||
end
|
||||
tokens_del[user] ||= []
|
||||
tokens_del[user] << session.sid
|
||||
end
|
||||
|
||||
|
||||
res["impersonation"].split("\n").each do |user|
|
||||
res["impersonation"].split("\n").each do |user|
|
||||
|
||||
opt_users.each do |needle|
|
||||
ndom,nusr = needle.split("\\")
|
||||
if(not nusr)
|
||||
nusr = ndom
|
||||
ndom = nil
|
||||
end
|
||||
opt_users.each do |needle|
|
||||
ndom,nusr = needle.split("\\")
|
||||
if(not nusr)
|
||||
nusr = ndom
|
||||
ndom = nil
|
||||
end
|
||||
|
||||
if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase)
|
||||
print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)")
|
||||
next
|
||||
end
|
||||
if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase)
|
||||
print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)")
|
||||
next
|
||||
end
|
||||
|
||||
fdom,fusr = user.split("\\")
|
||||
if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase)
|
||||
print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)")
|
||||
end
|
||||
end
|
||||
fdom,fusr = user.split("\\")
|
||||
if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase)
|
||||
print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)")
|
||||
end
|
||||
end
|
||||
|
||||
tokens_imp[user] ||= []
|
||||
tokens_imp[user] << session.sid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
tokens_imp[user] ||= []
|
||||
tokens_imp[user] << session.sid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
add_console_dispatcher(TokenCommandDispatcher)
|
||||
end
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
add_console_dispatcher(TokenCommandDispatcher)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('Token Hunter')
|
||||
end
|
||||
def cleanup
|
||||
remove_console_dispatcher('Token Hunter')
|
||||
end
|
||||
|
||||
def name
|
||||
"token_hunter"
|
||||
end
|
||||
def name
|
||||
"token_hunter"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Search all active meterpreter sessions for specific tokens"
|
||||
end
|
||||
def desc
|
||||
"Search all active meterpreter sessions for specific tokens"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+2271
-2271
@@ -13,2277 +13,2277 @@ require 'msf/core/rpc/v10/client'
|
||||
module Msf
|
||||
|
||||
class Plugin::Wmap < Msf::Plugin
|
||||
class WmapCommandDispatcher
|
||||
|
||||
attr_accessor :wmapmodules # Enabled Wmap modules
|
||||
attr_accessor :targets # Targets
|
||||
attr_accessor :lastsites # Temp location of previously obtained sites
|
||||
attr_accessor :rpcarr # Array or rpc connections
|
||||
attr_accessor :njobs # Max number of jobs
|
||||
attr_accessor :nmaxdisplay # Flag to stop displaying the same mesg
|
||||
attr_accessor :runlocal # Flag to run local modules only
|
||||
attr_accessor :masstop # Flag to stop everything
|
||||
attr_accessor :killwhenstop # Kill process when exiting
|
||||
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"wmap"
|
||||
end
|
||||
|
||||
#
|
||||
# The initial command set
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"wmap_targets" => "Manage targets",
|
||||
"wmap_sites" => "Manage sites",
|
||||
"wmap_nodes" => "Manage nodes",
|
||||
"wmap_run" => "Test targets",
|
||||
"wmap_modules" => "Manage wmap modules",
|
||||
"wmap_vulns" => "Display web vulns",
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_wmap_vulns(*args)
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-l'
|
||||
view_vulns
|
||||
return
|
||||
when '-h'
|
||||
print_status("Usage: wmap_vulns [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-l Display web vulns table")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_wmap_modules(*args)
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-l'
|
||||
view_modules
|
||||
return
|
||||
when '-r'
|
||||
load_wmap_modules(true)
|
||||
return
|
||||
when '-h'
|
||||
print_status("Usage: wmap_modules [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-l List all wmap enabled modules")
|
||||
print_line("\t-r Reload wmap modules")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_wmap_targets(*args)
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-c'
|
||||
self.targets = Hash.new()
|
||||
when '-l'
|
||||
view_targets
|
||||
return
|
||||
when '-t'
|
||||
process_urls(args.shift)
|
||||
when '-d'
|
||||
process_ids(args.shift)
|
||||
when '-h'
|
||||
print_status("Usage: wmap_targets [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-t [urls] Define target sites (vhost1,url[space]vhost2,url) ")
|
||||
print_line("\t-d [ids] Define target sites (id1, id2, id3 ...)")
|
||||
print_line("\t-c Clean target sites list")
|
||||
print_line("\t-l List all target sites")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_wmap_sites(*args)
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-a'
|
||||
s = add_web_site(args.shift)
|
||||
if s
|
||||
print_status("Site created.")
|
||||
else
|
||||
print_error("Unable to create site")
|
||||
end
|
||||
when '-d'
|
||||
del_idx = args
|
||||
if del_idx
|
||||
delete_sites(del_idx.select {|d| d =~ /^[0-9]*$/}.map(&:to_i).uniq)
|
||||
return
|
||||
else
|
||||
print_error("Provide index of site to delete")
|
||||
end
|
||||
when '-l'
|
||||
view_sites
|
||||
return
|
||||
when '-s'
|
||||
u = args.shift
|
||||
l = args.shift
|
||||
s = args.shift
|
||||
|
||||
if not u
|
||||
return
|
||||
end
|
||||
|
||||
if l == nil or l.empty?
|
||||
l = 200
|
||||
s = true
|
||||
else
|
||||
l = l.to_i
|
||||
s = false
|
||||
end
|
||||
|
||||
if u.include? 'http'
|
||||
# Parameters are in url form
|
||||
view_site_tree(u,l,s)
|
||||
else
|
||||
# Parameters are digits
|
||||
if !self.lastsites or self.lastsites.length == 0
|
||||
view_sites
|
||||
print_status ("Web sites ids. referenced from previous table.")
|
||||
end
|
||||
|
||||
target_whitelist = []
|
||||
ids = u.to_s.split(/,/)
|
||||
|
||||
ids.each do |id|
|
||||
next if id.to_s.strip.empty?
|
||||
|
||||
if id.to_i > self.lastsites.length
|
||||
print_error("Skipping id #{id}...")
|
||||
else
|
||||
target_whitelist << self.lastsites[id.to_i]
|
||||
#print_status("Loading #{self.lastsites[id.to_i]}.")
|
||||
end
|
||||
end
|
||||
|
||||
# Skip the DB entirely if no matches
|
||||
return if target_whitelist.length == 0
|
||||
|
||||
if not self.targets
|
||||
self.targets = Hash.new()
|
||||
end
|
||||
|
||||
target_whitelist.each do |ent|
|
||||
view_site_tree(ent,l,s)
|
||||
end
|
||||
end
|
||||
return
|
||||
when '-h'
|
||||
print_status("Usage: wmap_sites [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-a [url] Add site (vhost,url)")
|
||||
print_line("\t-d [ids] Delete sites (separate ids with space)")
|
||||
print_line("\t-l List all available sites")
|
||||
print_line("\t-s [id] Display site structure (vhost,url|ids) (level)")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_wmap_nodes(*args)
|
||||
|
||||
if not self.rpcarr
|
||||
self.rpcarr=Hash.new()
|
||||
end
|
||||
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-a'
|
||||
h = args.shift
|
||||
r = args.shift
|
||||
s = args.shift
|
||||
u = args.shift
|
||||
p = args.shift
|
||||
|
||||
res = rpc_add_node(h,r,s,u,p,false)
|
||||
if res
|
||||
print_status("Node created.")
|
||||
else
|
||||
print_error("Unable to create node")
|
||||
end
|
||||
when '-c'
|
||||
idref = args.shift
|
||||
|
||||
if not idref
|
||||
print_error("No id defined")
|
||||
return
|
||||
end
|
||||
if idref.upcase == 'ALL'
|
||||
print_status("All nodes removed")
|
||||
self.rpcarr = Hash.new()
|
||||
else
|
||||
idx=0
|
||||
self.rpcarr.each do |k,v|
|
||||
if idx == idref.to_i
|
||||
self.rpcarr.delete(k)
|
||||
print_status("Node deleted #{k}")
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
when '-d'
|
||||
host = args.shift
|
||||
port = args.shift
|
||||
user = args.shift
|
||||
pass = args.shift
|
||||
dbname = args.shift
|
||||
|
||||
res = rpc_db_nodes(host,port,user,pass,dbname)
|
||||
if res
|
||||
print_status("OK.")
|
||||
else
|
||||
print_error("Error")
|
||||
end
|
||||
when '-l'
|
||||
rpc_list_nodes
|
||||
return
|
||||
when '-j'
|
||||
rpc_view_jobs
|
||||
return
|
||||
when '-k'
|
||||
node = args.shift
|
||||
jid = args.shift
|
||||
rpc_kill_node(node,jid)
|
||||
return
|
||||
when '-h'
|
||||
print_status("Usage: wmap_nodes [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-c id Remove id node (Use ALL for ALL nodes")
|
||||
print_line("\t-a host port ssl user pass Add node")
|
||||
print_line("\t-d host port user pass db Force all nodes to connect to db")
|
||||
print_line("\t-j View detailed jobs")
|
||||
print_line("\t-k ALL|id ALL|job_id Kill jobs on node")
|
||||
print_line("\t-l List all current nodes")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_wmap_run(*args)
|
||||
# Stop everything
|
||||
self.masstop = false
|
||||
self.killwhenstop = true
|
||||
|
||||
trap("INT") {
|
||||
print_error("Stopping execution...")
|
||||
self.masstop = true
|
||||
if self.killwhenstop
|
||||
rpc_kill_node('ALL','ALL')
|
||||
end
|
||||
}
|
||||
|
||||
# Max numbers of concurrent jobs per node
|
||||
self.njobs = 25
|
||||
self.nmaxdisplay = false
|
||||
self.runlocal = false
|
||||
|
||||
# Formating
|
||||
sizeline = 60
|
||||
|
||||
wmap_show = 2**0
|
||||
wmap_expl = 2**1
|
||||
|
||||
# Exclude files can be modified by setting datastore['WMAP_EXCLUDE']
|
||||
wmap_exclude_files = '.*\.(gif|jpg|png*)$'
|
||||
|
||||
run_wmap_ssl = true
|
||||
run_wmap_server = true
|
||||
run_wmap_dir_file = true
|
||||
run_wmap_query = true
|
||||
run_wmap_unique_query = true
|
||||
run_wmap_generic = true
|
||||
|
||||
# If module supports datastore['VERBOSE']
|
||||
moduleverbose = false
|
||||
|
||||
showprogress = false
|
||||
|
||||
if not self.rpcarr
|
||||
self.rpcarr = Hash.new()
|
||||
end
|
||||
|
||||
if not run_wmap_ssl
|
||||
print_status("Loading of wmap ssl modules disabled.")
|
||||
end
|
||||
if not run_wmap_server
|
||||
print_status("Loading of wmap server modules disabled.")
|
||||
end
|
||||
if not run_wmap_dir_file
|
||||
print_status("Loading of wmap dir and file modules disabled.")
|
||||
end
|
||||
if not run_wmap_query
|
||||
print_status("Loading of wmap query modules disabled.")
|
||||
end
|
||||
if not run_wmap_unique_query
|
||||
print_status("Loading of wmap unique query modules disabled.")
|
||||
end
|
||||
if not run_wmap_generic
|
||||
print_status("Loading of wmap generic modules disabled.")
|
||||
end
|
||||
|
||||
stamp = Time.now.to_f
|
||||
mode = 0
|
||||
|
||||
eprofile = []
|
||||
using_p = false
|
||||
using_m = false
|
||||
usinginipath = false
|
||||
|
||||
mname = ''
|
||||
inipathname = '/'
|
||||
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-t'
|
||||
mode |= wmap_show
|
||||
when '-e'
|
||||
mode |= wmap_expl
|
||||
|
||||
profile = args.shift
|
||||
|
||||
if profile
|
||||
print_status("Using profile #{profile}.")
|
||||
|
||||
begin
|
||||
File.open(profile).each do |str|
|
||||
if not str.include? '#'
|
||||
# Not a comment
|
||||
modname = str.strip
|
||||
if not modname.empty?
|
||||
eprofile << modname
|
||||
end
|
||||
end
|
||||
using_p = true
|
||||
end
|
||||
rescue
|
||||
print_error("Profile not found or invalid.")
|
||||
return
|
||||
end
|
||||
else
|
||||
print_status("Using ALL wmap enabled modules.")
|
||||
end
|
||||
when '-m'
|
||||
mode |= wmap_expl
|
||||
|
||||
mname = args.shift
|
||||
|
||||
if mname
|
||||
print_status("Using module #{mname}.")
|
||||
end
|
||||
using_m = true
|
||||
when '-p'
|
||||
mode |= wmap_expl
|
||||
|
||||
inipathname = args.shift
|
||||
|
||||
if inipathname
|
||||
print_status("Using initial path #{inipathname}.")
|
||||
end
|
||||
usinginipath = true
|
||||
|
||||
when '-h'
|
||||
print_status("Usage: wmap_run [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-t Show all enabled modules")
|
||||
print_line("\t-m [regex] Launch only modules that name match provided regex.")
|
||||
print_line("\t-p [regex] Only test path defined by regex.")
|
||||
print_line("\t-e [/path/to/profile] Launch profile modules against all matched targets.")
|
||||
print_line("\t (No profile file runs all enabled modules.)")
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if (self.rpcarr.length == 0) and (mode & wmap_show == 0)
|
||||
print_error("NO WMAP NODES DEFINED. Executing local modules")
|
||||
self.runlocal = true
|
||||
end
|
||||
|
||||
if self.targets == nil
|
||||
print_error("Targets have not been selected.")
|
||||
return
|
||||
end
|
||||
|
||||
if self.targets.keys.length == 0
|
||||
print_error("Targets have not been selected.")
|
||||
return
|
||||
end
|
||||
|
||||
execmod = true
|
||||
if (mode & wmap_show != 0)
|
||||
execmod = false
|
||||
end
|
||||
|
||||
self.targets.each_with_index do |t, idx|
|
||||
|
||||
selected_host = t[1][:host]
|
||||
selected_port = t[1][:port]
|
||||
selected_ssl = t[1][:ssl]
|
||||
selected_vhost = t[1][:vhost]
|
||||
|
||||
print_status ("Testing target:")
|
||||
print_status ("\tSite: #{selected_vhost} (#{selected_host})")
|
||||
print_status ("\tPort: #{selected_port} SSL: #{selected_ssl}")
|
||||
print_line '='* sizeline
|
||||
print_status("Testing started. #{(Time.now )}")
|
||||
|
||||
if not selected_ssl
|
||||
run_wmap_ssl = false
|
||||
#print_status ("Target is not SSL. SSL modules disabled.")
|
||||
end
|
||||
|
||||
# wmap_dir, wmap_file
|
||||
matches = Hash.new()
|
||||
|
||||
# wmap_server
|
||||
matches1 = Hash.new()
|
||||
|
||||
# wmap_query
|
||||
matches2 = Hash.new()
|
||||
|
||||
# wmap_ssl
|
||||
matches3 = Hash.new()
|
||||
|
||||
# wmap_unique_query
|
||||
matches5 = Hash.new()
|
||||
|
||||
# wmap_generic
|
||||
matches10 = Hash.new()
|
||||
|
||||
# OPTIONS
|
||||
opt_str = nil
|
||||
jobify = false
|
||||
|
||||
# This will be clean later
|
||||
load_wmap_modules(false)
|
||||
|
||||
self.wmapmodules.each do |w|
|
||||
case w[2]
|
||||
when :wmap_server
|
||||
if run_wmap_server
|
||||
matches1[w]=true
|
||||
end
|
||||
when :wmap_query
|
||||
if run_wmap_query
|
||||
matches2[w]=true
|
||||
end
|
||||
when :wmap_unique_query
|
||||
if run_wmap_unique_query
|
||||
matches5[w]=true
|
||||
end
|
||||
when :wmap_generic
|
||||
if run_wmap_generic
|
||||
matches10[w]=true
|
||||
end
|
||||
when :wmap_dir, :wmap_file
|
||||
if run_wmap_dir_file
|
||||
matches[w]=true
|
||||
end
|
||||
when :wmap_ssl
|
||||
if run_wmap_ssl
|
||||
matches3[w]=true
|
||||
end
|
||||
else
|
||||
# Black Hole
|
||||
end
|
||||
end
|
||||
|
||||
# Execution order (orderid)
|
||||
matches = sort_by_orderid(matches)
|
||||
matches1 = sort_by_orderid(matches1)
|
||||
matches2 = sort_by_orderid(matches2)
|
||||
matches3 = sort_by_orderid(matches3)
|
||||
matches5 = sort_by_orderid(matches5)
|
||||
matches10 = sort_by_orderid(matches10)
|
||||
|
||||
#
|
||||
# Handle modules that need to be run before all tests IF SERVER is SSL, once usually again the SSL web server.
|
||||
# :wmap_ssl
|
||||
#
|
||||
|
||||
print_status "\n=[ SSL testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
if not selected_ssl
|
||||
print_status ("Target is not SSL. SSL modules disabled.")
|
||||
end
|
||||
|
||||
idx = 0
|
||||
matches3.each_key do |xref|
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handle modules that need to be run before all tests, once usually again the web server.
|
||||
# :wmap_server
|
||||
#
|
||||
print_status "\n=[ Web Server testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches1.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handle modules to be run at every path/file
|
||||
# wmap_dir, wmap_file
|
||||
#
|
||||
print_status "\n=[ File/Dir testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx+=1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
#
|
||||
# Run the plugins that only need to be
|
||||
# launched once.
|
||||
#
|
||||
|
||||
wtype = xref[2]
|
||||
|
||||
h = self.framework.db.workspace.hosts.find_by_address(selected_host)
|
||||
s = h.services.find_by_port(selected_port)
|
||||
w = s.web_sites.find_by_vhost(selected_vhost)
|
||||
|
||||
test_tree = load_tree(w)
|
||||
test_tree.each do |node|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
p = node.current_path
|
||||
testpath = Pathname.new(p)
|
||||
strpath = testpath.cleanpath(false).to_s
|
||||
|
||||
#
|
||||
# Fixing paths
|
||||
#
|
||||
|
||||
if node.is_leaf? and not node.is_root?
|
||||
#
|
||||
# Later we can add here more checks to see if its a file
|
||||
#
|
||||
else
|
||||
if node.is_root?
|
||||
strpath = "/"
|
||||
else
|
||||
strpath = strpath.chomp + "/"
|
||||
end
|
||||
end
|
||||
|
||||
strpath = strpath.gsub("//", "/")
|
||||
#print_status("Testing path: #{strpath}")
|
||||
|
||||
#
|
||||
# Launch plugin depending module type.
|
||||
# Module type depends on main input type.
|
||||
# Code may be the same but it depend on final
|
||||
# versions of plugins
|
||||
#
|
||||
|
||||
case wtype
|
||||
when :wmap_file
|
||||
if node.is_leaf? and not node.is_root?
|
||||
#
|
||||
# Check if an exclusion regex has been defined
|
||||
#
|
||||
if self.framework.datastore['WMAP_EXCLUDE']
|
||||
excludefilestr = self.framework.datastore['WMAP_EXCLUDE']
|
||||
else
|
||||
excludefilestr = wmap_exclude_files
|
||||
end
|
||||
|
||||
if not strpath.match(excludefilestr)
|
||||
if (not usinginipath) or (usinginipath and strpath.match(inipathname))
|
||||
modopts['PATH'] = strpath
|
||||
print_status("Path: #{strpath}")
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
when :wmap_dir
|
||||
if (node.is_leaf? and not strpath.include? ".") or node.is_root? or not node.is_leaf?
|
||||
if (not usinginipath) or (usinginipath and strpath.match(inipathname))
|
||||
|
||||
modopts['PATH'] = strpath
|
||||
print_status("Path: #{strpath}")
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Run modules for each request to play with URI with UNIQUE query parameters.
|
||||
# wmap_unique_query
|
||||
#
|
||||
print_status "\n=[ Unique Query testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches5.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
#
|
||||
# Run the plugins for each request that have a distinct
|
||||
# GET/POST URI QUERY string.
|
||||
#
|
||||
|
||||
utest_query = Hash.new()
|
||||
|
||||
h = self.framework.db.workspace.hosts.find_by_address(selected_host)
|
||||
s = h.services.find_by_port(selected_port)
|
||||
w = s.web_sites.find_by_vhost(selected_vhost)
|
||||
|
||||
w.web_forms.each do |form|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
#
|
||||
# Only test unique query strings by comparing signature to previous tested signatures 'path,p1,p2,pn'
|
||||
#
|
||||
|
||||
datastr = ""
|
||||
typestr = ""
|
||||
|
||||
temparr = []
|
||||
|
||||
#print_status "---------"
|
||||
#print_status form.params
|
||||
#print_status "+++++++++"
|
||||
|
||||
form.params.each do |p|
|
||||
pn, pv, pt = p
|
||||
if pn
|
||||
if not pn.empty?
|
||||
if not pv or pv.empty?
|
||||
#TODO add value based on param name
|
||||
pv = "aaa"
|
||||
end
|
||||
|
||||
#temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s)
|
||||
temparr << pn.to_s + "=" + pv.to_s
|
||||
end
|
||||
else
|
||||
print_error("Blank parameter name. Form #{form.path}")
|
||||
end
|
||||
end
|
||||
|
||||
datastr = temparr.join("&") if (temparr and not temparr.empty?)
|
||||
|
||||
if (utest_query.has_key?(signature(form.path,datastr)) == false)
|
||||
|
||||
modopts['METHOD'] = form.method.upcase
|
||||
modopts['PATH'] = form.path
|
||||
modopts['QUERY'] = form.query
|
||||
if form.method.upcase == 'GET'
|
||||
modopts['QUERY'] = datastr
|
||||
modopts['DATA'] = ""
|
||||
end
|
||||
if form.method.upcase == 'POST'
|
||||
modopts['DATA'] = datastr
|
||||
end
|
||||
modopts['TYPES'] = typestr
|
||||
|
||||
#
|
||||
# TODO: Add headers, etc.
|
||||
#
|
||||
if (not usinginipath) or (usinginipath and form.path.match(inipathname))
|
||||
|
||||
print_status "Path #{form.path}"
|
||||
#print_status("Unique PATH #{modopts['PATH']}")
|
||||
#print_status("Unique GET #{modopts['QUERY']}")
|
||||
#print_status("Unique POST #{modopts['DATA']}")
|
||||
#print_status("MODOPTS: #{modopts}")
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
utest_query[signature(form.path,datastr)]=1
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
else
|
||||
#print_status("Already tested")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Run modules for each request to play with URI query parameters.
|
||||
# This approach will reduce the complexity of the Tree used before
|
||||
# and will make this shotgun implementation much simple.
|
||||
# wmap_query
|
||||
#
|
||||
print_status "\n=[ Query testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches2.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
#
|
||||
# Run the plugins for each request that have a distinct
|
||||
# GET/POST URI QUERY string.
|
||||
#
|
||||
|
||||
h = self.framework.db.workspace.hosts.find_by_address(selected_host)
|
||||
s = h.services.find_by_port(selected_port)
|
||||
w = s.web_sites.find_by_vhost(selected_vhost)
|
||||
|
||||
w.web_forms.each do |req|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
datastr = ""
|
||||
typestr = ""
|
||||
|
||||
temparr = []
|
||||
|
||||
req.params.each do |p|
|
||||
pn, pv, pt = p
|
||||
if pn
|
||||
if not pn.empty?
|
||||
if not pv or pv.empty?
|
||||
#TODO add value based on param name
|
||||
pv = "aaa"
|
||||
end
|
||||
#temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s)
|
||||
temparr << pn.to_s + "=" + pv.to_s
|
||||
end
|
||||
else
|
||||
print_error("Blank parameter name. Form #{req.path}")
|
||||
end
|
||||
end
|
||||
|
||||
datastr = temparr.join("&") if (temparr and not temparr.empty?)
|
||||
|
||||
modopts['METHOD'] = req.method.upcase
|
||||
modopts['PATH'] = req.path
|
||||
if req.method.upcase == 'GET'
|
||||
modopts['QUERY'] = datastr
|
||||
modopts['DATA'] = ""
|
||||
end
|
||||
modopts['DATA'] = datastr if req.method.upcase == 'POST'
|
||||
modopts['TYPES'] = typestr
|
||||
|
||||
#
|
||||
# TODO: Add method, headers, etc.
|
||||
#
|
||||
if (not usinginipath) or (usinginipath and req.path.match(inipathname))
|
||||
|
||||
print_status "Path #{req.path}"
|
||||
#print_status("Query PATH #{modopts['PATH']}")
|
||||
#print_status("Query GET #{modopts['QUERY']}")
|
||||
#print_status("Query POST #{modopts['DATA']}")
|
||||
#print_status("Query TYPES #{typestr}")
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handle modules that need to be after all tests, once.
|
||||
# Good place to have modules that analize the test results and/or
|
||||
# launch exploits.
|
||||
# :wmap_generic
|
||||
#
|
||||
print_status "\n=[ General testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches10.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
#
|
||||
# Run the plugins that only need to be
|
||||
# launched once.
|
||||
#
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
print_line "+" * sizeline
|
||||
|
||||
if not self.runlocal
|
||||
if execmod
|
||||
rpc_list_nodes()
|
||||
print_status("Note: Use wmap_nodes -l to list node status for completion")
|
||||
end
|
||||
end
|
||||
|
||||
print_line("Launch completed in #{(Time.now.to_f - stamp)} seconds.")
|
||||
print_line "+" * sizeline
|
||||
end
|
||||
|
||||
print_status("Done.")
|
||||
end
|
||||
|
||||
# EOM
|
||||
end
|
||||
|
||||
def view_targets
|
||||
if self.targets == nil or self.targets.keys.length == 0
|
||||
print_status "No targets have been defined"
|
||||
return
|
||||
end
|
||||
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => 'Defined targets',
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Vhost',
|
||||
'Host',
|
||||
'Port',
|
||||
'SSL',
|
||||
'Path',
|
||||
])
|
||||
|
||||
self.targets.each_with_index { |t, idx|
|
||||
tbl << [ idx.to_s, t[1][:vhost], t[1][:host], t[1][:port], t[1][:ssl], "\t"+t[1][:path].to_s ]
|
||||
}
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
end
|
||||
|
||||
def delete_sites(wmap_index)
|
||||
idx = 0
|
||||
to_del = {}
|
||||
# Rebuild the index from wmap_sites -l
|
||||
self.framework.db.hosts.each do |bdhost|
|
||||
bdhost.services.each do |serv|
|
||||
serv.web_sites.each do |web|
|
||||
# If the index of this site matches any deletion index,
|
||||
# add to our hash, saving the index for later output
|
||||
to_del[idx] = web if wmap_index.any? {|w| w.to_i == idx}
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
to_del.each do |widx,wsite|
|
||||
if wsite.delete
|
||||
print_status("Deleted #{wsite.vhost} on #{wsite.service.host.address} at index #{widx}")
|
||||
else
|
||||
print_error("Could note delete {wsite.vhost} on #{wsite.service.host.address} at index #{widx}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def view_sites
|
||||
# Clean temporary sites list
|
||||
self.lastsites = []
|
||||
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => 'Available sites',
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Host',
|
||||
'Vhost',
|
||||
'Port',
|
||||
'Proto',
|
||||
'# Pages',
|
||||
'# Forms',
|
||||
])
|
||||
|
||||
idx = 0
|
||||
self.framework.db.hosts.each do |bdhost|
|
||||
bdhost.services.each do |serv|
|
||||
serv.web_sites.each do |web|
|
||||
c = web.web_pages.count
|
||||
f = web.web_forms.count
|
||||
tbl << [ idx.to_s, bdhost.address, web.vhost, serv.port, serv.name, c.to_s, f.to_s ]
|
||||
idx += 1
|
||||
|
||||
turl = web.vhost + "," + serv.name + "://" +bdhost.address.to_s + ":" + serv.port.to_s + "/"
|
||||
self.lastsites << turl
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
|
||||
end
|
||||
|
||||
# Reusing code from hdmoore
|
||||
#
|
||||
# Allow the URL to be supplied as VHOST,URL if a custom VHOST
|
||||
# should be used. This allows for things like:
|
||||
# localhost,http://192.168.0.2/admin/
|
||||
|
||||
def add_web_site(url)
|
||||
|
||||
vhost = nil
|
||||
|
||||
# Allow the URL to be supplied as VHOST,URL if a custom VHOST
|
||||
# should be used. This allows for things like:
|
||||
# localhost,http://192.168.0.2/admin/
|
||||
|
||||
if url !~ /^http/
|
||||
vhost,url = url.split(",", 2)
|
||||
if url.to_s.empty?
|
||||
url = vhost
|
||||
vhost = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Prefix http:// when the URL has no specified parameter
|
||||
if url !~ /^[a-z0-9A-Z]+:\/\//
|
||||
url = "http://" + url
|
||||
end
|
||||
|
||||
uri = URI.parse(url) rescue nil
|
||||
if not uri
|
||||
print_error("Could not understand URL: #{url}")
|
||||
return
|
||||
end
|
||||
|
||||
if uri.scheme !~ /^https?/
|
||||
print_error("Only http and https URLs are accepted: #{url}")
|
||||
return
|
||||
end
|
||||
|
||||
ssl = false
|
||||
if uri.scheme == 'https'
|
||||
ssl = true
|
||||
end
|
||||
|
||||
site = self.framework.db.report_web_site(:wait => true, :host => uri.host, :port => uri.port, :vhost => vhost, :ssl => ssl)
|
||||
|
||||
return site
|
||||
end
|
||||
|
||||
# Code by hdm. Modified two lines by et
|
||||
#
|
||||
def process_urls(urlstr)
|
||||
|
||||
target_whitelist = []
|
||||
|
||||
urls = urlstr.to_s.split(/\s+/)
|
||||
|
||||
|
||||
urls.each do |url|
|
||||
next if url.to_s.strip.empty?
|
||||
vhost = nil
|
||||
|
||||
# Allow the URL to be supplied as VHOST,URL if a custom VHOST
|
||||
# should be used. This allows for things like:
|
||||
# localhost,http://192.168.0.2/admin/
|
||||
|
||||
if url !~ /^http/
|
||||
vhost,url = url.split(",", 2)
|
||||
if url.to_s.empty?
|
||||
url = vhost
|
||||
vhost = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Prefix http:// when the URL has no specified parameter
|
||||
if url !~ /^[a-z0-9A-Z]+:\/\//
|
||||
url = "http://" + url
|
||||
end
|
||||
|
||||
uri = URI.parse(url) rescue nil
|
||||
if not uri
|
||||
print_error("Could not understand URL: #{url}")
|
||||
next
|
||||
end
|
||||
|
||||
if uri.scheme !~ /^https?/
|
||||
print_error("Only http and https URLs are accepted: #{url}")
|
||||
next
|
||||
end
|
||||
|
||||
target_whitelist << [vhost || uri.host, uri]
|
||||
end
|
||||
|
||||
# Skip the DB entirely if no matches
|
||||
return if target_whitelist.length == 0
|
||||
|
||||
if not self.targets
|
||||
# First time targets are defined
|
||||
self.targets = Hash.new()
|
||||
end
|
||||
|
||||
target_whitelist.each do |ent|
|
||||
vhost,target = ent
|
||||
|
||||
host = self.framework.db.workspace.hosts.find_by_address(target.host)
|
||||
if not host
|
||||
print_error("No matching host for #{target.host}")
|
||||
next
|
||||
end
|
||||
serv = host.services.find_by_port_and_proto(target.port, 'tcp')
|
||||
if not serv
|
||||
print_error("No matching service for #{target.host}:#{target.port}")
|
||||
next
|
||||
end
|
||||
|
||||
#print_status "aaa"
|
||||
#print_status framework.db.workspace.name
|
||||
|
||||
sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id)
|
||||
|
||||
sites.each do |site|
|
||||
# Initial defaul path
|
||||
inipath = target.path
|
||||
if target.path.empty?
|
||||
inipath = '/'
|
||||
end
|
||||
|
||||
#site.web_forms.find_all_by_path(target.path).each do |form|
|
||||
ckey = [ site.vhost, host.address, serv.port, inipath].join("|")
|
||||
|
||||
if not self.targets[ckey]
|
||||
self.targets[ckey] = WebTarget.new
|
||||
self.targets[ckey].merge!({
|
||||
:vhost => site.vhost,
|
||||
:host => host.address,
|
||||
:port => serv.port,
|
||||
:ssl => (serv.name == "https"),
|
||||
:path => inipath
|
||||
})
|
||||
#self.targets[ckey][inipath] = []
|
||||
else
|
||||
print_status("Target already set in targets list.")
|
||||
end
|
||||
|
||||
# Store the form object in the hash for this path
|
||||
#self.targets[ckey][inipath] << inipath
|
||||
#end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Code by hdm. Modified two lines by et
|
||||
# lastsites contains a temporary array with vhost,url strings so the id can be
|
||||
# referenced in the array and prevent new sites added in the db to corrupt previous id list.
|
||||
def process_ids(idsstr)
|
||||
|
||||
if !self.lastsites or self.lastsites.length == 0
|
||||
view_sites
|
||||
print_status ("Web sites ids. referenced from previous table.")
|
||||
end
|
||||
|
||||
target_whitelist = []
|
||||
ids = idsstr.to_s.split(/,/)
|
||||
|
||||
ids.each do |id|
|
||||
next if id.to_s.strip.empty?
|
||||
|
||||
if id.to_i > self.lastsites.length
|
||||
print_error("Skipping id #{id}...")
|
||||
else
|
||||
target_whitelist << self.lastsites[id.to_i]
|
||||
print_status("Loading #{self.lastsites[id.to_i]}.")
|
||||
end
|
||||
end
|
||||
|
||||
# Skip the DB entirely if no matches
|
||||
return if target_whitelist.length == 0
|
||||
|
||||
if not self.targets
|
||||
self.targets = Hash.new()
|
||||
end
|
||||
|
||||
target_whitelist.each do |ent|
|
||||
process_urls(ent)
|
||||
end
|
||||
end
|
||||
|
||||
def view_site_tree(urlstr, md, ld)
|
||||
if not urlstr
|
||||
return
|
||||
end
|
||||
|
||||
site_whitelist = []
|
||||
|
||||
urls = urlstr.to_s.split(/\s+/)
|
||||
|
||||
urls.each do |url|
|
||||
next if url.to_s.strip.empty?
|
||||
vhost = nil
|
||||
|
||||
# Allow the URL to be supplied as VHOST,URL if a custom VHOST
|
||||
# should be used. This allows for things like:
|
||||
# localhost,http://192.168.0.2/admin/
|
||||
|
||||
if url !~ /^http/
|
||||
vhost,url = url.split(",", 2)
|
||||
|
||||
if url.to_s.empty?
|
||||
url = vhost
|
||||
vhost = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Prefix http:// when the URL has no specified parameter
|
||||
if url !~ /^[a-z0-9A-Z]+:\/\//
|
||||
url = "http://" + url
|
||||
end
|
||||
|
||||
uri = URI.parse(url) rescue nil
|
||||
if not uri
|
||||
print_error("Could not understand URL: #{url}")
|
||||
next
|
||||
end
|
||||
|
||||
if uri.scheme !~ /^https?/
|
||||
print_error("Only http and https URLs are accepted: #{url}")
|
||||
next
|
||||
end
|
||||
|
||||
site_whitelist << [vhost || uri.host, uri]
|
||||
end
|
||||
|
||||
# Skip the DB entirely if no matches
|
||||
return if site_whitelist.length == 0
|
||||
|
||||
vsites = Hash.new()
|
||||
|
||||
site_whitelist.each do |ent|
|
||||
vhost,target = ent
|
||||
|
||||
host = self.framework.db.workspace.hosts.find_by_address(target.host)
|
||||
if not host
|
||||
print_error("No matching host for #{target.host}")
|
||||
next
|
||||
end
|
||||
serv = host.services.find_by_port_and_proto(target.port, 'tcp')
|
||||
if not serv
|
||||
print_error("No matching service for #{target.host}:#{target.port}")
|
||||
next
|
||||
end
|
||||
|
||||
sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id)
|
||||
|
||||
sites.each do |site|
|
||||
t = load_tree(site)
|
||||
print_tree(t,target.host,md,ld)
|
||||
print_line("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Load website structure into a tree
|
||||
#
|
||||
|
||||
def load_tree(s)
|
||||
|
||||
pathchr = '/'
|
||||
|
||||
wtree = Tree.new(s.vhost)
|
||||
|
||||
# Load site pages
|
||||
s.web_pages.find(:all, :order => 'path').each do |req|
|
||||
tarray = req.path.to_s.split(pathchr)
|
||||
tarray.delete("")
|
||||
tpath = Pathname.new(pathchr)
|
||||
tarray.each do |df|
|
||||
wtree.add_at_path(tpath.to_s,df)
|
||||
tpath = tpath + Pathname.new(df.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Load site forms
|
||||
s.web_forms.each do |req|
|
||||
tarray = req.path.to_s.split(pathchr)
|
||||
tarray.delete("")
|
||||
tpath = Pathname.new(pathchr)
|
||||
tarray.each do |df|
|
||||
wtree.add_at_path(tpath.to_s,df)
|
||||
tpath = tpath + Pathname.new(df.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
return wtree
|
||||
end
|
||||
|
||||
#
|
||||
# Print Tree structure. Still ugly
|
||||
#
|
||||
|
||||
def print_tree(tree, ip, maxlevel, limitlevel)
|
||||
initab = " " * 4
|
||||
indent = 6
|
||||
if tree != nil and tree.depth <= maxlevel
|
||||
print initab + (" " * indent * tree.depth)
|
||||
if tree.depth > 0
|
||||
print "|"+("-" * (indent-1))+"/"
|
||||
end
|
||||
if tree.depth >= 0
|
||||
if tree.depth == 0
|
||||
print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n"
|
||||
|
||||
else
|
||||
c = tree.children.count
|
||||
if c > 0
|
||||
print tree.name + " (" + c.to_s+")\n"
|
||||
else
|
||||
print tree.name + "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tree.children.each_pair do |name,child|
|
||||
print_tree(child,ip,maxlevel,limitlevel)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def signature(fpath,fquery)
|
||||
hsig = Hash.new()
|
||||
|
||||
hsig = queryparse(fquery)
|
||||
|
||||
#
|
||||
# Signature of the form ',p1,p2,pn' then to be appended to path: path,p1,p2,pn
|
||||
#
|
||||
|
||||
sigstr = fpath + "," + hsig.map{|p| p[0].to_s}.join(",")
|
||||
end
|
||||
|
||||
def queryparse(query)
|
||||
params = Hash.new()
|
||||
|
||||
query.split(/[&;]/n).each do |pairs|
|
||||
key, value = pairs.split('=',2)
|
||||
if params.has_key?(key)
|
||||
#Error
|
||||
else
|
||||
params[key] = value
|
||||
end
|
||||
end
|
||||
params
|
||||
end
|
||||
|
||||
def rpc_add_node(host,port,ssl,user,pass,bypass_exist)
|
||||
|
||||
if not self.rpcarr
|
||||
self.rpcarr = Hash.new()
|
||||
end
|
||||
|
||||
begin
|
||||
istr = "#{host}|#{port}|#{ssl}|#{user}|#{pass}"
|
||||
if self.rpcarr.has_key?(istr) and not bypass_exist and self.rpcarr[istr] != nil
|
||||
print_error("Connection already exists #{istr}")
|
||||
else
|
||||
begin
|
||||
temprpc = ::Msf::RPC::Client.new(
|
||||
:host => host,
|
||||
:port => port,
|
||||
:ssl => ssl
|
||||
)
|
||||
rescue
|
||||
print_error "Unable to connect"
|
||||
#raise ConnectionError
|
||||
return
|
||||
end
|
||||
|
||||
res = temprpc.login( user , pass)
|
||||
|
||||
if not res
|
||||
print_error("Unable to authenticate to #{host}:#{port}.")
|
||||
return
|
||||
else
|
||||
res = temprpc.call('core.version')
|
||||
end
|
||||
|
||||
print_status("Connected to #{host}:#{port} [#{res['version']}].")
|
||||
self.rpcarr[istr] = temprpc
|
||||
end
|
||||
rescue
|
||||
print_error("Unable to connect")
|
||||
end
|
||||
end
|
||||
|
||||
def local_module_exec(mod,mtype, opts, nmaxjobs)
|
||||
jobify = false
|
||||
|
||||
modinst = framework.modules.create(mod)
|
||||
|
||||
if(not modinst)
|
||||
print_error("Unknown module")
|
||||
return
|
||||
end
|
||||
|
||||
sess = nil
|
||||
|
||||
case mtype
|
||||
when 'auxiliary'
|
||||
Msf::Simple::Auxiliary.run_simple(modinst, {
|
||||
'Action' => opts['ACTION'],
|
||||
'LocalOutput' => driver.output,
|
||||
'RunAsJob' => jobify,
|
||||
'Options' => opts
|
||||
})
|
||||
when 'exploit'
|
||||
if not opts['PAYLOAD']
|
||||
opts['PAYLOAD'] = WmapCommandDispatcher::Exploit.choose_payload(modinst, opts['TARGET'])
|
||||
end
|
||||
|
||||
sess = Msf::Simple::Exploit.exploit_simple(modinst, {
|
||||
'Payload' => opts['PAYLOAD'],
|
||||
'Target' => opts['TARGET'],
|
||||
'LocalOutput' => driver.output,
|
||||
'RunAsJob' => jobify,
|
||||
'Options' => opts
|
||||
})
|
||||
else
|
||||
print_error("Wrong mtype.")
|
||||
end
|
||||
|
||||
if sess
|
||||
if (jobify == false and sess.interactive?)
|
||||
print_line
|
||||
driver.run_single("sessions -q -i #{sess.sid}")
|
||||
else
|
||||
print_status("Session #{sess.sid} created in the background.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_round_exec(mod,mtype, opts, nmaxjobs)
|
||||
|
||||
res = nil
|
||||
idx = 0
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
if not self.runlocal
|
||||
print_error("All active nodes not working or removed")
|
||||
return
|
||||
end
|
||||
res = true
|
||||
else
|
||||
rpc_reconnect_nodes()
|
||||
end
|
||||
|
||||
if self.masstop
|
||||
return
|
||||
end
|
||||
|
||||
while not res
|
||||
if active_rpc_nodes == 0
|
||||
print_error("All active nodes not working or removed")
|
||||
return
|
||||
end
|
||||
|
||||
#find the node with less jobs load.
|
||||
minjobs = nmaxjobs
|
||||
minconn = nil
|
||||
nid = 0
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
if not rpccon
|
||||
print_error("Skipping inactive node #{nid} #{k}")
|
||||
else
|
||||
begin
|
||||
currentjobs = rpccon.call('job.list').length
|
||||
|
||||
if currentjobs < minjobs
|
||||
minconn = rpccon
|
||||
minjobs = currentjobs
|
||||
end
|
||||
|
||||
if currentjobs == nmaxjobs
|
||||
if self.nmaxdisplay == false
|
||||
print_error("Node #{nid} reached max number of jobs #{nmaxjobs}")
|
||||
print_error("Waiting for available node/slot...")
|
||||
self.nmaxdisplay = true
|
||||
end
|
||||
end
|
||||
#print_status("Node #{nid} #currentjobs #{currentjobs} #min #{minjobs}")
|
||||
rescue
|
||||
print_error("Unable to connect. Node #{tarr[0]}:#{tarr[1]}")
|
||||
self.rpcarr[k]=nil
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
print_error("All active nodes ,not working or removed")
|
||||
return
|
||||
else
|
||||
print_error("Sending job to next node")
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
nid += 1
|
||||
end
|
||||
|
||||
if minjobs < nmaxjobs
|
||||
res=minconn.call('module.execute', mtype, mod, opts)
|
||||
self.nmaxdisplay = false
|
||||
#print_status(">>>#{res} #{mod}")
|
||||
|
||||
if res
|
||||
if res.has_key?("job_id")
|
||||
return
|
||||
else
|
||||
print_error("Unable to execute module in node #{k} #{res}")
|
||||
end
|
||||
end
|
||||
else
|
||||
#print_status("Max number of jobs #{nmaxjobs} reached in node #{k}")
|
||||
end
|
||||
|
||||
idx += 1
|
||||
end
|
||||
|
||||
if self.runlocal and not self.masstop
|
||||
local_module_exec(mod,mtype, opts, nmaxjobs)
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_db_nodes(host,port,user,pass,name)
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
print_error("No active nodes at this time")
|
||||
return
|
||||
end
|
||||
|
||||
self.rpcarr.each do |k,v|
|
||||
if v
|
||||
res = v.call('db.driver',{:driver => 'postgresql'})
|
||||
res = v.call('db.connect',{:database => name, :host => host, :port => port, :username => user, :password => pass})
|
||||
res = v.call('db.status')
|
||||
|
||||
if res['db'] == name
|
||||
print_status("db_connect #{res} #{host}:#{port} OK")
|
||||
else
|
||||
print_error("Error db_connect #{res} #{host}:#{port}")
|
||||
end
|
||||
else
|
||||
print_error("No connection to node #{k}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_reconnect_nodes()
|
||||
begin
|
||||
# Sucky 5 mins token timeout.
|
||||
|
||||
idx = nil
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
if rpccon
|
||||
idx = k
|
||||
begin
|
||||
currentjobs = rpccon.call('job.list').length
|
||||
rescue
|
||||
tarr = k.split("|")
|
||||
rflag = false
|
||||
|
||||
res = rpccon.login(tarr[3],tarr[4])
|
||||
|
||||
if res
|
||||
rflag = true
|
||||
print_error("Reauth to node #{tarr[0]}:#{tarr[1]}")
|
||||
break
|
||||
else
|
||||
raise ConnectionError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
print_error("ERROR CONNECTING TO NODE. Disabling #{idx} use wmap_nodes -a to reconnect")
|
||||
self.rpcarr[idx] = nil
|
||||
if active_rpc_nodes == 0
|
||||
print_error("No active nodes")
|
||||
self.masstop = true
|
||||
else
|
||||
#blah
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_kill_node(i,j)
|
||||
|
||||
if not i
|
||||
print_error("Nodes not defined")
|
||||
return
|
||||
end
|
||||
|
||||
if not j
|
||||
print_error("Node jobs defined")
|
||||
return
|
||||
end
|
||||
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
print_error("No active nodes at this time")
|
||||
return
|
||||
end
|
||||
|
||||
idx=0
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
if idx == i.to_i or i.upcase == 'ALL'
|
||||
#begin
|
||||
if not rpccon
|
||||
print_error("No connection to node #{idx}")
|
||||
else
|
||||
n = rpccon.call('job.list')
|
||||
n.each do |id,name|
|
||||
if j==id.to_s or j.upcase == 'ALL'
|
||||
rpccon.call('job.stop',id)
|
||||
print_status("Node #{idx} Killed job id #{id} #{name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
#rescue
|
||||
# print_error("No connection")
|
||||
#end
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_view_jobs()
|
||||
indent = ' '
|
||||
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
print_error("No active nodes at this time")
|
||||
return
|
||||
end
|
||||
|
||||
idx=0
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
if not rpccon
|
||||
print_status("[Node ##{idx}: #{k} DISABLED/NO CONNECTION]")
|
||||
else
|
||||
|
||||
arrk = k.split('|')
|
||||
print_status("[Node ##{idx}: #{arrk[0]} Port:#{arrk[1]} SSL:#{arrk[2]} User:#{arrk[3]}]")
|
||||
|
||||
begin
|
||||
n = rpccon.call('job.list')
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => 'Jobs',
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Job name',
|
||||
'Target',
|
||||
'PATH',
|
||||
])
|
||||
|
||||
n.each do |id,name|
|
||||
jinfo = rpccon.call('job.info',id)
|
||||
dstore = jinfo['datastore']
|
||||
tbl << [ id.to_s, name,dstore['VHOST']+":"+dstore['RPORT'],dstore['PATH']]
|
||||
end
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
|
||||
rescue
|
||||
print_status("[Node ##{idx} #{k} DISABLED/NO CONNECTION]")
|
||||
end
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Modified from http://stackoverflow.com/questions/946738/detect-key-press-non-blocking-w-o-getc-gets-in-ruby
|
||||
def quit?
|
||||
begin
|
||||
while c = driver.input.read_nonblock(1)
|
||||
print_status("Quited")
|
||||
return true if c == 'Q'
|
||||
end
|
||||
false
|
||||
rescue Errno::EINTR
|
||||
false
|
||||
rescue Errno::EAGAIN
|
||||
false
|
||||
rescue EOFError
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_mon_nodes()
|
||||
# Pretty monitor
|
||||
|
||||
color = self.opts["ConsoleDriver"].output.supports_color? rescue false
|
||||
|
||||
colors = [
|
||||
'%grn',
|
||||
'%blu',
|
||||
'%yel',
|
||||
'%whi'
|
||||
]
|
||||
|
||||
#begin
|
||||
loop do
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
idx = 0
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
|
||||
arrk = k.split('|')
|
||||
|
||||
v = "NOCONN"
|
||||
n = 1
|
||||
c = '%red'
|
||||
|
||||
if not rpccon
|
||||
v = "NOCONN"
|
||||
n = 1
|
||||
c = '%red'
|
||||
else
|
||||
begin
|
||||
v = ""
|
||||
c = '%blu'
|
||||
rescue
|
||||
v = "ERROR"
|
||||
c = '%red'
|
||||
end
|
||||
|
||||
begin
|
||||
n = rpccon.call('job.list').length
|
||||
c = '%blu'
|
||||
rescue
|
||||
n = 1
|
||||
v = "NOCONN"
|
||||
c = '%red'
|
||||
end
|
||||
end
|
||||
|
||||
#begin
|
||||
if not @stdio
|
||||
@stdio = Rex::Ui::Text::Output::Stdio.new
|
||||
end
|
||||
|
||||
if color == true
|
||||
@stdio.auto_color
|
||||
else
|
||||
@stdio.disable_color
|
||||
end
|
||||
msg = "[#{idx}] #{"%bld#{c}||%clr"*n} #{n} #{v}\n"
|
||||
@stdio.print_raw(@stdio.substitute_colors(msg))
|
||||
|
||||
#rescue
|
||||
#blah
|
||||
#end
|
||||
sleep(2)
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
#rescue
|
||||
# print_status("End.")
|
||||
#end
|
||||
end
|
||||
|
||||
def rpc_list_nodes()
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => 'Nodes',
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Host',
|
||||
'Port',
|
||||
'SSL',
|
||||
'User',
|
||||
'Pass',
|
||||
'Status',
|
||||
'#jobs',
|
||||
])
|
||||
|
||||
idx=0
|
||||
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
|
||||
arrk = k.split('|')
|
||||
|
||||
if not rpccon
|
||||
v = "NOCONN"
|
||||
n = ""
|
||||
else
|
||||
begin
|
||||
v = rpccon.call('core.version')['version']
|
||||
rescue
|
||||
v = "ERROR"
|
||||
end
|
||||
|
||||
begin
|
||||
n = rpccon.call('job.list').length
|
||||
rescue
|
||||
n = ""
|
||||
end
|
||||
end
|
||||
|
||||
tbl << [ idx.to_s, arrk[0], arrk[1], arrk[2], arrk[3], arrk[4], v, n]
|
||||
idx += 1
|
||||
end
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
end
|
||||
|
||||
def active_rpc_nodes
|
||||
if self.rpcarr.length == 0
|
||||
return 0
|
||||
else
|
||||
idx = 0
|
||||
self.rpcarr.each do |k,conn|
|
||||
if conn
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
return idx
|
||||
end
|
||||
end
|
||||
|
||||
def view_modules
|
||||
indent = ' '
|
||||
|
||||
wmaptype = [:wmap_ssl,
|
||||
:wmap_server,
|
||||
:wmap_dir,
|
||||
:wmap_file,
|
||||
:wmap_unique_query,
|
||||
:wmap_query,
|
||||
:wmap_generic
|
||||
]
|
||||
|
||||
if not self.wmapmodules
|
||||
load_wmap_modules(true)
|
||||
end
|
||||
|
||||
wmaptype.each do |modt|
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => modt.to_s,
|
||||
'Columns' =>
|
||||
[
|
||||
'Name',
|
||||
'OrderID',
|
||||
])
|
||||
|
||||
idx = 0
|
||||
self.wmapmodules.each do |w|
|
||||
oid = w[3]
|
||||
if w[3] == 0xFFFFFF
|
||||
oid = ":last"
|
||||
end
|
||||
|
||||
if w[2] == modt
|
||||
tbl << [w[0],oid]
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
end
|
||||
end
|
||||
|
||||
# Yes sorting hashes dont make sense but actually it does when you are enumerating one. And
|
||||
# sort_by of a hash returns an array so this is the reason for this ugly piece of code
|
||||
def sort_by_orderid(m)
|
||||
temphash=Hash.new()
|
||||
temparr=[]
|
||||
|
||||
temparr = m.sort_by do |xref,v|
|
||||
xref[3]
|
||||
end
|
||||
|
||||
temparr.each do |b|
|
||||
temphash[b[0]] = b[1]
|
||||
end
|
||||
temphash
|
||||
end
|
||||
|
||||
# Load all wmap modules
|
||||
def load_wmap_modules(reload)
|
||||
if reload or not self.wmapmodules
|
||||
print_status("Loading wmap modules...")
|
||||
|
||||
self.wmapmodules=[]
|
||||
|
||||
idx = 0
|
||||
[ [ framework.auxiliary, 'auxiliary' ], [framework.exploits, 'exploit' ] ].each do |mtype|
|
||||
# Scan all exploit modules for matching references
|
||||
mtype[0].each_module do |n,m|
|
||||
e = m.new
|
||||
|
||||
# Only include wmap_enabled plugins
|
||||
if e.respond_to?("wmap_enabled")
|
||||
penabled = e.wmap_enabled
|
||||
|
||||
if penabled
|
||||
self.wmapmodules << [mtype[1]+'/'+n,mtype[1],e.wmap_type,e.orderid]
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
print_status("#{idx} wmap enabled modules loaded.")
|
||||
end
|
||||
end
|
||||
|
||||
def view_vulns
|
||||
framework.db.hosts.each do |host|
|
||||
host.services.each do |serv|
|
||||
serv.web_sites.each do |site|
|
||||
site.web_vulns.each do |wv|
|
||||
print_status("+ [#{host.address}] (#{site.vhost}): #{wv.category} #{wv.path}")
|
||||
print_status("\t#{wv.name} #{wv.description}")
|
||||
print_status("\t#{wv.method} #{wv.proof}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class WebTarget < ::Hash
|
||||
def to_url
|
||||
proto = self[:ssl] ? "https" : "http"
|
||||
"#{proto}://#{self[:host]}:#{self[:port]}#{self[:path]}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
color = self.opts["ConsoleDriver"].output.supports_color? rescue false
|
||||
|
||||
wmapversion = '1.5.1'
|
||||
|
||||
wmapbanner = "%red\n.-.-.-..-.-.-..---..---.%clr\n"
|
||||
wmapbanner += "%red| | | || | | || | || |-'%clr\n"
|
||||
wmapbanner += "%red`-----'`-'-'-'`-^-'`-'%clr\n"
|
||||
wmapbanner += "[WMAP #{wmapversion}] === et [ ] metasploit.com 2012\n"
|
||||
|
||||
if not @stdio
|
||||
@stdio = Rex::Ui::Text::Output::Stdio.new
|
||||
end
|
||||
|
||||
if color == true
|
||||
@stdio.auto_color
|
||||
else
|
||||
@stdio.disable_color
|
||||
end
|
||||
|
||||
@stdio.print_raw(@stdio.substitute_colors(wmapbanner))
|
||||
|
||||
add_console_dispatcher(WmapCommandDispatcher)
|
||||
#print_status("#{wmapbanner}")
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('wmap')
|
||||
end
|
||||
|
||||
def name
|
||||
"wmap"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Web assessment plugin"
|
||||
end
|
||||
class WmapCommandDispatcher
|
||||
|
||||
attr_accessor :wmapmodules # Enabled Wmap modules
|
||||
attr_accessor :targets # Targets
|
||||
attr_accessor :lastsites # Temp location of previously obtained sites
|
||||
attr_accessor :rpcarr # Array or rpc connections
|
||||
attr_accessor :njobs # Max number of jobs
|
||||
attr_accessor :nmaxdisplay # Flag to stop displaying the same mesg
|
||||
attr_accessor :runlocal # Flag to run local modules only
|
||||
attr_accessor :masstop # Flag to stop everything
|
||||
attr_accessor :killwhenstop # Kill process when exiting
|
||||
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
"wmap"
|
||||
end
|
||||
|
||||
#
|
||||
# The initial command set
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"wmap_targets" => "Manage targets",
|
||||
"wmap_sites" => "Manage sites",
|
||||
"wmap_nodes" => "Manage nodes",
|
||||
"wmap_run" => "Test targets",
|
||||
"wmap_modules" => "Manage wmap modules",
|
||||
"wmap_vulns" => "Display web vulns",
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_wmap_vulns(*args)
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-l'
|
||||
view_vulns
|
||||
return
|
||||
when '-h'
|
||||
print_status("Usage: wmap_vulns [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-l Display web vulns table")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cmd_wmap_modules(*args)
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-l'
|
||||
view_modules
|
||||
return
|
||||
when '-r'
|
||||
load_wmap_modules(true)
|
||||
return
|
||||
when '-h'
|
||||
print_status("Usage: wmap_modules [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-l List all wmap enabled modules")
|
||||
print_line("\t-r Reload wmap modules")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_wmap_targets(*args)
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-c'
|
||||
self.targets = Hash.new()
|
||||
when '-l'
|
||||
view_targets
|
||||
return
|
||||
when '-t'
|
||||
process_urls(args.shift)
|
||||
when '-d'
|
||||
process_ids(args.shift)
|
||||
when '-h'
|
||||
print_status("Usage: wmap_targets [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-t [urls] Define target sites (vhost1,url[space]vhost2,url) ")
|
||||
print_line("\t-d [ids] Define target sites (id1, id2, id3 ...)")
|
||||
print_line("\t-c Clean target sites list")
|
||||
print_line("\t-l List all target sites")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_wmap_sites(*args)
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-a'
|
||||
s = add_web_site(args.shift)
|
||||
if s
|
||||
print_status("Site created.")
|
||||
else
|
||||
print_error("Unable to create site")
|
||||
end
|
||||
when '-d'
|
||||
del_idx = args
|
||||
if del_idx
|
||||
delete_sites(del_idx.select {|d| d =~ /^[0-9]*$/}.map(&:to_i).uniq)
|
||||
return
|
||||
else
|
||||
print_error("Provide index of site to delete")
|
||||
end
|
||||
when '-l'
|
||||
view_sites
|
||||
return
|
||||
when '-s'
|
||||
u = args.shift
|
||||
l = args.shift
|
||||
s = args.shift
|
||||
|
||||
if not u
|
||||
return
|
||||
end
|
||||
|
||||
if l == nil or l.empty?
|
||||
l = 200
|
||||
s = true
|
||||
else
|
||||
l = l.to_i
|
||||
s = false
|
||||
end
|
||||
|
||||
if u.include? 'http'
|
||||
# Parameters are in url form
|
||||
view_site_tree(u,l,s)
|
||||
else
|
||||
# Parameters are digits
|
||||
if !self.lastsites or self.lastsites.length == 0
|
||||
view_sites
|
||||
print_status ("Web sites ids. referenced from previous table.")
|
||||
end
|
||||
|
||||
target_whitelist = []
|
||||
ids = u.to_s.split(/,/)
|
||||
|
||||
ids.each do |id|
|
||||
next if id.to_s.strip.empty?
|
||||
|
||||
if id.to_i > self.lastsites.length
|
||||
print_error("Skipping id #{id}...")
|
||||
else
|
||||
target_whitelist << self.lastsites[id.to_i]
|
||||
#print_status("Loading #{self.lastsites[id.to_i]}.")
|
||||
end
|
||||
end
|
||||
|
||||
# Skip the DB entirely if no matches
|
||||
return if target_whitelist.length == 0
|
||||
|
||||
if not self.targets
|
||||
self.targets = Hash.new()
|
||||
end
|
||||
|
||||
target_whitelist.each do |ent|
|
||||
view_site_tree(ent,l,s)
|
||||
end
|
||||
end
|
||||
return
|
||||
when '-h'
|
||||
print_status("Usage: wmap_sites [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-a [url] Add site (vhost,url)")
|
||||
print_line("\t-d [ids] Delete sites (separate ids with space)")
|
||||
print_line("\t-l List all available sites")
|
||||
print_line("\t-s [id] Display site structure (vhost,url|ids) (level)")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_wmap_nodes(*args)
|
||||
|
||||
if not self.rpcarr
|
||||
self.rpcarr=Hash.new()
|
||||
end
|
||||
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-a'
|
||||
h = args.shift
|
||||
r = args.shift
|
||||
s = args.shift
|
||||
u = args.shift
|
||||
p = args.shift
|
||||
|
||||
res = rpc_add_node(h,r,s,u,p,false)
|
||||
if res
|
||||
print_status("Node created.")
|
||||
else
|
||||
print_error("Unable to create node")
|
||||
end
|
||||
when '-c'
|
||||
idref = args.shift
|
||||
|
||||
if not idref
|
||||
print_error("No id defined")
|
||||
return
|
||||
end
|
||||
if idref.upcase == 'ALL'
|
||||
print_status("All nodes removed")
|
||||
self.rpcarr = Hash.new()
|
||||
else
|
||||
idx=0
|
||||
self.rpcarr.each do |k,v|
|
||||
if idx == idref.to_i
|
||||
self.rpcarr.delete(k)
|
||||
print_status("Node deleted #{k}")
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
when '-d'
|
||||
host = args.shift
|
||||
port = args.shift
|
||||
user = args.shift
|
||||
pass = args.shift
|
||||
dbname = args.shift
|
||||
|
||||
res = rpc_db_nodes(host,port,user,pass,dbname)
|
||||
if res
|
||||
print_status("OK.")
|
||||
else
|
||||
print_error("Error")
|
||||
end
|
||||
when '-l'
|
||||
rpc_list_nodes
|
||||
return
|
||||
when '-j'
|
||||
rpc_view_jobs
|
||||
return
|
||||
when '-k'
|
||||
node = args.shift
|
||||
jid = args.shift
|
||||
rpc_kill_node(node,jid)
|
||||
return
|
||||
when '-h'
|
||||
print_status("Usage: wmap_nodes [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-c id Remove id node (Use ALL for ALL nodes")
|
||||
print_line("\t-a host port ssl user pass Add node")
|
||||
print_line("\t-d host port user pass db Force all nodes to connect to db")
|
||||
print_line("\t-j View detailed jobs")
|
||||
print_line("\t-k ALL|id ALL|job_id Kill jobs on node")
|
||||
print_line("\t-l List all current nodes")
|
||||
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag.")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_wmap_run(*args)
|
||||
# Stop everything
|
||||
self.masstop = false
|
||||
self.killwhenstop = true
|
||||
|
||||
trap("INT") {
|
||||
print_error("Stopping execution...")
|
||||
self.masstop = true
|
||||
if self.killwhenstop
|
||||
rpc_kill_node('ALL','ALL')
|
||||
end
|
||||
}
|
||||
|
||||
# Max numbers of concurrent jobs per node
|
||||
self.njobs = 25
|
||||
self.nmaxdisplay = false
|
||||
self.runlocal = false
|
||||
|
||||
# Formating
|
||||
sizeline = 60
|
||||
|
||||
wmap_show = 2**0
|
||||
wmap_expl = 2**1
|
||||
|
||||
# Exclude files can be modified by setting datastore['WMAP_EXCLUDE']
|
||||
wmap_exclude_files = '.*\.(gif|jpg|png*)$'
|
||||
|
||||
run_wmap_ssl = true
|
||||
run_wmap_server = true
|
||||
run_wmap_dir_file = true
|
||||
run_wmap_query = true
|
||||
run_wmap_unique_query = true
|
||||
run_wmap_generic = true
|
||||
|
||||
# If module supports datastore['VERBOSE']
|
||||
moduleverbose = false
|
||||
|
||||
showprogress = false
|
||||
|
||||
if not self.rpcarr
|
||||
self.rpcarr = Hash.new()
|
||||
end
|
||||
|
||||
if not run_wmap_ssl
|
||||
print_status("Loading of wmap ssl modules disabled.")
|
||||
end
|
||||
if not run_wmap_server
|
||||
print_status("Loading of wmap server modules disabled.")
|
||||
end
|
||||
if not run_wmap_dir_file
|
||||
print_status("Loading of wmap dir and file modules disabled.")
|
||||
end
|
||||
if not run_wmap_query
|
||||
print_status("Loading of wmap query modules disabled.")
|
||||
end
|
||||
if not run_wmap_unique_query
|
||||
print_status("Loading of wmap unique query modules disabled.")
|
||||
end
|
||||
if not run_wmap_generic
|
||||
print_status("Loading of wmap generic modules disabled.")
|
||||
end
|
||||
|
||||
stamp = Time.now.to_f
|
||||
mode = 0
|
||||
|
||||
eprofile = []
|
||||
using_p = false
|
||||
using_m = false
|
||||
usinginipath = false
|
||||
|
||||
mname = ''
|
||||
inipathname = '/'
|
||||
|
||||
args.push("-h") if args.length == 0
|
||||
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-t'
|
||||
mode |= wmap_show
|
||||
when '-e'
|
||||
mode |= wmap_expl
|
||||
|
||||
profile = args.shift
|
||||
|
||||
if profile
|
||||
print_status("Using profile #{profile}.")
|
||||
|
||||
begin
|
||||
File.open(profile).each do |str|
|
||||
if not str.include? '#'
|
||||
# Not a comment
|
||||
modname = str.strip
|
||||
if not modname.empty?
|
||||
eprofile << modname
|
||||
end
|
||||
end
|
||||
using_p = true
|
||||
end
|
||||
rescue
|
||||
print_error("Profile not found or invalid.")
|
||||
return
|
||||
end
|
||||
else
|
||||
print_status("Using ALL wmap enabled modules.")
|
||||
end
|
||||
when '-m'
|
||||
mode |= wmap_expl
|
||||
|
||||
mname = args.shift
|
||||
|
||||
if mname
|
||||
print_status("Using module #{mname}.")
|
||||
end
|
||||
using_m = true
|
||||
when '-p'
|
||||
mode |= wmap_expl
|
||||
|
||||
inipathname = args.shift
|
||||
|
||||
if inipathname
|
||||
print_status("Using initial path #{inipathname}.")
|
||||
end
|
||||
usinginipath = true
|
||||
|
||||
when '-h'
|
||||
print_status("Usage: wmap_run [options]")
|
||||
print_line("\t-h Display this help text")
|
||||
print_line("\t-t Show all enabled modules")
|
||||
print_line("\t-m [regex] Launch only modules that name match provided regex.")
|
||||
print_line("\t-p [regex] Only test path defined by regex.")
|
||||
print_line("\t-e [/path/to/profile] Launch profile modules against all matched targets.")
|
||||
print_line("\t (No profile file runs all enabled modules.)")
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
print_error("Unknown flag")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if (self.rpcarr.length == 0) and (mode & wmap_show == 0)
|
||||
print_error("NO WMAP NODES DEFINED. Executing local modules")
|
||||
self.runlocal = true
|
||||
end
|
||||
|
||||
if self.targets == nil
|
||||
print_error("Targets have not been selected.")
|
||||
return
|
||||
end
|
||||
|
||||
if self.targets.keys.length == 0
|
||||
print_error("Targets have not been selected.")
|
||||
return
|
||||
end
|
||||
|
||||
execmod = true
|
||||
if (mode & wmap_show != 0)
|
||||
execmod = false
|
||||
end
|
||||
|
||||
self.targets.each_with_index do |t, idx|
|
||||
|
||||
selected_host = t[1][:host]
|
||||
selected_port = t[1][:port]
|
||||
selected_ssl = t[1][:ssl]
|
||||
selected_vhost = t[1][:vhost]
|
||||
|
||||
print_status ("Testing target:")
|
||||
print_status ("\tSite: #{selected_vhost} (#{selected_host})")
|
||||
print_status ("\tPort: #{selected_port} SSL: #{selected_ssl}")
|
||||
print_line '='* sizeline
|
||||
print_status("Testing started. #{(Time.now )}")
|
||||
|
||||
if not selected_ssl
|
||||
run_wmap_ssl = false
|
||||
#print_status ("Target is not SSL. SSL modules disabled.")
|
||||
end
|
||||
|
||||
# wmap_dir, wmap_file
|
||||
matches = Hash.new()
|
||||
|
||||
# wmap_server
|
||||
matches1 = Hash.new()
|
||||
|
||||
# wmap_query
|
||||
matches2 = Hash.new()
|
||||
|
||||
# wmap_ssl
|
||||
matches3 = Hash.new()
|
||||
|
||||
# wmap_unique_query
|
||||
matches5 = Hash.new()
|
||||
|
||||
# wmap_generic
|
||||
matches10 = Hash.new()
|
||||
|
||||
# OPTIONS
|
||||
opt_str = nil
|
||||
jobify = false
|
||||
|
||||
# This will be clean later
|
||||
load_wmap_modules(false)
|
||||
|
||||
self.wmapmodules.each do |w|
|
||||
case w[2]
|
||||
when :wmap_server
|
||||
if run_wmap_server
|
||||
matches1[w]=true
|
||||
end
|
||||
when :wmap_query
|
||||
if run_wmap_query
|
||||
matches2[w]=true
|
||||
end
|
||||
when :wmap_unique_query
|
||||
if run_wmap_unique_query
|
||||
matches5[w]=true
|
||||
end
|
||||
when :wmap_generic
|
||||
if run_wmap_generic
|
||||
matches10[w]=true
|
||||
end
|
||||
when :wmap_dir, :wmap_file
|
||||
if run_wmap_dir_file
|
||||
matches[w]=true
|
||||
end
|
||||
when :wmap_ssl
|
||||
if run_wmap_ssl
|
||||
matches3[w]=true
|
||||
end
|
||||
else
|
||||
# Black Hole
|
||||
end
|
||||
end
|
||||
|
||||
# Execution order (orderid)
|
||||
matches = sort_by_orderid(matches)
|
||||
matches1 = sort_by_orderid(matches1)
|
||||
matches2 = sort_by_orderid(matches2)
|
||||
matches3 = sort_by_orderid(matches3)
|
||||
matches5 = sort_by_orderid(matches5)
|
||||
matches10 = sort_by_orderid(matches10)
|
||||
|
||||
#
|
||||
# Handle modules that need to be run before all tests IF SERVER is SSL, once usually again the SSL web server.
|
||||
# :wmap_ssl
|
||||
#
|
||||
|
||||
print_status "\n=[ SSL testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
if not selected_ssl
|
||||
print_status ("Target is not SSL. SSL modules disabled.")
|
||||
end
|
||||
|
||||
idx = 0
|
||||
matches3.each_key do |xref|
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handle modules that need to be run before all tests, once usually again the web server.
|
||||
# :wmap_server
|
||||
#
|
||||
print_status "\n=[ Web Server testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches1.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handle modules to be run at every path/file
|
||||
# wmap_dir, wmap_file
|
||||
#
|
||||
print_status "\n=[ File/Dir testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx+=1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
#
|
||||
# Run the plugins that only need to be
|
||||
# launched once.
|
||||
#
|
||||
|
||||
wtype = xref[2]
|
||||
|
||||
h = self.framework.db.workspace.hosts.find_by_address(selected_host)
|
||||
s = h.services.find_by_port(selected_port)
|
||||
w = s.web_sites.find_by_vhost(selected_vhost)
|
||||
|
||||
test_tree = load_tree(w)
|
||||
test_tree.each do |node|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
p = node.current_path
|
||||
testpath = Pathname.new(p)
|
||||
strpath = testpath.cleanpath(false).to_s
|
||||
|
||||
#
|
||||
# Fixing paths
|
||||
#
|
||||
|
||||
if node.is_leaf? and not node.is_root?
|
||||
#
|
||||
# Later we can add here more checks to see if its a file
|
||||
#
|
||||
else
|
||||
if node.is_root?
|
||||
strpath = "/"
|
||||
else
|
||||
strpath = strpath.chomp + "/"
|
||||
end
|
||||
end
|
||||
|
||||
strpath = strpath.gsub("//", "/")
|
||||
#print_status("Testing path: #{strpath}")
|
||||
|
||||
#
|
||||
# Launch plugin depending module type.
|
||||
# Module type depends on main input type.
|
||||
# Code may be the same but it depend on final
|
||||
# versions of plugins
|
||||
#
|
||||
|
||||
case wtype
|
||||
when :wmap_file
|
||||
if node.is_leaf? and not node.is_root?
|
||||
#
|
||||
# Check if an exclusion regex has been defined
|
||||
#
|
||||
if self.framework.datastore['WMAP_EXCLUDE']
|
||||
excludefilestr = self.framework.datastore['WMAP_EXCLUDE']
|
||||
else
|
||||
excludefilestr = wmap_exclude_files
|
||||
end
|
||||
|
||||
if not strpath.match(excludefilestr)
|
||||
if (not usinginipath) or (usinginipath and strpath.match(inipathname))
|
||||
modopts['PATH'] = strpath
|
||||
print_status("Path: #{strpath}")
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
when :wmap_dir
|
||||
if (node.is_leaf? and not strpath.include? ".") or node.is_root? or not node.is_leaf?
|
||||
if (not usinginipath) or (usinginipath and strpath.match(inipathname))
|
||||
|
||||
modopts['PATH'] = strpath
|
||||
print_status("Path: #{strpath}")
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Run modules for each request to play with URI with UNIQUE query parameters.
|
||||
# wmap_unique_query
|
||||
#
|
||||
print_status "\n=[ Unique Query testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches5.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
#
|
||||
# Run the plugins for each request that have a distinct
|
||||
# GET/POST URI QUERY string.
|
||||
#
|
||||
|
||||
utest_query = Hash.new()
|
||||
|
||||
h = self.framework.db.workspace.hosts.find_by_address(selected_host)
|
||||
s = h.services.find_by_port(selected_port)
|
||||
w = s.web_sites.find_by_vhost(selected_vhost)
|
||||
|
||||
w.web_forms.each do |form|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
#
|
||||
# Only test unique query strings by comparing signature to previous tested signatures 'path,p1,p2,pn'
|
||||
#
|
||||
|
||||
datastr = ""
|
||||
typestr = ""
|
||||
|
||||
temparr = []
|
||||
|
||||
#print_status "---------"
|
||||
#print_status form.params
|
||||
#print_status "+++++++++"
|
||||
|
||||
form.params.each do |p|
|
||||
pn, pv, pt = p
|
||||
if pn
|
||||
if not pn.empty?
|
||||
if not pv or pv.empty?
|
||||
#TODO add value based on param name
|
||||
pv = "aaa"
|
||||
end
|
||||
|
||||
#temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s)
|
||||
temparr << pn.to_s + "=" + pv.to_s
|
||||
end
|
||||
else
|
||||
print_error("Blank parameter name. Form #{form.path}")
|
||||
end
|
||||
end
|
||||
|
||||
datastr = temparr.join("&") if (temparr and not temparr.empty?)
|
||||
|
||||
if (utest_query.has_key?(signature(form.path,datastr)) == false)
|
||||
|
||||
modopts['METHOD'] = form.method.upcase
|
||||
modopts['PATH'] = form.path
|
||||
modopts['QUERY'] = form.query
|
||||
if form.method.upcase == 'GET'
|
||||
modopts['QUERY'] = datastr
|
||||
modopts['DATA'] = ""
|
||||
end
|
||||
if form.method.upcase == 'POST'
|
||||
modopts['DATA'] = datastr
|
||||
end
|
||||
modopts['TYPES'] = typestr
|
||||
|
||||
#
|
||||
# TODO: Add headers, etc.
|
||||
#
|
||||
if (not usinginipath) or (usinginipath and form.path.match(inipathname))
|
||||
|
||||
print_status "Path #{form.path}"
|
||||
#print_status("Unique PATH #{modopts['PATH']}")
|
||||
#print_status("Unique GET #{modopts['QUERY']}")
|
||||
#print_status("Unique POST #{modopts['DATA']}")
|
||||
#print_status("MODOPTS: #{modopts}")
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
utest_query[signature(form.path,datastr)]=1
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
else
|
||||
#print_status("Already tested")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Run modules for each request to play with URI query parameters.
|
||||
# This approach will reduce the complexity of the Tree used before
|
||||
# and will make this shotgun implementation much simple.
|
||||
# wmap_query
|
||||
#
|
||||
print_status "\n=[ Query testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches2.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
#
|
||||
# Run the plugins for each request that have a distinct
|
||||
# GET/POST URI QUERY string.
|
||||
#
|
||||
|
||||
h = self.framework.db.workspace.hosts.find_by_address(selected_host)
|
||||
s = h.services.find_by_port(selected_port)
|
||||
w = s.web_sites.find_by_vhost(selected_vhost)
|
||||
|
||||
w.web_forms.each do |req|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
datastr = ""
|
||||
typestr = ""
|
||||
|
||||
temparr = []
|
||||
|
||||
req.params.each do |p|
|
||||
pn, pv, pt = p
|
||||
if pn
|
||||
if not pn.empty?
|
||||
if not pv or pv.empty?
|
||||
#TODO add value based on param name
|
||||
pv = "aaa"
|
||||
end
|
||||
#temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s)
|
||||
temparr << pn.to_s + "=" + pv.to_s
|
||||
end
|
||||
else
|
||||
print_error("Blank parameter name. Form #{req.path}")
|
||||
end
|
||||
end
|
||||
|
||||
datastr = temparr.join("&") if (temparr and not temparr.empty?)
|
||||
|
||||
modopts['METHOD'] = req.method.upcase
|
||||
modopts['PATH'] = req.path
|
||||
if req.method.upcase == 'GET'
|
||||
modopts['QUERY'] = datastr
|
||||
modopts['DATA'] = ""
|
||||
end
|
||||
modopts['DATA'] = datastr if req.method.upcase == 'POST'
|
||||
modopts['TYPES'] = typestr
|
||||
|
||||
#
|
||||
# TODO: Add method, headers, etc.
|
||||
#
|
||||
if (not usinginipath) or (usinginipath and req.path.match(inipathname))
|
||||
|
||||
print_status "Path #{req.path}"
|
||||
#print_status("Query PATH #{modopts['PATH']}")
|
||||
#print_status("Query GET #{modopts['QUERY']}")
|
||||
#print_status("Query POST #{modopts['DATA']}")
|
||||
#print_status("Query TYPES #{typestr}")
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handle modules that need to be after all tests, once.
|
||||
# Good place to have modules that analize the test results and/or
|
||||
# launch exploits.
|
||||
# :wmap_generic
|
||||
#
|
||||
print_status "\n=[ General testing ]="
|
||||
print_line "=" * sizeline
|
||||
|
||||
idx = 0
|
||||
matches10.each_key do |xref|
|
||||
|
||||
if self.masstop
|
||||
print_error("STOPPED.")
|
||||
return
|
||||
end
|
||||
|
||||
# Module not part of profile or not match
|
||||
if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p)
|
||||
idx += 1
|
||||
|
||||
|
||||
begin
|
||||
# Module options hash
|
||||
modopts = Hash.new()
|
||||
|
||||
#
|
||||
# The code is just a proof-of-concept and will be expanded in the future
|
||||
#
|
||||
|
||||
print_status "Module #{xref[0]}"
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
|
||||
#
|
||||
# For modules to have access to the global datastore
|
||||
# i.e. set -g DOMAIN test.com
|
||||
#
|
||||
self.framework.datastore.each do |gkey,gval|
|
||||
modopts[gkey]=gval
|
||||
end
|
||||
|
||||
#
|
||||
# Parameters passed in hash xref
|
||||
#
|
||||
|
||||
modopts['RHOST'] = selected_host
|
||||
modopts['RHOSTS'] = selected_host
|
||||
modopts['RPORT'] = selected_port.to_s
|
||||
modopts['SSL'] = selected_ssl
|
||||
modopts['VHOST'] = selected_vhost.to_s
|
||||
modopts['VERBOSE'] = moduleverbose
|
||||
modopts['ShowProgress'] = showprogress
|
||||
modopts['RunAsJob'] = jobify
|
||||
|
||||
#
|
||||
# Run the plugins that only need to be
|
||||
# launched once.
|
||||
#
|
||||
|
||||
begin
|
||||
if execmod
|
||||
rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs)
|
||||
end
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception during launch from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Exception
|
||||
print_status(" >> Exception from #{xref[0]}: #{$!}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if (mode & wmap_expl != 0)
|
||||
print_line "+" * sizeline
|
||||
|
||||
if not self.runlocal
|
||||
if execmod
|
||||
rpc_list_nodes()
|
||||
print_status("Note: Use wmap_nodes -l to list node status for completion")
|
||||
end
|
||||
end
|
||||
|
||||
print_line("Launch completed in #{(Time.now.to_f - stamp)} seconds.")
|
||||
print_line "+" * sizeline
|
||||
end
|
||||
|
||||
print_status("Done.")
|
||||
end
|
||||
|
||||
# EOM
|
||||
end
|
||||
|
||||
def view_targets
|
||||
if self.targets == nil or self.targets.keys.length == 0
|
||||
print_status "No targets have been defined"
|
||||
return
|
||||
end
|
||||
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => 'Defined targets',
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Vhost',
|
||||
'Host',
|
||||
'Port',
|
||||
'SSL',
|
||||
'Path',
|
||||
])
|
||||
|
||||
self.targets.each_with_index { |t, idx|
|
||||
tbl << [ idx.to_s, t[1][:vhost], t[1][:host], t[1][:port], t[1][:ssl], "\t"+t[1][:path].to_s ]
|
||||
}
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
end
|
||||
|
||||
def delete_sites(wmap_index)
|
||||
idx = 0
|
||||
to_del = {}
|
||||
# Rebuild the index from wmap_sites -l
|
||||
self.framework.db.hosts.each do |bdhost|
|
||||
bdhost.services.each do |serv|
|
||||
serv.web_sites.each do |web|
|
||||
# If the index of this site matches any deletion index,
|
||||
# add to our hash, saving the index for later output
|
||||
to_del[idx] = web if wmap_index.any? {|w| w.to_i == idx}
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
to_del.each do |widx,wsite|
|
||||
if wsite.delete
|
||||
print_status("Deleted #{wsite.vhost} on #{wsite.service.host.address} at index #{widx}")
|
||||
else
|
||||
print_error("Could note delete {wsite.vhost} on #{wsite.service.host.address} at index #{widx}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def view_sites
|
||||
# Clean temporary sites list
|
||||
self.lastsites = []
|
||||
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => 'Available sites',
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Host',
|
||||
'Vhost',
|
||||
'Port',
|
||||
'Proto',
|
||||
'# Pages',
|
||||
'# Forms',
|
||||
])
|
||||
|
||||
idx = 0
|
||||
self.framework.db.hosts.each do |bdhost|
|
||||
bdhost.services.each do |serv|
|
||||
serv.web_sites.each do |web|
|
||||
c = web.web_pages.count
|
||||
f = web.web_forms.count
|
||||
tbl << [ idx.to_s, bdhost.address, web.vhost, serv.port, serv.name, c.to_s, f.to_s ]
|
||||
idx += 1
|
||||
|
||||
turl = web.vhost + "," + serv.name + "://" +bdhost.address.to_s + ":" + serv.port.to_s + "/"
|
||||
self.lastsites << turl
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
|
||||
end
|
||||
|
||||
# Reusing code from hdmoore
|
||||
#
|
||||
# Allow the URL to be supplied as VHOST,URL if a custom VHOST
|
||||
# should be used. This allows for things like:
|
||||
# localhost,http://192.168.0.2/admin/
|
||||
|
||||
def add_web_site(url)
|
||||
|
||||
vhost = nil
|
||||
|
||||
# Allow the URL to be supplied as VHOST,URL if a custom VHOST
|
||||
# should be used. This allows for things like:
|
||||
# localhost,http://192.168.0.2/admin/
|
||||
|
||||
if url !~ /^http/
|
||||
vhost,url = url.split(",", 2)
|
||||
if url.to_s.empty?
|
||||
url = vhost
|
||||
vhost = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Prefix http:// when the URL has no specified parameter
|
||||
if url !~ /^[a-z0-9A-Z]+:\/\//
|
||||
url = "http://" + url
|
||||
end
|
||||
|
||||
uri = URI.parse(url) rescue nil
|
||||
if not uri
|
||||
print_error("Could not understand URL: #{url}")
|
||||
return
|
||||
end
|
||||
|
||||
if uri.scheme !~ /^https?/
|
||||
print_error("Only http and https URLs are accepted: #{url}")
|
||||
return
|
||||
end
|
||||
|
||||
ssl = false
|
||||
if uri.scheme == 'https'
|
||||
ssl = true
|
||||
end
|
||||
|
||||
site = self.framework.db.report_web_site(:wait => true, :host => uri.host, :port => uri.port, :vhost => vhost, :ssl => ssl)
|
||||
|
||||
return site
|
||||
end
|
||||
|
||||
# Code by hdm. Modified two lines by et
|
||||
#
|
||||
def process_urls(urlstr)
|
||||
|
||||
target_whitelist = []
|
||||
|
||||
urls = urlstr.to_s.split(/\s+/)
|
||||
|
||||
|
||||
urls.each do |url|
|
||||
next if url.to_s.strip.empty?
|
||||
vhost = nil
|
||||
|
||||
# Allow the URL to be supplied as VHOST,URL if a custom VHOST
|
||||
# should be used. This allows for things like:
|
||||
# localhost,http://192.168.0.2/admin/
|
||||
|
||||
if url !~ /^http/
|
||||
vhost,url = url.split(",", 2)
|
||||
if url.to_s.empty?
|
||||
url = vhost
|
||||
vhost = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Prefix http:// when the URL has no specified parameter
|
||||
if url !~ /^[a-z0-9A-Z]+:\/\//
|
||||
url = "http://" + url
|
||||
end
|
||||
|
||||
uri = URI.parse(url) rescue nil
|
||||
if not uri
|
||||
print_error("Could not understand URL: #{url}")
|
||||
next
|
||||
end
|
||||
|
||||
if uri.scheme !~ /^https?/
|
||||
print_error("Only http and https URLs are accepted: #{url}")
|
||||
next
|
||||
end
|
||||
|
||||
target_whitelist << [vhost || uri.host, uri]
|
||||
end
|
||||
|
||||
# Skip the DB entirely if no matches
|
||||
return if target_whitelist.length == 0
|
||||
|
||||
if not self.targets
|
||||
# First time targets are defined
|
||||
self.targets = Hash.new()
|
||||
end
|
||||
|
||||
target_whitelist.each do |ent|
|
||||
vhost,target = ent
|
||||
|
||||
host = self.framework.db.workspace.hosts.find_by_address(target.host)
|
||||
if not host
|
||||
print_error("No matching host for #{target.host}")
|
||||
next
|
||||
end
|
||||
serv = host.services.find_by_port_and_proto(target.port, 'tcp')
|
||||
if not serv
|
||||
print_error("No matching service for #{target.host}:#{target.port}")
|
||||
next
|
||||
end
|
||||
|
||||
#print_status "aaa"
|
||||
#print_status framework.db.workspace.name
|
||||
|
||||
sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id)
|
||||
|
||||
sites.each do |site|
|
||||
# Initial defaul path
|
||||
inipath = target.path
|
||||
if target.path.empty?
|
||||
inipath = '/'
|
||||
end
|
||||
|
||||
#site.web_forms.find_all_by_path(target.path).each do |form|
|
||||
ckey = [ site.vhost, host.address, serv.port, inipath].join("|")
|
||||
|
||||
if not self.targets[ckey]
|
||||
self.targets[ckey] = WebTarget.new
|
||||
self.targets[ckey].merge!({
|
||||
:vhost => site.vhost,
|
||||
:host => host.address,
|
||||
:port => serv.port,
|
||||
:ssl => (serv.name == "https"),
|
||||
:path => inipath
|
||||
})
|
||||
#self.targets[ckey][inipath] = []
|
||||
else
|
||||
print_status("Target already set in targets list.")
|
||||
end
|
||||
|
||||
# Store the form object in the hash for this path
|
||||
#self.targets[ckey][inipath] << inipath
|
||||
#end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Code by hdm. Modified two lines by et
|
||||
# lastsites contains a temporary array with vhost,url strings so the id can be
|
||||
# referenced in the array and prevent new sites added in the db to corrupt previous id list.
|
||||
def process_ids(idsstr)
|
||||
|
||||
if !self.lastsites or self.lastsites.length == 0
|
||||
view_sites
|
||||
print_status ("Web sites ids. referenced from previous table.")
|
||||
end
|
||||
|
||||
target_whitelist = []
|
||||
ids = idsstr.to_s.split(/,/)
|
||||
|
||||
ids.each do |id|
|
||||
next if id.to_s.strip.empty?
|
||||
|
||||
if id.to_i > self.lastsites.length
|
||||
print_error("Skipping id #{id}...")
|
||||
else
|
||||
target_whitelist << self.lastsites[id.to_i]
|
||||
print_status("Loading #{self.lastsites[id.to_i]}.")
|
||||
end
|
||||
end
|
||||
|
||||
# Skip the DB entirely if no matches
|
||||
return if target_whitelist.length == 0
|
||||
|
||||
if not self.targets
|
||||
self.targets = Hash.new()
|
||||
end
|
||||
|
||||
target_whitelist.each do |ent|
|
||||
process_urls(ent)
|
||||
end
|
||||
end
|
||||
|
||||
def view_site_tree(urlstr, md, ld)
|
||||
if not urlstr
|
||||
return
|
||||
end
|
||||
|
||||
site_whitelist = []
|
||||
|
||||
urls = urlstr.to_s.split(/\s+/)
|
||||
|
||||
urls.each do |url|
|
||||
next if url.to_s.strip.empty?
|
||||
vhost = nil
|
||||
|
||||
# Allow the URL to be supplied as VHOST,URL if a custom VHOST
|
||||
# should be used. This allows for things like:
|
||||
# localhost,http://192.168.0.2/admin/
|
||||
|
||||
if url !~ /^http/
|
||||
vhost,url = url.split(",", 2)
|
||||
|
||||
if url.to_s.empty?
|
||||
url = vhost
|
||||
vhost = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Prefix http:// when the URL has no specified parameter
|
||||
if url !~ /^[a-z0-9A-Z]+:\/\//
|
||||
url = "http://" + url
|
||||
end
|
||||
|
||||
uri = URI.parse(url) rescue nil
|
||||
if not uri
|
||||
print_error("Could not understand URL: #{url}")
|
||||
next
|
||||
end
|
||||
|
||||
if uri.scheme !~ /^https?/
|
||||
print_error("Only http and https URLs are accepted: #{url}")
|
||||
next
|
||||
end
|
||||
|
||||
site_whitelist << [vhost || uri.host, uri]
|
||||
end
|
||||
|
||||
# Skip the DB entirely if no matches
|
||||
return if site_whitelist.length == 0
|
||||
|
||||
vsites = Hash.new()
|
||||
|
||||
site_whitelist.each do |ent|
|
||||
vhost,target = ent
|
||||
|
||||
host = self.framework.db.workspace.hosts.find_by_address(target.host)
|
||||
if not host
|
||||
print_error("No matching host for #{target.host}")
|
||||
next
|
||||
end
|
||||
serv = host.services.find_by_port_and_proto(target.port, 'tcp')
|
||||
if not serv
|
||||
print_error("No matching service for #{target.host}:#{target.port}")
|
||||
next
|
||||
end
|
||||
|
||||
sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id)
|
||||
|
||||
sites.each do |site|
|
||||
t = load_tree(site)
|
||||
print_tree(t,target.host,md,ld)
|
||||
print_line("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Load website structure into a tree
|
||||
#
|
||||
|
||||
def load_tree(s)
|
||||
|
||||
pathchr = '/'
|
||||
|
||||
wtree = Tree.new(s.vhost)
|
||||
|
||||
# Load site pages
|
||||
s.web_pages.find(:all, :order => 'path').each do |req|
|
||||
tarray = req.path.to_s.split(pathchr)
|
||||
tarray.delete("")
|
||||
tpath = Pathname.new(pathchr)
|
||||
tarray.each do |df|
|
||||
wtree.add_at_path(tpath.to_s,df)
|
||||
tpath = tpath + Pathname.new(df.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Load site forms
|
||||
s.web_forms.each do |req|
|
||||
tarray = req.path.to_s.split(pathchr)
|
||||
tarray.delete("")
|
||||
tpath = Pathname.new(pathchr)
|
||||
tarray.each do |df|
|
||||
wtree.add_at_path(tpath.to_s,df)
|
||||
tpath = tpath + Pathname.new(df.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
return wtree
|
||||
end
|
||||
|
||||
#
|
||||
# Print Tree structure. Still ugly
|
||||
#
|
||||
|
||||
def print_tree(tree, ip, maxlevel, limitlevel)
|
||||
initab = " " * 4
|
||||
indent = 6
|
||||
if tree != nil and tree.depth <= maxlevel
|
||||
print initab + (" " * indent * tree.depth)
|
||||
if tree.depth > 0
|
||||
print "|"+("-" * (indent-1))+"/"
|
||||
end
|
||||
if tree.depth >= 0
|
||||
if tree.depth == 0
|
||||
print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n"
|
||||
|
||||
else
|
||||
c = tree.children.count
|
||||
if c > 0
|
||||
print tree.name + " (" + c.to_s+")\n"
|
||||
else
|
||||
print tree.name + "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tree.children.each_pair do |name,child|
|
||||
print_tree(child,ip,maxlevel,limitlevel)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def signature(fpath,fquery)
|
||||
hsig = Hash.new()
|
||||
|
||||
hsig = queryparse(fquery)
|
||||
|
||||
#
|
||||
# Signature of the form ',p1,p2,pn' then to be appended to path: path,p1,p2,pn
|
||||
#
|
||||
|
||||
sigstr = fpath + "," + hsig.map{|p| p[0].to_s}.join(",")
|
||||
end
|
||||
|
||||
def queryparse(query)
|
||||
params = Hash.new()
|
||||
|
||||
query.split(/[&;]/n).each do |pairs|
|
||||
key, value = pairs.split('=',2)
|
||||
if params.has_key?(key)
|
||||
#Error
|
||||
else
|
||||
params[key] = value
|
||||
end
|
||||
end
|
||||
params
|
||||
end
|
||||
|
||||
def rpc_add_node(host,port,ssl,user,pass,bypass_exist)
|
||||
|
||||
if not self.rpcarr
|
||||
self.rpcarr = Hash.new()
|
||||
end
|
||||
|
||||
begin
|
||||
istr = "#{host}|#{port}|#{ssl}|#{user}|#{pass}"
|
||||
if self.rpcarr.has_key?(istr) and not bypass_exist and self.rpcarr[istr] != nil
|
||||
print_error("Connection already exists #{istr}")
|
||||
else
|
||||
begin
|
||||
temprpc = ::Msf::RPC::Client.new(
|
||||
:host => host,
|
||||
:port => port,
|
||||
:ssl => ssl
|
||||
)
|
||||
rescue
|
||||
print_error "Unable to connect"
|
||||
#raise ConnectionError
|
||||
return
|
||||
end
|
||||
|
||||
res = temprpc.login( user , pass)
|
||||
|
||||
if not res
|
||||
print_error("Unable to authenticate to #{host}:#{port}.")
|
||||
return
|
||||
else
|
||||
res = temprpc.call('core.version')
|
||||
end
|
||||
|
||||
print_status("Connected to #{host}:#{port} [#{res['version']}].")
|
||||
self.rpcarr[istr] = temprpc
|
||||
end
|
||||
rescue
|
||||
print_error("Unable to connect")
|
||||
end
|
||||
end
|
||||
|
||||
def local_module_exec(mod,mtype, opts, nmaxjobs)
|
||||
jobify = false
|
||||
|
||||
modinst = framework.modules.create(mod)
|
||||
|
||||
if(not modinst)
|
||||
print_error("Unknown module")
|
||||
return
|
||||
end
|
||||
|
||||
sess = nil
|
||||
|
||||
case mtype
|
||||
when 'auxiliary'
|
||||
Msf::Simple::Auxiliary.run_simple(modinst, {
|
||||
'Action' => opts['ACTION'],
|
||||
'LocalOutput' => driver.output,
|
||||
'RunAsJob' => jobify,
|
||||
'Options' => opts
|
||||
})
|
||||
when 'exploit'
|
||||
if not opts['PAYLOAD']
|
||||
opts['PAYLOAD'] = WmapCommandDispatcher::Exploit.choose_payload(modinst, opts['TARGET'])
|
||||
end
|
||||
|
||||
sess = Msf::Simple::Exploit.exploit_simple(modinst, {
|
||||
'Payload' => opts['PAYLOAD'],
|
||||
'Target' => opts['TARGET'],
|
||||
'LocalOutput' => driver.output,
|
||||
'RunAsJob' => jobify,
|
||||
'Options' => opts
|
||||
})
|
||||
else
|
||||
print_error("Wrong mtype.")
|
||||
end
|
||||
|
||||
if sess
|
||||
if (jobify == false and sess.interactive?)
|
||||
print_line
|
||||
driver.run_single("sessions -q -i #{sess.sid}")
|
||||
else
|
||||
print_status("Session #{sess.sid} created in the background.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_round_exec(mod,mtype, opts, nmaxjobs)
|
||||
|
||||
res = nil
|
||||
idx = 0
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
if not self.runlocal
|
||||
print_error("All active nodes not working or removed")
|
||||
return
|
||||
end
|
||||
res = true
|
||||
else
|
||||
rpc_reconnect_nodes()
|
||||
end
|
||||
|
||||
if self.masstop
|
||||
return
|
||||
end
|
||||
|
||||
while not res
|
||||
if active_rpc_nodes == 0
|
||||
print_error("All active nodes not working or removed")
|
||||
return
|
||||
end
|
||||
|
||||
#find the node with less jobs load.
|
||||
minjobs = nmaxjobs
|
||||
minconn = nil
|
||||
nid = 0
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
if not rpccon
|
||||
print_error("Skipping inactive node #{nid} #{k}")
|
||||
else
|
||||
begin
|
||||
currentjobs = rpccon.call('job.list').length
|
||||
|
||||
if currentjobs < minjobs
|
||||
minconn = rpccon
|
||||
minjobs = currentjobs
|
||||
end
|
||||
|
||||
if currentjobs == nmaxjobs
|
||||
if self.nmaxdisplay == false
|
||||
print_error("Node #{nid} reached max number of jobs #{nmaxjobs}")
|
||||
print_error("Waiting for available node/slot...")
|
||||
self.nmaxdisplay = true
|
||||
end
|
||||
end
|
||||
#print_status("Node #{nid} #currentjobs #{currentjobs} #min #{minjobs}")
|
||||
rescue
|
||||
print_error("Unable to connect. Node #{tarr[0]}:#{tarr[1]}")
|
||||
self.rpcarr[k]=nil
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
print_error("All active nodes ,not working or removed")
|
||||
return
|
||||
else
|
||||
print_error("Sending job to next node")
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
nid += 1
|
||||
end
|
||||
|
||||
if minjobs < nmaxjobs
|
||||
res=minconn.call('module.execute', mtype, mod, opts)
|
||||
self.nmaxdisplay = false
|
||||
#print_status(">>>#{res} #{mod}")
|
||||
|
||||
if res
|
||||
if res.has_key?("job_id")
|
||||
return
|
||||
else
|
||||
print_error("Unable to execute module in node #{k} #{res}")
|
||||
end
|
||||
end
|
||||
else
|
||||
#print_status("Max number of jobs #{nmaxjobs} reached in node #{k}")
|
||||
end
|
||||
|
||||
idx += 1
|
||||
end
|
||||
|
||||
if self.runlocal and not self.masstop
|
||||
local_module_exec(mod,mtype, opts, nmaxjobs)
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_db_nodes(host,port,user,pass,name)
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
print_error("No active nodes at this time")
|
||||
return
|
||||
end
|
||||
|
||||
self.rpcarr.each do |k,v|
|
||||
if v
|
||||
res = v.call('db.driver',{:driver => 'postgresql'})
|
||||
res = v.call('db.connect',{:database => name, :host => host, :port => port, :username => user, :password => pass})
|
||||
res = v.call('db.status')
|
||||
|
||||
if res['db'] == name
|
||||
print_status("db_connect #{res} #{host}:#{port} OK")
|
||||
else
|
||||
print_error("Error db_connect #{res} #{host}:#{port}")
|
||||
end
|
||||
else
|
||||
print_error("No connection to node #{k}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_reconnect_nodes()
|
||||
begin
|
||||
# Sucky 5 mins token timeout.
|
||||
|
||||
idx = nil
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
if rpccon
|
||||
idx = k
|
||||
begin
|
||||
currentjobs = rpccon.call('job.list').length
|
||||
rescue
|
||||
tarr = k.split("|")
|
||||
rflag = false
|
||||
|
||||
res = rpccon.login(tarr[3],tarr[4])
|
||||
|
||||
if res
|
||||
rflag = true
|
||||
print_error("Reauth to node #{tarr[0]}:#{tarr[1]}")
|
||||
break
|
||||
else
|
||||
raise ConnectionError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
print_error("ERROR CONNECTING TO NODE. Disabling #{idx} use wmap_nodes -a to reconnect")
|
||||
self.rpcarr[idx] = nil
|
||||
if active_rpc_nodes == 0
|
||||
print_error("No active nodes")
|
||||
self.masstop = true
|
||||
else
|
||||
#blah
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_kill_node(i,j)
|
||||
|
||||
if not i
|
||||
print_error("Nodes not defined")
|
||||
return
|
||||
end
|
||||
|
||||
if not j
|
||||
print_error("Node jobs defined")
|
||||
return
|
||||
end
|
||||
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
print_error("No active nodes at this time")
|
||||
return
|
||||
end
|
||||
|
||||
idx=0
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
if idx == i.to_i or i.upcase == 'ALL'
|
||||
#begin
|
||||
if not rpccon
|
||||
print_error("No connection to node #{idx}")
|
||||
else
|
||||
n = rpccon.call('job.list')
|
||||
n.each do |id,name|
|
||||
if j==id.to_s or j.upcase == 'ALL'
|
||||
rpccon.call('job.stop',id)
|
||||
print_status("Node #{idx} Killed job id #{id} #{name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
#rescue
|
||||
# print_error("No connection")
|
||||
#end
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_view_jobs()
|
||||
indent = ' '
|
||||
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
if active_rpc_nodes == 0
|
||||
print_error("No active nodes at this time")
|
||||
return
|
||||
end
|
||||
|
||||
idx=0
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
if not rpccon
|
||||
print_status("[Node ##{idx}: #{k} DISABLED/NO CONNECTION]")
|
||||
else
|
||||
|
||||
arrk = k.split('|')
|
||||
print_status("[Node ##{idx}: #{arrk[0]} Port:#{arrk[1]} SSL:#{arrk[2]} User:#{arrk[3]}]")
|
||||
|
||||
begin
|
||||
n = rpccon.call('job.list')
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => 'Jobs',
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Job name',
|
||||
'Target',
|
||||
'PATH',
|
||||
])
|
||||
|
||||
n.each do |id,name|
|
||||
jinfo = rpccon.call('job.info',id)
|
||||
dstore = jinfo['datastore']
|
||||
tbl << [ id.to_s, name,dstore['VHOST']+":"+dstore['RPORT'],dstore['PATH']]
|
||||
end
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
|
||||
rescue
|
||||
print_status("[Node ##{idx} #{k} DISABLED/NO CONNECTION]")
|
||||
end
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Modified from http://stackoverflow.com/questions/946738/detect-key-press-non-blocking-w-o-getc-gets-in-ruby
|
||||
def quit?
|
||||
begin
|
||||
while c = driver.input.read_nonblock(1)
|
||||
print_status("Quited")
|
||||
return true if c == 'Q'
|
||||
end
|
||||
false
|
||||
rescue Errno::EINTR
|
||||
false
|
||||
rescue Errno::EAGAIN
|
||||
false
|
||||
rescue EOFError
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_mon_nodes()
|
||||
# Pretty monitor
|
||||
|
||||
color = self.opts["ConsoleDriver"].output.supports_color? rescue false
|
||||
|
||||
colors = [
|
||||
'%grn',
|
||||
'%blu',
|
||||
'%yel',
|
||||
'%whi'
|
||||
]
|
||||
|
||||
#begin
|
||||
loop do
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
idx = 0
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
|
||||
arrk = k.split('|')
|
||||
|
||||
v = "NOCONN"
|
||||
n = 1
|
||||
c = '%red'
|
||||
|
||||
if not rpccon
|
||||
v = "NOCONN"
|
||||
n = 1
|
||||
c = '%red'
|
||||
else
|
||||
begin
|
||||
v = ""
|
||||
c = '%blu'
|
||||
rescue
|
||||
v = "ERROR"
|
||||
c = '%red'
|
||||
end
|
||||
|
||||
begin
|
||||
n = rpccon.call('job.list').length
|
||||
c = '%blu'
|
||||
rescue
|
||||
n = 1
|
||||
v = "NOCONN"
|
||||
c = '%red'
|
||||
end
|
||||
end
|
||||
|
||||
#begin
|
||||
if not @stdio
|
||||
@stdio = Rex::Ui::Text::Output::Stdio.new
|
||||
end
|
||||
|
||||
if color == true
|
||||
@stdio.auto_color
|
||||
else
|
||||
@stdio.disable_color
|
||||
end
|
||||
msg = "[#{idx}] #{"%bld#{c}||%clr"*n} #{n} #{v}\n"
|
||||
@stdio.print_raw(@stdio.substitute_colors(msg))
|
||||
|
||||
#rescue
|
||||
#blah
|
||||
#end
|
||||
sleep(2)
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
#rescue
|
||||
# print_status("End.")
|
||||
#end
|
||||
end
|
||||
|
||||
def rpc_list_nodes()
|
||||
indent = ' '
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => 'Nodes',
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Host',
|
||||
'Port',
|
||||
'SSL',
|
||||
'User',
|
||||
'Pass',
|
||||
'Status',
|
||||
'#jobs',
|
||||
])
|
||||
|
||||
idx=0
|
||||
|
||||
rpc_reconnect_nodes()
|
||||
|
||||
self.rpcarr.each do |k,rpccon|
|
||||
|
||||
arrk = k.split('|')
|
||||
|
||||
if not rpccon
|
||||
v = "NOCONN"
|
||||
n = ""
|
||||
else
|
||||
begin
|
||||
v = rpccon.call('core.version')['version']
|
||||
rescue
|
||||
v = "ERROR"
|
||||
end
|
||||
|
||||
begin
|
||||
n = rpccon.call('job.list').length
|
||||
rescue
|
||||
n = ""
|
||||
end
|
||||
end
|
||||
|
||||
tbl << [ idx.to_s, arrk[0], arrk[1], arrk[2], arrk[3], arrk[4], v, n]
|
||||
idx += 1
|
||||
end
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
end
|
||||
|
||||
def active_rpc_nodes
|
||||
if self.rpcarr.length == 0
|
||||
return 0
|
||||
else
|
||||
idx = 0
|
||||
self.rpcarr.each do |k,conn|
|
||||
if conn
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
return idx
|
||||
end
|
||||
end
|
||||
|
||||
def view_modules
|
||||
indent = ' '
|
||||
|
||||
wmaptype = [:wmap_ssl,
|
||||
:wmap_server,
|
||||
:wmap_dir,
|
||||
:wmap_file,
|
||||
:wmap_unique_query,
|
||||
:wmap_query,
|
||||
:wmap_generic
|
||||
]
|
||||
|
||||
if not self.wmapmodules
|
||||
load_wmap_modules(true)
|
||||
end
|
||||
|
||||
wmaptype.each do |modt|
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => modt.to_s,
|
||||
'Columns' =>
|
||||
[
|
||||
'Name',
|
||||
'OrderID',
|
||||
])
|
||||
|
||||
idx = 0
|
||||
self.wmapmodules.each do |w|
|
||||
oid = w[3]
|
||||
if w[3] == 0xFFFFFF
|
||||
oid = ":last"
|
||||
end
|
||||
|
||||
if w[2] == modt
|
||||
tbl << [w[0],oid]
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
|
||||
print_status tbl.to_s + "\n"
|
||||
end
|
||||
end
|
||||
|
||||
# Yes sorting hashes dont make sense but actually it does when you are enumerating one. And
|
||||
# sort_by of a hash returns an array so this is the reason for this ugly piece of code
|
||||
def sort_by_orderid(m)
|
||||
temphash=Hash.new()
|
||||
temparr=[]
|
||||
|
||||
temparr = m.sort_by do |xref,v|
|
||||
xref[3]
|
||||
end
|
||||
|
||||
temparr.each do |b|
|
||||
temphash[b[0]] = b[1]
|
||||
end
|
||||
temphash
|
||||
end
|
||||
|
||||
# Load all wmap modules
|
||||
def load_wmap_modules(reload)
|
||||
if reload or not self.wmapmodules
|
||||
print_status("Loading wmap modules...")
|
||||
|
||||
self.wmapmodules=[]
|
||||
|
||||
idx = 0
|
||||
[ [ framework.auxiliary, 'auxiliary' ], [framework.exploits, 'exploit' ] ].each do |mtype|
|
||||
# Scan all exploit modules for matching references
|
||||
mtype[0].each_module do |n,m|
|
||||
e = m.new
|
||||
|
||||
# Only include wmap_enabled plugins
|
||||
if e.respond_to?("wmap_enabled")
|
||||
penabled = e.wmap_enabled
|
||||
|
||||
if penabled
|
||||
self.wmapmodules << [mtype[1]+'/'+n,mtype[1],e.wmap_type,e.orderid]
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
print_status("#{idx} wmap enabled modules loaded.")
|
||||
end
|
||||
end
|
||||
|
||||
def view_vulns
|
||||
framework.db.hosts.each do |host|
|
||||
host.services.each do |serv|
|
||||
serv.web_sites.each do |site|
|
||||
site.web_vulns.each do |wv|
|
||||
print_status("+ [#{host.address}] (#{site.vhost}): #{wv.category} #{wv.path}")
|
||||
print_status("\t#{wv.name} #{wv.description}")
|
||||
print_status("\t#{wv.method} #{wv.proof}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class WebTarget < ::Hash
|
||||
def to_url
|
||||
proto = self[:ssl] ? "https" : "http"
|
||||
"#{proto}://#{self[:host]}:#{self[:port]}#{self[:path]}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
color = self.opts["ConsoleDriver"].output.supports_color? rescue false
|
||||
|
||||
wmapversion = '1.5.1'
|
||||
|
||||
wmapbanner = "%red\n.-.-.-..-.-.-..---..---.%clr\n"
|
||||
wmapbanner += "%red| | | || | | || | || |-'%clr\n"
|
||||
wmapbanner += "%red`-----'`-'-'-'`-^-'`-'%clr\n"
|
||||
wmapbanner += "[WMAP #{wmapversion}] === et [ ] metasploit.com 2012\n"
|
||||
|
||||
if not @stdio
|
||||
@stdio = Rex::Ui::Text::Output::Stdio.new
|
||||
end
|
||||
|
||||
if color == true
|
||||
@stdio.auto_color
|
||||
else
|
||||
@stdio.disable_color
|
||||
end
|
||||
|
||||
@stdio.print_raw(@stdio.substitute_colors(wmapbanner))
|
||||
|
||||
add_console_dispatcher(WmapCommandDispatcher)
|
||||
#print_status("#{wmapbanner}")
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('wmap')
|
||||
end
|
||||
|
||||
def name
|
||||
"wmap"
|
||||
end
|
||||
|
||||
def desc
|
||||
"Web assessment plugin"
|
||||
end
|
||||
|
||||
protected
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user