2009-11-05 20:26:28 +00:00
|
|
|
|
2011-05-12 20:03:55 +00:00
|
|
|
require 'rexml/document'
|
|
|
|
require 'rex/parser/nmap_xml'
|
2010-10-17 04:50:15 +00:00
|
|
|
require 'msf/core/db_export'
|
2009-11-05 20:26:28 +00:00
|
|
|
|
2006-05-30 15:44:48 +00:00
|
|
|
module Msf
|
|
|
|
module Ui
|
|
|
|
module Console
|
|
|
|
module CommandDispatcher
|
2009-03-28 21:42:30 +00:00
|
|
|
class Db
|
2006-05-30 15:44:48 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
require 'tempfile'
|
2009-03-28 21:42:30 +00:00
|
|
|
|
|
|
|
include Msf::Ui::Console::CommandDispatcher
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
#
|
|
|
|
# Constants
|
|
|
|
#
|
|
|
|
|
2006-12-10 08:21:52 +00:00
|
|
|
PWN_SHOW = 2**0
|
|
|
|
PWN_XREF = 2**1
|
|
|
|
PWN_PORT = 2**2
|
|
|
|
PWN_EXPL = 2**3
|
|
|
|
PWN_SING = 2**4
|
2008-11-18 22:01:15 +00:00
|
|
|
PWN_SLNT = 2**5
|
2009-12-03 01:36:17 +00:00
|
|
|
PWN_VERB = 2**6
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-05-30 15:44:48 +00:00
|
|
|
#
|
|
|
|
# The dispatcher's name.
|
|
|
|
#
|
|
|
|
def name
|
|
|
|
"Database Backend"
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Returns the hash of commands supported by this dispatcher.
|
|
|
|
#
|
|
|
|
def commands
|
2009-03-28 21:42:30 +00:00
|
|
|
base = {
|
|
|
|
"db_driver" => "Specify a database driver",
|
|
|
|
"db_connect" => "Connect to an existing database",
|
|
|
|
"db_disconnect" => "Disconnect from the current database instance",
|
2010-05-10 09:23:53 +00:00
|
|
|
"db_status" => "Show the current database status",
|
2009-03-28 21:42:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
more = {
|
2011-07-20 08:39:35 +00:00
|
|
|
"workspace" => "Switch between database workspaces",
|
|
|
|
"hosts" => "List all hosts in the database",
|
|
|
|
"services" => "List all services in the database",
|
|
|
|
"vulns" => "List all vulnerabilities in the database",
|
|
|
|
"notes" => "List all notes in the database",
|
2011-08-12 19:09:05 +00:00
|
|
|
"loot" => "List all loot in the database",
|
2011-07-20 08:39:35 +00:00
|
|
|
"creds" => "List all credentials in the database",
|
2009-03-28 21:42:30 +00:00
|
|
|
"db_autopwn" => "Automatically exploit everything",
|
2010-01-07 19:06:29 +00:00
|
|
|
"db_import" => "Import a scan result file (filetype will be auto-detected)",
|
2011-07-19 17:41:20 +00:00
|
|
|
"db_export" => "Export a file containing the contents of the database",
|
|
|
|
"db_nmap" => "Executes nmap and records the output automatically",
|
2006-05-30 15:44:48 +00:00
|
|
|
}
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2011-04-16 22:49:31 +00:00
|
|
|
# Always include commands that only make sense when connected.
|
|
|
|
# This avoids the problem of them disappearing unexpectedly if the
|
|
|
|
# database dies or times out. See #1923
|
2010-05-25 01:32:30 +00:00
|
|
|
base.merge(more)
|
|
|
|
end
|
|
|
|
|
2011-04-16 22:49:31 +00:00
|
|
|
#
|
|
|
|
# Returns true if the db is connected, prints an error and returns
|
|
|
|
# false if not.
|
|
|
|
#
|
|
|
|
# All commands that require an active database should call this before
|
|
|
|
# doing anything.
|
|
|
|
#
|
2010-05-25 01:32:30 +00:00
|
|
|
def active?
|
|
|
|
if not framework.db.active
|
|
|
|
print_error("Database not connected")
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
true
|
2006-05-30 15:44:48 +00:00
|
|
|
end
|
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_workspace_help
|
2011-04-16 22:49:31 +00:00
|
|
|
print_line "Usage:"
|
2011-07-20 08:39:35 +00:00
|
|
|
print_line " workspace List workspaces"
|
|
|
|
print_line " workspace [name] Switch workspace"
|
|
|
|
print_line " workspace -a [name] ... Add workspace(s)"
|
|
|
|
print_line " workspace -d [name] ... Delete workspace(s)"
|
|
|
|
print_line " workspace -h Show this help information"
|
2011-04-16 22:49:31 +00:00
|
|
|
print_line
|
|
|
|
end
|
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_workspace(*args)
|
2010-05-25 01:32:30 +00:00
|
|
|
return unless active?
|
2009-12-14 22:52:34 +00:00
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
|
|
|
when '-h','--help'
|
2011-07-20 08:39:35 +00:00
|
|
|
cmd_workspace_help
|
2009-12-14 22:52:34 +00:00
|
|
|
return
|
|
|
|
when '-a','--add'
|
|
|
|
adding = true
|
|
|
|
when '-d','--del'
|
|
|
|
deleting = true
|
|
|
|
else
|
2010-10-15 18:06:21 +00:00
|
|
|
names ||= []
|
|
|
|
names << arg
|
2009-12-14 22:52:34 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-10-15 18:06:21 +00:00
|
|
|
if adding and names
|
|
|
|
# Add workspaces
|
|
|
|
workspace = nil
|
|
|
|
names.each do |name|
|
2010-01-15 15:58:13 +00:00
|
|
|
workspace = framework.db.add_workspace(name)
|
2010-10-15 18:06:21 +00:00
|
|
|
print_status("Added workspace: #{workspace.name}")
|
|
|
|
end
|
|
|
|
framework.db.workspace = workspace
|
|
|
|
elsif deleting and names
|
|
|
|
# Delete workspaces
|
|
|
|
names.each do |name|
|
|
|
|
workspace = framework.db.find_workspace(name)
|
|
|
|
if workspace.nil?
|
|
|
|
print_error("Workspace not found: #{name}")
|
|
|
|
elsif workspace.default?
|
|
|
|
workspace.destroy
|
|
|
|
workspace = framework.db.add_workspace(name)
|
|
|
|
print_status("Deleted and recreated the default workspace")
|
|
|
|
else
|
|
|
|
# switch to the default workspace if we're about to delete the current one
|
|
|
|
framework.db.workspace = framework.db.default_workspace if framework.db.workspace.name == workspace.name
|
|
|
|
# now destroy the named workspace
|
|
|
|
workspace.destroy
|
|
|
|
print_status("Deleted workspace: #{name}")
|
|
|
|
end
|
2009-12-14 22:52:34 +00:00
|
|
|
end
|
2010-10-15 18:06:21 +00:00
|
|
|
elsif names
|
|
|
|
name = names.last
|
2009-12-14 22:52:34 +00:00
|
|
|
# Switch workspace
|
|
|
|
workspace = framework.db.find_workspace(name)
|
|
|
|
if workspace
|
|
|
|
framework.db.workspace = workspace
|
|
|
|
print_status("Workspace: #{workspace.name}")
|
|
|
|
else
|
|
|
|
print_error("Workspace not found: #{name}")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
else
|
|
|
|
# List workspaces
|
|
|
|
framework.db.workspaces.each do |s|
|
2009-12-23 20:37:30 +00:00
|
|
|
pad = (s.name == framework.db.workspace.name) ? "* " : " "
|
2009-12-14 22:52:34 +00:00
|
|
|
print_line(pad + s.name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_workspace_tabs(str, words)
|
2010-10-15 18:06:21 +00:00
|
|
|
return [] unless active?
|
2009-12-14 22:52:34 +00:00
|
|
|
framework.db.workspaces.map { |s| s.name } if (words & ['-a','--add']).empty?
|
|
|
|
end
|
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_hosts_help
|
2011-04-16 22:49:31 +00:00
|
|
|
# This command does some lookups for the list of appropriate column
|
|
|
|
# names, so instead of putting all the usage stuff here like other
|
|
|
|
# help methods, just use it's "-h" so we don't have to recreating
|
|
|
|
# that list
|
2011-07-20 08:39:35 +00:00
|
|
|
cmd_hosts("-h")
|
2011-04-16 22:49:31 +00:00
|
|
|
end
|
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_hosts(*args)
|
2010-05-25 01:32:30 +00:00
|
|
|
return unless active?
|
2009-11-06 21:08:34 +00:00
|
|
|
onlyup = false
|
2009-12-18 01:14:05 +00:00
|
|
|
host_search = nil
|
2011-04-22 05:08:08 +00:00
|
|
|
set_rhosts = false
|
2011-07-19 06:09:10 +00:00
|
|
|
mode = :search
|
2011-07-20 03:58:25 +00:00
|
|
|
delete_count = 0
|
|
|
|
|
|
|
|
host_ranges = []
|
2011-02-26 17:42:03 +00:00
|
|
|
|
2010-05-30 13:49:28 +00:00
|
|
|
output = nil
|
2009-12-22 22:20:58 +00:00
|
|
|
default_columns = ::Msf::DBManager::Host.column_names.sort
|
2010-07-06 18:18:26 +00:00
|
|
|
virtual_columns = [ 'svcs', 'vulns', 'workspace' ]
|
2011-02-26 17:42:03 +00:00
|
|
|
|
2011-08-12 19:09:05 +00:00
|
|
|
col_search = [ 'address', 'mac', 'name', 'os_name', 'os_flavor', 'os_sp', 'purpose', 'info', 'comments' ]
|
|
|
|
|
2009-12-22 22:20:58 +00:00
|
|
|
default_columns.delete_if {|v| (v[-2,2] == "id")}
|
2009-11-06 21:08:34 +00:00
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
2011-07-19 06:09:10 +00:00
|
|
|
when '-a','--add'
|
|
|
|
mode = :add
|
2011-07-19 08:13:55 +00:00
|
|
|
when '-d','--delete'
|
|
|
|
mode = :delete
|
2009-12-22 22:20:58 +00:00
|
|
|
when '-c'
|
|
|
|
list = args.shift
|
|
|
|
if(!list)
|
|
|
|
print_error("Invalid column list")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
col_search = list.strip().split(",")
|
|
|
|
col_search.each { |c|
|
2011-03-02 17:12:10 +00:00
|
|
|
if not default_columns.include?(c) and not virtual_columns.include?(c)
|
2010-07-06 18:18:26 +00:00
|
|
|
all_columns = default_columns + virtual_columns
|
|
|
|
print_error("Invalid column list. Possible values are (#{all_columns.join("|")})")
|
2009-12-22 22:20:58 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
}
|
2009-11-06 21:08:34 +00:00
|
|
|
when '-u','--up'
|
|
|
|
onlyup = true
|
2010-05-30 13:49:28 +00:00
|
|
|
when '-o'
|
|
|
|
output = args.shift
|
2011-04-22 05:08:08 +00:00
|
|
|
when '-R','--rhosts'
|
|
|
|
set_rhosts = true
|
|
|
|
rhosts = []
|
|
|
|
|
2009-11-06 21:08:34 +00:00
|
|
|
when '-h','--help'
|
2011-07-20 08:39:35 +00:00
|
|
|
print_line "Usage: hosts [ options ] [addr1 addr2 ...]"
|
2009-12-22 22:20:58 +00:00
|
|
|
print_line
|
2011-06-14 22:17:07 +00:00
|
|
|
print_line "OPTIONS:"
|
2011-07-19 06:09:10 +00:00
|
|
|
print_line " -a,--add Add the hosts instead of searching"
|
2011-07-19 08:13:55 +00:00
|
|
|
print_line " -d,--delete Delete the hosts instead of searching"
|
2011-06-14 22:17:07 +00:00
|
|
|
print_line " -c <col1,col2> Only show the given columns (see list below)"
|
2009-12-22 22:20:58 +00:00
|
|
|
print_line " -h,--help Show this help information"
|
|
|
|
print_line " -u,--up Only show hosts which are up"
|
2010-07-22 16:53:11 +00:00
|
|
|
print_line " -o <file> Send output to a file in csv format"
|
2011-04-22 05:08:08 +00:00
|
|
|
print_line " -R,--rhosts Set RHOSTS from the results of the search"
|
2009-12-22 22:20:58 +00:00
|
|
|
print_line
|
|
|
|
print_line "Available columns: #{default_columns.join(", ")}"
|
|
|
|
print_line
|
2009-11-06 21:08:34 +00:00
|
|
|
return
|
2011-06-14 22:17:07 +00:00
|
|
|
else
|
2011-07-20 03:58:25 +00:00
|
|
|
# Anything that wasn't an option is a host to search for
|
|
|
|
unless (arg_host_range(arg, host_ranges))
|
|
|
|
return
|
|
|
|
end
|
2009-11-06 21:08:34 +00:00
|
|
|
end
|
2006-05-30 15:44:48 +00:00
|
|
|
end
|
2009-11-06 21:08:34 +00:00
|
|
|
|
2009-12-22 22:20:58 +00:00
|
|
|
if col_search
|
2011-02-26 17:42:03 +00:00
|
|
|
col_names = col_search
|
2011-07-19 06:09:10 +00:00
|
|
|
else
|
|
|
|
col_names = default_columns + virtual_columns
|
2009-12-22 22:20:58 +00:00
|
|
|
end
|
2010-07-22 16:53:11 +00:00
|
|
|
|
2011-07-20 03:58:25 +00:00
|
|
|
if mode == :add
|
2011-07-20 04:39:52 +00:00
|
|
|
host_ranges.each do |range|
|
|
|
|
range.each do |address|
|
|
|
|
host = framework.db.find_or_create_host(:host => address)
|
|
|
|
print_status("Time: #{host.created_at} Host: host=#{host.address}")
|
|
|
|
if set_rhosts
|
|
|
|
# only unique addresses
|
|
|
|
rhosts << host.address unless rhosts.include?(host.address)
|
|
|
|
end
|
2011-07-19 06:09:10 +00:00
|
|
|
end
|
|
|
|
end
|
2011-07-20 03:58:25 +00:00
|
|
|
return
|
|
|
|
end
|
2010-07-22 16:53:11 +00:00
|
|
|
|
2011-07-20 03:58:25 +00:00
|
|
|
# If we got here, we're searching. Delete implies search
|
|
|
|
|
|
|
|
tbl = Rex::Ui::Text::Table.new(
|
|
|
|
{
|
|
|
|
'Header' => "Hosts",
|
|
|
|
'Columns' => col_names,
|
|
|
|
})
|
|
|
|
|
|
|
|
# Sentinal value meaning all
|
|
|
|
host_ranges.push(nil) if host_ranges.empty?
|
|
|
|
|
2011-07-20 04:39:52 +00:00
|
|
|
each_host_range_chunk(host_ranges) do |host_search|
|
2011-07-19 06:09:10 +00:00
|
|
|
framework.db.hosts(framework.db.workspace, onlyup, host_search).each do |host|
|
|
|
|
columns = col_names.map do |n|
|
|
|
|
# Deal with the special cases
|
|
|
|
if virtual_columns.include?(n)
|
|
|
|
case n
|
|
|
|
when "svcs"; host.services.length
|
|
|
|
when "vulns"; host.vulns.length
|
|
|
|
when "workspace"; host.workspace.name
|
|
|
|
end
|
|
|
|
# Otherwise, it's just an attribute
|
|
|
|
else
|
|
|
|
host.attributes[n] || ""
|
2010-07-06 18:18:26 +00:00
|
|
|
end
|
|
|
|
end
|
2010-07-22 16:53:11 +00:00
|
|
|
|
2011-07-19 06:09:10 +00:00
|
|
|
tbl << columns
|
|
|
|
if set_rhosts
|
|
|
|
# only unique addresses
|
|
|
|
rhosts << host.address unless rhosts.include?(host.address)
|
|
|
|
end
|
2011-07-20 03:58:25 +00:00
|
|
|
if mode == :delete
|
|
|
|
host.destroy
|
|
|
|
delete_count += 1
|
|
|
|
end
|
2011-07-19 06:09:10 +00:00
|
|
|
end
|
2011-07-20 03:58:25 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if output
|
|
|
|
print_status("Wrote hosts to #{output}")
|
|
|
|
::File.open(output, "wb") { |ofd|
|
|
|
|
ofd.write(tbl.to_csv)
|
|
|
|
}
|
|
|
|
else
|
|
|
|
print_line
|
|
|
|
print_line tbl.to_s
|
2009-12-22 02:48:54 +00:00
|
|
|
end
|
2011-04-22 05:08:08 +00:00
|
|
|
|
|
|
|
# Finally, handle the case where the user wants the resulting list
|
|
|
|
# of hosts to go into RHOSTS.
|
|
|
|
set_rhosts_from_addrs(rhosts) if set_rhosts
|
2011-07-20 03:58:25 +00:00
|
|
|
print_status("Deleted #{delete_count} hosts") if delete_count > 0
|
2010-05-17 04:01:26 +00:00
|
|
|
end
|
2006-05-30 15:44:48 +00:00
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_services_help
|
|
|
|
# Like cmd_hosts, use "-h" instead of recreating the column list
|
2011-04-16 22:49:31 +00:00
|
|
|
# here
|
2011-07-20 08:39:35 +00:00
|
|
|
cmd_services("-h")
|
2011-04-16 22:49:31 +00:00
|
|
|
end
|
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_services(*args)
|
2010-05-25 01:32:30 +00:00
|
|
|
return unless active?
|
2011-07-19 11:16:36 +00:00
|
|
|
mode = :search
|
2009-11-06 21:08:34 +00:00
|
|
|
onlyup = false
|
2011-04-22 02:22:21 +00:00
|
|
|
output_file = nil
|
2011-04-22 05:08:08 +00:00
|
|
|
set_rhosts = nil
|
2011-02-27 15:58:08 +00:00
|
|
|
col_search = ['port', 'proto', 'name', 'state', 'info']
|
2009-12-22 22:20:58 +00:00
|
|
|
default_columns = ::Msf::DBManager::Service.column_names.sort
|
|
|
|
default_columns.delete_if {|v| (v[-2,2] == "id")}
|
2011-07-20 04:39:52 +00:00
|
|
|
|
|
|
|
host_ranges = []
|
2011-07-20 02:07:14 +00:00
|
|
|
port_ranges = []
|
2011-07-20 04:39:52 +00:00
|
|
|
delete_count = 0
|
2011-07-20 02:07:14 +00:00
|
|
|
|
|
|
|
# option parsing
|
2009-11-06 21:08:34 +00:00
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
2011-07-19 11:16:36 +00:00
|
|
|
when '-a','--add'
|
|
|
|
mode = :add
|
|
|
|
when '-d','--delete'
|
|
|
|
mode = :delete
|
2009-11-06 21:08:34 +00:00
|
|
|
when '-u','--up'
|
|
|
|
onlyup = true
|
2009-12-22 22:20:58 +00:00
|
|
|
when '-c'
|
|
|
|
list = args.shift
|
|
|
|
if(!list)
|
|
|
|
print_error("Invalid column list")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
col_search = list.strip().split(",")
|
|
|
|
col_search.each { |c|
|
|
|
|
if not default_columns.include? c
|
|
|
|
print_error("Invalid column list. Possible values are (#{default_columns.join("|")})")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
}
|
2009-11-06 21:08:34 +00:00
|
|
|
when '-p'
|
2011-07-20 02:07:14 +00:00
|
|
|
unless (arg_port_range(args.shift, port_ranges, true))
|
2009-11-06 21:08:34 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
when '-r'
|
2009-12-21 16:46:11 +00:00
|
|
|
proto = args.shift
|
|
|
|
if (!proto)
|
2009-11-06 21:08:34 +00:00
|
|
|
print_status("Invalid protocol")
|
|
|
|
return
|
|
|
|
end
|
2009-12-21 16:46:11 +00:00
|
|
|
proto = proto.strip
|
2011-07-20 04:39:52 +00:00
|
|
|
when '-s'
|
2009-11-06 21:08:34 +00:00
|
|
|
namelist = args.shift
|
2009-12-21 16:46:11 +00:00
|
|
|
if (!namelist)
|
|
|
|
print_error("Invalid name list")
|
2009-11-06 21:08:34 +00:00
|
|
|
return
|
|
|
|
end
|
2009-12-21 16:46:11 +00:00
|
|
|
names = namelist.strip().split(",")
|
2011-04-22 02:22:21 +00:00
|
|
|
when '-o'
|
|
|
|
output_file = args.shift
|
|
|
|
if (!output_file)
|
|
|
|
print_error("Invalid output filename")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
output_file = File.expand_path(output_file)
|
2011-04-22 05:08:08 +00:00
|
|
|
when '-R','--rhosts'
|
|
|
|
set_rhosts = true
|
|
|
|
rhosts = []
|
2009-11-06 21:08:34 +00:00
|
|
|
|
|
|
|
when '-h','--help'
|
2009-12-22 22:20:58 +00:00
|
|
|
print_line
|
2011-07-20 08:39:35 +00:00
|
|
|
print_line "Usage: services [-h] [-u] [-a] [-r <proto>] [-p <port1,port2>] [-n <name1,name2>] [-o <filename>] [addr1 addr2 ...]"
|
2009-12-22 22:20:58 +00:00
|
|
|
print_line
|
2011-07-20 02:07:14 +00:00
|
|
|
print_line " -a,--add Add the services instead of searching"
|
2011-07-19 11:16:36 +00:00
|
|
|
print_line " -d,--delete Delete the services instead of searching"
|
2009-12-22 22:20:58 +00:00
|
|
|
print_line " -c <col1,col2> Only show the given columns"
|
|
|
|
print_line " -h,--help Show this help information"
|
2011-07-20 04:39:52 +00:00
|
|
|
print_line " -s <name1,name2> Search for a list of service names"
|
2009-12-22 22:20:58 +00:00
|
|
|
print_line " -p <port1,port2> Search for a list of ports"
|
|
|
|
print_line " -r <protocol> Only show [tcp|udp] services"
|
|
|
|
print_line " -u,--up Only show services which are up"
|
2011-04-22 02:22:21 +00:00
|
|
|
print_line " -o <file> Send output to a file in csv format"
|
2011-04-22 05:08:08 +00:00
|
|
|
print_line " -R,--rhosts Set RHOSTS from the results of the search"
|
2009-12-22 22:20:58 +00:00
|
|
|
print_line
|
|
|
|
print_line "Available columns: #{default_columns.join(", ")}"
|
|
|
|
print_line
|
2009-11-06 21:08:34 +00:00
|
|
|
return
|
2011-07-19 11:16:36 +00:00
|
|
|
else
|
2011-07-20 04:39:52 +00:00
|
|
|
# Anything that wasn't an option is a host to search for
|
|
|
|
unless (arg_host_range(arg, host_ranges))
|
|
|
|
return
|
|
|
|
end
|
2009-11-06 21:08:34 +00:00
|
|
|
end
|
2006-05-30 15:44:48 +00:00
|
|
|
end
|
2009-12-21 16:46:11 +00:00
|
|
|
|
2011-07-20 02:07:14 +00:00
|
|
|
ports = port_ranges.flatten.uniq
|
|
|
|
|
2011-07-20 04:39:52 +00:00
|
|
|
if mode == :add
|
|
|
|
# Can only deal with one port and one service name at a time
|
|
|
|
# right now. Them's the breaks.
|
|
|
|
if ports.length != 1
|
|
|
|
print_error("Exactly one port required")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
host_ranges.each do |range|
|
|
|
|
range.each do |addr|
|
|
|
|
host = framework.db.find_or_create_host(:host => addr)
|
|
|
|
next if not host
|
|
|
|
info = {
|
|
|
|
:host => host,
|
|
|
|
:port => ports.first.to_i
|
|
|
|
}
|
|
|
|
info[:proto] = proto.downcase if proto
|
|
|
|
info[:name] = names.first.downcase if names and names.first
|
2011-07-19 11:16:36 +00:00
|
|
|
|
2011-07-20 04:39:52 +00:00
|
|
|
svc = framework.db.find_or_create_service(info)
|
|
|
|
print_status("Time: #{svc.created_at} Service: host=#{svc.host.address} port=#{svc.port} proto=#{svc.proto} name=#{svc.name}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
2011-07-19 11:16:36 +00:00
|
|
|
|
2011-07-20 04:39:52 +00:00
|
|
|
# If we got here, we're searching. Delete implies search
|
2011-07-19 11:16:36 +00:00
|
|
|
|
2011-07-20 04:39:52 +00:00
|
|
|
col_names = default_columns
|
|
|
|
if col_search
|
|
|
|
col_names = col_search
|
|
|
|
end
|
|
|
|
tbl = Rex::Ui::Text::Table.new({
|
|
|
|
'Header' => "Services",
|
|
|
|
'Columns' => ['host'] + col_names,
|
|
|
|
})
|
|
|
|
|
|
|
|
# Sentinal value meaning all
|
|
|
|
host_ranges.push(nil) if host_ranges.empty?
|
|
|
|
ports = nil if ports.empty?
|
2011-07-19 11:16:36 +00:00
|
|
|
|
2011-07-20 04:39:52 +00:00
|
|
|
each_host_range_chunk(host_ranges) do |host_search|
|
|
|
|
framework.db.services(framework.db.workspace, onlyup, proto, host_search, ports, names).each do |service|
|
2011-07-20 02:07:14 +00:00
|
|
|
|
2011-07-19 11:16:36 +00:00
|
|
|
host = service.host
|
|
|
|
columns = [host.address] + col_names.map { |n| service[n].to_s || "" }
|
|
|
|
tbl << columns
|
|
|
|
if set_rhosts
|
|
|
|
# only unique addresses
|
|
|
|
rhosts << host.address unless rhosts.include?(host.address)
|
|
|
|
end
|
2011-07-20 04:39:52 +00:00
|
|
|
if (mode == :delete)
|
|
|
|
service.destroy
|
|
|
|
delete_count += 1
|
|
|
|
end
|
2011-07-19 11:16:36 +00:00
|
|
|
end
|
2011-07-20 04:39:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
print_line
|
|
|
|
if (output_file == nil)
|
|
|
|
print_line tbl.to_s
|
|
|
|
else
|
|
|
|
# create the output file
|
|
|
|
File.open(output_file, "wb") { |f| f.write(tbl.to_csv) }
|
|
|
|
print_status("Wrote services to #{output_file}")
|
2011-04-22 02:22:21 +00:00
|
|
|
end
|
2011-04-22 05:08:08 +00:00
|
|
|
|
|
|
|
# Finally, handle the case where the user wants the resulting list
|
|
|
|
# of hosts to go into RHOSTS.
|
|
|
|
set_rhosts_from_addrs(rhosts) if set_rhosts
|
2011-07-20 04:39:52 +00:00
|
|
|
print_status("Deleted #{delete_count} services") if delete_count > 0
|
2011-04-22 05:08:08 +00:00
|
|
|
|
2009-12-18 01:14:05 +00:00
|
|
|
end
|
2009-11-06 21:08:34 +00:00
|
|
|
|
2011-04-22 05:08:08 +00:00
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_vulns_help
|
2011-04-16 22:49:31 +00:00
|
|
|
print_line "Print all vulnerabilities in the database"
|
|
|
|
print_line
|
2011-07-20 08:39:35 +00:00
|
|
|
print_line "Usage: vulns [addr range]"
|
2011-07-20 06:32:40 +00:00
|
|
|
print_line
|
|
|
|
#print_line " -a,--add Add creds to the given addresses instead of listing"
|
|
|
|
#print_line " -d,--delete Delete the creds instead of searching"
|
|
|
|
print_line " -h,--help Show this help information"
|
|
|
|
print_line " -p,--port <portspec> List vulns matching this port spec"
|
|
|
|
print_line " -s <svc names> List vulns matching these service names"
|
|
|
|
print_line
|
|
|
|
print_line "Examples:"
|
2011-07-20 08:39:35 +00:00
|
|
|
print_line " vulns -p 1-65536 # only vulns with associated services"
|
|
|
|
print_line " vulns -p 1-65536 -s http # identified as http on any port"
|
2011-07-20 06:32:40 +00:00
|
|
|
print_line
|
2011-04-16 22:49:31 +00:00
|
|
|
end
|
|
|
|
|
2010-02-18 06:40:38 +00:00
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_vulns(*args)
|
2010-05-25 01:32:30 +00:00
|
|
|
return unless active?
|
2011-07-20 06:32:40 +00:00
|
|
|
|
|
|
|
host_ranges = []
|
|
|
|
port_ranges = []
|
|
|
|
svcs = []
|
|
|
|
|
|
|
|
# Short-circuit help
|
|
|
|
if args.delete "-h"
|
2011-07-27 19:38:34 +00:00
|
|
|
cmd_vulns_help
|
2011-07-20 06:32:40 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
mode = :search
|
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
|
|
|
#when "-a","--add"
|
|
|
|
# mode = :add
|
|
|
|
#when "-d"
|
|
|
|
# mode = :delete
|
|
|
|
when "-h"
|
2011-07-20 08:39:35 +00:00
|
|
|
cmd_creds_help
|
2011-07-20 06:32:40 +00:00
|
|
|
return
|
|
|
|
when "-p","--port"
|
|
|
|
unless (arg_port_range(args.shift, port_ranges, true))
|
|
|
|
return
|
|
|
|
end
|
|
|
|
when "-s","--service"
|
|
|
|
service = args.shift
|
|
|
|
if (!service)
|
|
|
|
print_error("Argument required for -s")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
svcs = service.split(/[\s]*,[\s]*/)
|
2009-11-25 01:44:55 +00:00
|
|
|
else
|
2011-07-20 06:32:40 +00:00
|
|
|
# Anything that wasn't an option is a host to search for
|
|
|
|
unless (arg_host_range(arg, host_ranges))
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# normalize
|
|
|
|
host_ranges.push(nil) if host_ranges.empty?
|
|
|
|
ports = port_ranges.flatten.uniq
|
|
|
|
svcs.flatten!
|
|
|
|
|
|
|
|
each_host_range_chunk(host_ranges) do |host_search|
|
|
|
|
framework.db.hosts(framework.db.workspace, false, host_search).each do |host|
|
|
|
|
host.vulns.each do |vuln|
|
|
|
|
reflist = vuln.refs.map { |r| r.name }
|
|
|
|
if(vuln.service)
|
|
|
|
# Skip this one if the user specified a port and it
|
|
|
|
# doesn't match.
|
|
|
|
next unless ports.empty? or ports.include? vuln.service.port
|
|
|
|
# Same for service names
|
|
|
|
next unless svcs.empty? or svcs.include?(vuln.service.name)
|
|
|
|
print_status("Time: #{vuln.created_at} Vuln: host=#{host.address} port=#{vuln.service.port} proto=#{vuln.service.proto} name=#{vuln.name} refs=#{reflist.join(',')}")
|
|
|
|
else
|
|
|
|
# This vuln has no service, so it can't match
|
|
|
|
next unless ports.empty? and svcs.empty?
|
|
|
|
print_status("Time: #{vuln.created_at} Vuln: host=#{host.address} name=#{vuln.name} refs=#{reflist.join(',')}")
|
|
|
|
end
|
|
|
|
end
|
2009-11-25 01:44:55 +00:00
|
|
|
end
|
2006-05-30 15:44:48 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
end
|
2008-03-02 04:46:13 +00:00
|
|
|
|
2011-07-20 06:32:40 +00:00
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_creds_help
|
|
|
|
print_line "Usage: creds [addr range]"
|
|
|
|
print_line "Usage: creds -a <addr range> -p <port> -t <type> -u <user> -P <pass>"
|
2011-07-19 11:16:36 +00:00
|
|
|
print_line
|
2011-07-19 22:53:02 +00:00
|
|
|
print_line " -a,--add Add creds to the given addresses instead of listing"
|
|
|
|
print_line " -d,--delete Delete the creds instead of searching"
|
|
|
|
print_line " -h,--help Show this help information"
|
|
|
|
print_line " -p,--port <portspec> List creds matching this port spec"
|
|
|
|
print_line " -s <svc names> List creds matching these service names"
|
|
|
|
print_line " -t,--type <type> Add a cred of this type (only with -a). Default: password"
|
|
|
|
print_line " -u,--user Add a cred for this user (only with -a). Default: blank"
|
|
|
|
print_line " -P,--password Add a cred with this password (only with -a). Default: blank"
|
2011-07-19 11:16:36 +00:00
|
|
|
print_line
|
|
|
|
print_line "Examples:"
|
2011-07-20 08:39:35 +00:00
|
|
|
print_line " creds # Default, returns all active credentials"
|
|
|
|
print_line " creds all # Returns all credentials active or not"
|
|
|
|
print_line " creds 1.2.3.4/24 # nmap host specification"
|
|
|
|
print_line " creds -p 22-25,445 # nmap port specification"
|
|
|
|
print_line " creds 10.1.*.* -s ssh,smb all"
|
2011-07-19 11:16:36 +00:00
|
|
|
print_line
|
|
|
|
end
|
|
|
|
|
2011-06-29 14:50:15 +00:00
|
|
|
#
|
2011-07-19 19:42:45 +00:00
|
|
|
# Can return return active or all, on a certain host or range, on a
|
|
|
|
# certain port or range, and/or on a service name.
|
2010-08-18 00:58:20 +00:00
|
|
|
#
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_creds(*args)
|
2010-08-18 00:58:20 +00:00
|
|
|
return unless active?
|
2011-07-19 11:16:36 +00:00
|
|
|
|
2010-08-18 00:58:20 +00:00
|
|
|
search_param = nil
|
|
|
|
inactive_ok = false
|
2011-07-19 22:53:02 +00:00
|
|
|
type = "password"
|
|
|
|
|
|
|
|
host_ranges = []
|
|
|
|
port_ranges = []
|
|
|
|
svcs = []
|
2011-07-19 11:16:36 +00:00
|
|
|
|
|
|
|
# Short-circuit help
|
|
|
|
if args.delete "-h"
|
2011-07-20 08:39:35 +00:00
|
|
|
cmd_creds_help
|
2011-07-19 11:16:36 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2011-07-19 22:53:02 +00:00
|
|
|
mode = :search
|
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
|
|
|
when "-a","--add"
|
|
|
|
mode = :add
|
|
|
|
when "-d"
|
|
|
|
mode = :delete
|
|
|
|
when "-h"
|
2011-07-20 08:39:35 +00:00
|
|
|
cmd_creds_help
|
2011-07-19 22:53:02 +00:00
|
|
|
return
|
|
|
|
when "-p","--port"
|
2011-07-20 00:33:52 +00:00
|
|
|
unless (arg_port_range(args.shift, port_ranges, true))
|
2011-07-19 22:53:02 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
when "-t","--type"
|
|
|
|
ptype = args.shift
|
|
|
|
if (!ptype)
|
|
|
|
print_error("Argument required for -t")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
when "-s","--service"
|
|
|
|
service = args.shift
|
|
|
|
if (!service)
|
|
|
|
print_error("Argument required for -s")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
svcs = service.split(/[\s]*,[\s]*/)
|
|
|
|
when "-P","--password"
|
|
|
|
pass = args.shift
|
|
|
|
if (!pass)
|
|
|
|
print_error("Argument required for -P")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
when "-u","--user"
|
|
|
|
user = args.shift
|
|
|
|
if (!user)
|
|
|
|
print_error("Argument required for -u")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
when "all"
|
|
|
|
# The user wants inactive passwords, too
|
|
|
|
inactive_ok = true
|
|
|
|
else
|
2011-07-20 00:33:52 +00:00
|
|
|
# Anything that wasn't an option is a host to search for
|
|
|
|
unless (arg_host_range(arg, host_ranges))
|
2011-07-19 22:53:02 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
2010-08-18 00:58:20 +00:00
|
|
|
end
|
2011-07-19 11:16:36 +00:00
|
|
|
|
2011-07-19 22:53:02 +00:00
|
|
|
if mode == :add
|
|
|
|
if port_ranges.length != 1 or port_ranges.first.length != 1
|
|
|
|
print_error("Exactly one port required")
|
2011-07-19 11:16:36 +00:00
|
|
|
return
|
|
|
|
end
|
2011-07-19 22:53:02 +00:00
|
|
|
port = port_ranges.first.first
|
|
|
|
host_ranges.each do |range|
|
|
|
|
range.each do |host|
|
|
|
|
cred = framework.db.find_or_create_cred(
|
|
|
|
:host => host,
|
|
|
|
:port => port,
|
|
|
|
:user => (user == "NULL" ? nil : user),
|
|
|
|
:pass => (pass == "NULL" ? nil : pass),
|
|
|
|
:ptype => ptype,
|
|
|
|
:sname => service,
|
|
|
|
:active => true
|
|
|
|
)
|
|
|
|
print_status("Time: #{cred.updated_at} Credential: host=#{cred.service.host.address} port=#{cred.service.port} proto=#{cred.service.proto} sname=#{cred.service.name} type=#{cred.ptype} user=#{cred.user} pass=#{cred.pass} active=#{cred.active}")
|
|
|
|
end
|
2011-07-19 11:16:36 +00:00
|
|
|
end
|
2011-07-19 22:53:02 +00:00
|
|
|
return
|
2011-07-19 11:16:36 +00:00
|
|
|
end
|
|
|
|
|
2011-07-19 22:53:02 +00:00
|
|
|
# If we get here, we're searching. Delete implies search
|
|
|
|
|
2011-07-20 00:33:52 +00:00
|
|
|
# normalize
|
|
|
|
ports = port_ranges.flatten.uniq
|
|
|
|
svcs.flatten!
|
|
|
|
|
2011-07-20 04:48:55 +00:00
|
|
|
tbl = Rex::Ui::Text::Table.new({
|
|
|
|
'Header' => "Credentials",
|
|
|
|
'Columns' => [ 'host', 'port', 'user', 'pass', 'type', 'active?' ],
|
|
|
|
})
|
|
|
|
|
2011-07-19 11:16:36 +00:00
|
|
|
creds_returned = 0
|
2011-07-19 22:53:02 +00:00
|
|
|
# Now do the actual search
|
2010-08-18 00:58:20 +00:00
|
|
|
framework.db.each_cred(framework.db.workspace) do |cred|
|
2011-07-19 11:16:36 +00:00
|
|
|
# skip if it's inactive and user didn't ask for all
|
|
|
|
next unless (cred.active or inactive_ok)
|
|
|
|
|
|
|
|
# Also skip if the user is searching for something and this
|
|
|
|
# one doesn't match
|
2011-07-19 22:53:02 +00:00
|
|
|
includes = false
|
|
|
|
host_ranges.map do |rw|
|
|
|
|
includes = rw.include? cred.service.host.address
|
|
|
|
break if includes
|
2010-08-18 00:58:20 +00:00
|
|
|
end
|
2011-07-19 22:53:02 +00:00
|
|
|
next unless host_ranges.empty? or includes
|
|
|
|
|
|
|
|
# Same for ports
|
2011-07-20 00:33:52 +00:00
|
|
|
next unless ports.empty? or ports.include? cred.service.port
|
2011-07-19 22:53:02 +00:00
|
|
|
|
|
|
|
# Same for service names
|
|
|
|
next unless svcs.empty? or svcs.include?(cred.service.name)
|
2011-07-19 11:16:36 +00:00
|
|
|
|
2011-07-20 04:48:55 +00:00
|
|
|
row = [
|
|
|
|
cred.service.host.address, cred.service.port,
|
|
|
|
cred.user, cred.pass, cred.ptype,
|
|
|
|
(cred.active ? "true" : "false")
|
|
|
|
]
|
|
|
|
tbl << row
|
2011-07-19 22:53:02 +00:00
|
|
|
if mode == :delete
|
|
|
|
cred.destroy
|
|
|
|
end
|
2011-07-19 11:16:36 +00:00
|
|
|
creds_returned += 1
|
2010-08-18 00:58:20 +00:00
|
|
|
end
|
2011-07-20 04:48:55 +00:00
|
|
|
print_line
|
|
|
|
print_line tbl.to_s
|
2010-08-18 00:58:20 +00:00
|
|
|
print_status "Found #{creds_returned} credential#{creds_returned == 1 ? "" : "s"}."
|
|
|
|
end
|
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_notes_help
|
|
|
|
print_line "Usage: notes [-h] [-t <type1,type2>] [-n <data string>] [-a] [addr range]"
|
2011-04-16 22:49:31 +00:00
|
|
|
print_line
|
2011-07-19 08:13:55 +00:00
|
|
|
print_line " -a,--add Add a note to the list of addresses, instead of listing"
|
2011-07-19 11:16:36 +00:00
|
|
|
print_line " -d,--delete Delete the hosts instead of searching"
|
2011-07-19 08:13:55 +00:00
|
|
|
print_line " -n,--note <data> Set the data for a new note (only with -a)"
|
2011-04-16 22:49:31 +00:00
|
|
|
print_line " -t <type1,type2> Search for a list of types"
|
|
|
|
print_line " -h,--help Show this help information"
|
2011-04-22 05:08:08 +00:00
|
|
|
print_line " -R,--rhosts Set RHOSTS from the results of the search"
|
2011-04-16 22:49:31 +00:00
|
|
|
print_line
|
2011-07-19 08:13:55 +00:00
|
|
|
print_line "Examples:"
|
2011-07-20 08:39:35 +00:00
|
|
|
print_line " notes --add -t apps -n 'winzip' 10.1.1.34 10.1.20.41"
|
|
|
|
print_line " notes -t smb.fingerprint 10.1.1.34 10.1.20.41"
|
2011-07-19 08:13:55 +00:00
|
|
|
print_line
|
2011-04-16 22:49:31 +00:00
|
|
|
end
|
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_notes(*args)
|
2010-05-25 01:32:30 +00:00
|
|
|
return unless active?
|
2011-07-19 08:13:55 +00:00
|
|
|
mode = :search
|
|
|
|
data = nil
|
2009-11-06 21:08:34 +00:00
|
|
|
types = nil
|
2011-04-22 05:08:08 +00:00
|
|
|
set_rhosts = false
|
2011-07-20 07:29:42 +00:00
|
|
|
|
|
|
|
host_ranges = []
|
2011-07-19 08:13:55 +00:00
|
|
|
|
2009-11-06 21:08:34 +00:00
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
2011-07-19 08:13:55 +00:00
|
|
|
when '-a','--add'
|
|
|
|
mode = :add
|
|
|
|
when '-d','--delete'
|
|
|
|
mode = :delete
|
|
|
|
when '-n','--note'
|
|
|
|
data = args.shift
|
|
|
|
if(!data)
|
|
|
|
print_error("Can't make a note with no data")
|
2009-11-06 21:08:34 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
when '-t'
|
|
|
|
typelist = args.shift
|
|
|
|
if(!typelist)
|
2011-07-19 08:13:55 +00:00
|
|
|
print_error("Invalid type list")
|
2009-11-06 21:08:34 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
types = typelist.strip().split(",")
|
2011-04-22 05:08:08 +00:00
|
|
|
when '-R','--rhosts'
|
|
|
|
set_rhosts = true
|
|
|
|
rhosts = []
|
2009-11-06 21:08:34 +00:00
|
|
|
when '-h','--help'
|
2011-07-20 08:39:35 +00:00
|
|
|
cmd_notes_help
|
2009-11-06 21:08:34 +00:00
|
|
|
return
|
2011-07-19 08:13:55 +00:00
|
|
|
else
|
2011-07-20 07:29:42 +00:00
|
|
|
# Anything that wasn't an option is a host to search for
|
|
|
|
unless (arg_host_range(arg, host_ranges))
|
|
|
|
return
|
|
|
|
end
|
2009-11-06 21:08:34 +00:00
|
|
|
end
|
|
|
|
|
2008-03-02 04:46:13 +00:00
|
|
|
end
|
2011-07-19 19:18:12 +00:00
|
|
|
|
2011-07-20 07:29:42 +00:00
|
|
|
if mode == :add
|
2011-07-20 07:34:52 +00:00
|
|
|
if types.nil? or types.size != 1
|
|
|
|
print_error("Exactly one note type is required")
|
|
|
|
return
|
2010-09-20 01:54:23 +00:00
|
|
|
end
|
2011-07-19 08:13:55 +00:00
|
|
|
type = types.first
|
2011-07-20 07:29:42 +00:00
|
|
|
host_ranges.each { |range|
|
|
|
|
range.each { |addr|
|
|
|
|
host = framework.db.find_or_create_host(:host => addr)
|
|
|
|
break if not host
|
|
|
|
note = framework.db.find_or_create_note(:host => host, :type => type, :data => data)
|
|
|
|
break if not note
|
|
|
|
print_status("Time: #{note.created_at} Note: host=#{host.address} type=#{note.ntype} data=#{note.data}")
|
|
|
|
}
|
2011-07-19 08:13:55 +00:00
|
|
|
}
|
2011-07-20 07:29:42 +00:00
|
|
|
return
|
|
|
|
end
|
2011-07-19 08:13:55 +00:00
|
|
|
|
2011-07-20 07:29:42 +00:00
|
|
|
host_ranges.push(nil) if host_ranges.empty?
|
2011-07-19 08:13:55 +00:00
|
|
|
|
2011-07-20 07:29:42 +00:00
|
|
|
delete_count = 0
|
|
|
|
each_host_range_chunk(host_ranges) do |host_search|
|
|
|
|
framework.db.hosts(framework.db.workspace, false, host_search).each do |host|
|
|
|
|
host.notes.each do |note|
|
2011-07-19 19:05:16 +00:00
|
|
|
next if(types and types.index(note.ntype).nil?)
|
2011-07-19 08:13:55 +00:00
|
|
|
msg = "Time: #{note.created_at} Note:"
|
|
|
|
if (note.host)
|
|
|
|
host = note.host
|
|
|
|
msg << " host=#{note.host.address}"
|
|
|
|
if set_rhosts
|
|
|
|
# only unique addresses
|
|
|
|
rhosts << host.address unless rhosts.include?(host.address)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if (note.service)
|
|
|
|
name = (note.service.name ? note.service.name : "#{note.service.port}/#{note.service.proto}")
|
|
|
|
msg << " service=#{name}"
|
|
|
|
end
|
|
|
|
msg << " type=#{note.ntype} data=#{note.data.inspect}"
|
|
|
|
print_status(msg)
|
2011-07-20 07:29:42 +00:00
|
|
|
if mode == :delete
|
|
|
|
note.destroy
|
|
|
|
delete_count += 1
|
|
|
|
end
|
|
|
|
end
|
2009-12-29 23:48:45 +00:00
|
|
|
end
|
2010-05-17 04:01:26 +00:00
|
|
|
end
|
2011-04-22 05:08:08 +00:00
|
|
|
|
|
|
|
# Finally, handle the case where the user wants the resulting list
|
|
|
|
# of hosts to go into RHOSTS.
|
|
|
|
set_rhosts_from_addrs(rhosts) if set_rhosts
|
2011-07-20 07:29:42 +00:00
|
|
|
|
|
|
|
print_status("Deleted #{delete_count} note#{delete_count == 1 ? "" : "s"}") if delete_count > 0
|
2010-05-17 04:01:26 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_loot_help
|
|
|
|
print_line "Usage: loot [-h] [addr1 addr2 ...] [-t <type1,type2>]"
|
2011-04-16 22:49:31 +00:00
|
|
|
print_line
|
|
|
|
print_line " -t <type1,type2> Search for a list of types"
|
|
|
|
print_line " -h,--help Show this help information"
|
2011-04-22 05:08:08 +00:00
|
|
|
print_line
|
2011-04-16 22:49:31 +00:00
|
|
|
end
|
2011-02-27 16:30:17 +00:00
|
|
|
|
2011-07-20 08:39:35 +00:00
|
|
|
def cmd_loot(*args)
|
2011-02-27 16:30:17 +00:00
|
|
|
return unless active?
|
2011-07-20 05:13:59 +00:00
|
|
|
mode = :search
|
|
|
|
host_ranges = []
|
2011-02-27 16:30:17 +00:00
|
|
|
types = nil
|
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
2011-07-20 05:13:59 +00:00
|
|
|
when '-d','--delete'
|
|
|
|
mode = :delete
|
2011-02-27 16:30:17 +00:00
|
|
|
when '-t'
|
|
|
|
typelist = args.shift
|
|
|
|
if(!typelist)
|
2011-07-19 19:42:45 +00:00
|
|
|
print_status("Invalid type list")
|
2011-02-27 16:30:17 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
types = typelist.strip().split(",")
|
|
|
|
when '-h','--help'
|
2011-07-20 08:39:35 +00:00
|
|
|
cmd_loot_help
|
2011-02-27 16:30:17 +00:00
|
|
|
return
|
2011-07-19 19:05:16 +00:00
|
|
|
else
|
2011-07-20 05:13:59 +00:00
|
|
|
# Anything that wasn't an option is a host to search for
|
|
|
|
unless (arg_host_range(arg, host_ranges))
|
|
|
|
return
|
|
|
|
end
|
2011-02-27 16:30:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
2011-07-20 05:13:59 +00:00
|
|
|
tbl = Rex::Ui::Text::Table.new({
|
|
|
|
'Header' => "Loot",
|
2011-07-27 19:38:34 +00:00
|
|
|
'Columns' => [ 'host', 'service', 'type', 'name', 'content', 'info', 'path' ],
|
2011-07-20 05:13:59 +00:00
|
|
|
})
|
2011-02-27 16:30:17 +00:00
|
|
|
|
2011-07-20 05:13:59 +00:00
|
|
|
# Sentinal value meaning all
|
|
|
|
host_ranges.push(nil) if host_ranges.empty?
|
|
|
|
|
|
|
|
each_host_range_chunk(host_ranges) do |host_search|
|
|
|
|
framework.db.hosts(framework.db.workspace, false, host_search).each do |host|
|
|
|
|
host.loots.each do |loot|
|
|
|
|
next if(types and types.index(loot.ltype).nil?)
|
|
|
|
row = []
|
|
|
|
row.push( (loot.host ? loot.host.address : "") )
|
|
|
|
if (loot.service)
|
2011-07-27 19:38:34 +00:00
|
|
|
svc = (loot.service.name ? loot.service.name : "#{loot.service.port}/#{loot.service.proto}")
|
|
|
|
row.push svc
|
2011-07-20 05:13:59 +00:00
|
|
|
else
|
|
|
|
row.push ""
|
2011-08-12 19:09:05 +00:00
|
|
|
end
|
2011-07-20 05:13:59 +00:00
|
|
|
row.push(loot.ltype)
|
2011-07-27 19:38:34 +00:00
|
|
|
row.push(loot.name || "")
|
2011-07-20 05:13:59 +00:00
|
|
|
row.push(loot.content_type)
|
2011-07-27 19:38:34 +00:00
|
|
|
row.push(loot.info || "")
|
|
|
|
row.push(loot.path)
|
2011-07-20 05:13:59 +00:00
|
|
|
|
|
|
|
tbl << row
|
|
|
|
if (mode == :delete)
|
|
|
|
loot.destroy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2011-02-27 16:30:17 +00:00
|
|
|
end
|
2011-07-20 05:13:59 +00:00
|
|
|
print_line
|
|
|
|
print_line tbl.to_s
|
2011-02-27 16:30:17 +00:00
|
|
|
end
|
2006-05-30 15:44:48 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
#
|
|
|
|
# A shotgun approach to network-wide exploitation
|
|
|
|
#
|
|
|
|
def cmd_db_autopwn(*args)
|
2010-05-25 01:32:30 +00:00
|
|
|
return unless active?
|
2006-09-17 22:07:52 +00:00
|
|
|
|
|
|
|
stamp = Time.now.to_f
|
|
|
|
vcnt = 0
|
|
|
|
rcnt = 0
|
|
|
|
mode = 0
|
|
|
|
code = :bind
|
2008-11-18 22:01:15 +00:00
|
|
|
mjob = 5
|
|
|
|
regx = nil
|
2009-12-23 03:53:16 +00:00
|
|
|
minrank = nil
|
2010-03-24 00:11:21 +00:00
|
|
|
maxtime = 120
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2008-11-18 22:01:15 +00:00
|
|
|
port_inc = []
|
|
|
|
port_exc = []
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-12-10 08:21:52 +00:00
|
|
|
targ_inc = []
|
|
|
|
targ_exc = []
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
args.push("-h") if args.length == 0
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
|
|
|
when '-t'
|
|
|
|
mode |= PWN_SHOW
|
|
|
|
when '-x'
|
|
|
|
mode |= PWN_XREF
|
|
|
|
when '-p'
|
|
|
|
mode |= PWN_PORT
|
|
|
|
when '-e'
|
|
|
|
mode |= PWN_EXPL
|
|
|
|
when '-s'
|
2008-11-18 22:01:15 +00:00
|
|
|
mode |= PWN_SING
|
|
|
|
when '-q'
|
|
|
|
mode |= PWN_SLNT
|
2009-12-03 01:36:17 +00:00
|
|
|
when '-v'
|
|
|
|
mode |= PWN_VERB
|
2008-11-18 22:01:15 +00:00
|
|
|
when '-j'
|
|
|
|
mjob = args.shift.to_i
|
2006-09-17 22:07:52 +00:00
|
|
|
when '-r'
|
|
|
|
code = :conn
|
|
|
|
when '-b'
|
|
|
|
code = :bind
|
2006-12-10 08:21:52 +00:00
|
|
|
when '-I'
|
2010-05-17 04:35:33 +00:00
|
|
|
tmpopt = OptAddressRange.new('TEMPRANGE', [ true, '' ])
|
|
|
|
range = args.shift
|
|
|
|
if not tmpopt.valid?(range)
|
|
|
|
print_error("Invalid range for -I")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
targ_inc << Rex::Socket::RangeWalker.new(tmpopt.normalize(range))
|
2006-12-10 08:21:52 +00:00
|
|
|
when '-X'
|
2010-05-17 04:35:33 +00:00
|
|
|
tmpopt = OptAddressRange.new('TEMPRANGE', [ true, '' ])
|
|
|
|
range = args.shift
|
|
|
|
if not tmpopt.valid?(range)
|
|
|
|
print_error("Invalid range for -X")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
targ_exc << Rex::Socket::RangeWalker.new(tmpopt.normalize(range))
|
2008-11-18 22:01:15 +00:00
|
|
|
when '-PI'
|
2010-03-28 23:02:28 +00:00
|
|
|
port_inc = Rex::Socket.portspec_to_portlist(args.shift)
|
2008-11-18 22:01:15 +00:00
|
|
|
when '-PX'
|
2010-03-28 23:02:28 +00:00
|
|
|
port_exc = Rex::Socket.portspec_to_portlist(args.shift)
|
2008-11-18 22:01:15 +00:00
|
|
|
when '-m'
|
|
|
|
regx = args.shift
|
2009-12-23 03:53:16 +00:00
|
|
|
when '-R'
|
|
|
|
minrank = args.shift
|
2010-03-24 00:11:21 +00:00
|
|
|
when '-T'
|
|
|
|
maxtime = args.shift.to_f
|
2009-10-28 18:04:50 +00:00
|
|
|
when '-h','--help'
|
2006-09-17 22:07:52 +00:00
|
|
|
print_status("Usage: db_autopwn [options]")
|
2008-11-18 22:01:15 +00:00
|
|
|
print_line("\t-h Display this help text")
|
|
|
|
print_line("\t-t Show all matching exploit modules")
|
|
|
|
print_line("\t-x Select modules based on vulnerability references")
|
|
|
|
print_line("\t-p Select modules based on open ports")
|
|
|
|
print_line("\t-e Launch exploits against all matched targets")
|
|
|
|
# print_line("\t-s Only obtain a single shell per target system (NON-FUNCTIONAL)")
|
|
|
|
print_line("\t-r Use a reverse connect shell")
|
2009-10-04 19:48:48 +00:00
|
|
|
print_line("\t-b Use a bind shell on a random port (default)")
|
2008-12-02 02:03:22 +00:00
|
|
|
print_line("\t-q Disable exploit module output")
|
2009-12-23 03:53:16 +00:00
|
|
|
print_line("\t-R [rank] Only run modules with a minimal rank")
|
2008-11-18 22:01:15 +00:00
|
|
|
print_line("\t-I [range] Only exploit hosts inside this range")
|
|
|
|
print_line("\t-X [range] Always exclude hosts inside this range")
|
|
|
|
print_line("\t-PI [range] Only exploit hosts with these ports open")
|
|
|
|
print_line("\t-PX [range] Always exclude hosts with these ports open")
|
|
|
|
print_line("\t-m [regex] Only run modules whose name matches the regex")
|
2010-03-24 00:11:21 +00:00
|
|
|
print_line("\t-T [secs] Maximum runtime for any exploit in seconds")
|
2006-09-17 22:07:52 +00:00
|
|
|
print_line("")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-12-23 03:53:16 +00:00
|
|
|
minrank = minrank || framework.datastore['MinimumRank'] || 'manual'
|
2009-12-10 15:12:59 +00:00
|
|
|
if ! RankingName.values.include?(minrank)
|
2009-12-09 23:07:58 +00:00
|
|
|
print_error("MinimumRank invalid! Possible values are (#{RankingName.sort.map{|r|r[1]}.join("|")})")
|
2009-12-09 01:54:20 +00:00
|
|
|
wlog("MinimumRank invalid, ignoring", 'core', LEV_0)
|
2009-12-09 23:07:58 +00:00
|
|
|
return
|
2009-12-09 01:54:20 +00:00
|
|
|
else
|
|
|
|
minrank = RankingName.invert[minrank]
|
|
|
|
end
|
|
|
|
|
2009-12-03 01:36:17 +00:00
|
|
|
# Default to quiet mode
|
|
|
|
if (mode & PWN_VERB == 0)
|
|
|
|
mode |= PWN_SLNT
|
|
|
|
end
|
|
|
|
|
|
|
|
matches = {}
|
|
|
|
refmatches = {}
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
# Pre-allocate a list of references and ports for all exploits
|
|
|
|
mrefs = {}
|
|
|
|
mports = {}
|
|
|
|
mservs = {}
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2010-03-18 18:26:53 +00:00
|
|
|
# A list of jobs we spawned and need to wait for
|
|
|
|
autopwn_jobs = []
|
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
[ [framework.exploits, 'exploit' ], [ framework.auxiliary, 'auxiliary' ] ].each do |mtype|
|
|
|
|
mtype[0].each_module do |modname, mod|
|
|
|
|
o = mod.new
|
2006-09-17 22:07:52 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
if(mode & PWN_XREF != 0)
|
|
|
|
o.references.each do |r|
|
2009-12-03 01:36:17 +00:00
|
|
|
next if r.ctx_id == 'URL'
|
2009-12-11 20:21:18 +00:00
|
|
|
ref = r.ctx_id + "-" + r.ctx_val
|
|
|
|
ref.upcase!
|
2009-12-03 01:36:17 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
mrefs[ref] ||= {}
|
|
|
|
mrefs[ref][o.fullname] = o
|
|
|
|
end
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
if(mode & PWN_PORT != 0)
|
|
|
|
if(o.datastore['RPORT'])
|
|
|
|
rport = o.datastore['RPORT']
|
|
|
|
mports[rport.to_i] ||= {}
|
|
|
|
mports[rport.to_i][o.fullname] = o
|
|
|
|
end
|
2009-12-03 01:36:17 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
if(o.respond_to?('autofilter_ports'))
|
|
|
|
o.autofilter_ports.each do |rport|
|
|
|
|
mports[rport.to_i] ||= {}
|
|
|
|
mports[rport.to_i][o.fullname] = o
|
|
|
|
end
|
|
|
|
end
|
2009-12-03 01:36:17 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
if(o.respond_to?('autofilter_services'))
|
|
|
|
o.autofilter_services.each do |serv|
|
|
|
|
mservs[serv] ||= {}
|
|
|
|
mservs[serv][o.fullname] = o
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2009-12-03 01:36:17 +00:00
|
|
|
|
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
begin
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
framework.db.hosts.each do |host|
|
|
|
|
xhost = host.address
|
|
|
|
next if (targ_inc.length > 0 and not range_include?(targ_inc, xhost))
|
|
|
|
next if (targ_exc.length > 0 and range_include?(targ_exc, xhost))
|
2009-12-03 01:36:17 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
if(mode & PWN_VERB != 0)
|
|
|
|
print_status("Scanning #{xhost} for matching exploit modules...")
|
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
#
|
|
|
|
# Match based on vulnerability references
|
|
|
|
#
|
|
|
|
if (mode & PWN_XREF != 0)
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
host.vulns.each do |vuln|
|
2009-10-28 18:04:50 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
# Faster to handle these here
|
|
|
|
serv = vuln.service
|
|
|
|
xport = xprot = nil
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
if(serv)
|
|
|
|
xport = serv.port
|
|
|
|
xprot = serv.proto
|
2009-11-06 16:01:24 +00:00
|
|
|
end
|
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
vuln.refs.each do |ref|
|
|
|
|
mods = mrefs[ref.name.upcase] || {}
|
|
|
|
mods.each_key do |modname|
|
|
|
|
mod = mods[modname]
|
|
|
|
next if minrank and minrank > mod.rank
|
|
|
|
next if (regx and mod.fullname !~ /#{regx}/)
|
|
|
|
|
|
|
|
if(xport)
|
|
|
|
next if (port_inc.length > 0 and not port_inc.include?(serv.port.to_i))
|
|
|
|
next if (port_exc.length > 0 and port_exc.include?(serv.port.to_i))
|
|
|
|
else
|
|
|
|
if(mod.datastore['RPORT'])
|
|
|
|
next if (port_inc.length > 0 and not port_inc.include?(mod.datastore['RPORT'].to_i))
|
|
|
|
next if (port_exc.length > 0 and port_exc.include?(mod.datastore['RPORT'].to_i))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-09-11 15:32:23 +00:00
|
|
|
next if (regx and e.fullname !~ /#{regx}/)
|
2009-12-03 01:36:17 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
mod.datastore['RPORT'] = xport if xport
|
|
|
|
mod.datastore['RHOST'] = xhost
|
2009-12-03 15:26:30 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
filtered = false
|
2009-12-03 15:26:30 +00:00
|
|
|
begin
|
2009-12-11 21:46:44 +00:00
|
|
|
::Timeout.timeout(2, ::RuntimeError) do
|
2009-12-11 20:21:18 +00:00
|
|
|
filtered = true if not mod.autofilter()
|
|
|
|
end
|
2009-12-03 15:26:30 +00:00
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
2009-12-11 20:21:18 +00:00
|
|
|
rescue ::Timeout::Error
|
|
|
|
filtered = true
|
2009-12-03 15:26:30 +00:00
|
|
|
rescue ::Exception
|
2009-12-11 20:21:18 +00:00
|
|
|
filtered = true
|
2009-12-03 15:26:30 +00:00
|
|
|
end
|
2009-12-11 20:21:18 +00:00
|
|
|
next if filtered
|
2009-12-03 01:36:17 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
matches[[xport,xprot,xhost,mod.fullname]]=true
|
|
|
|
refmatches[[xport,xprot,xhost,mod.fullname]] ||= []
|
|
|
|
refmatches[[xport,xprot,xhost,mod.fullname]] << ref.name
|
2006-09-17 22:07:52 +00:00
|
|
|
end
|
2009-12-11 20:21:18 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2009-10-28 18:04:50 +00:00
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
#
|
|
|
|
# Match based on open ports
|
|
|
|
#
|
|
|
|
if (mode & PWN_PORT != 0)
|
|
|
|
host.services.each do |serv|
|
|
|
|
next if not serv.host
|
2010-02-05 15:43:24 +00:00
|
|
|
next if (serv.state != ServiceState::Open)
|
2009-12-11 20:21:18 +00:00
|
|
|
|
|
|
|
xport = serv.port.to_i
|
|
|
|
xprot = serv.proto
|
|
|
|
xname = serv.name
|
|
|
|
|
|
|
|
next if xport == 0
|
|
|
|
|
|
|
|
next if (port_inc.length > 0 and not port_inc.include?(xport))
|
|
|
|
next if (port_exc.length > 0 and port_exc.include?(xport))
|
|
|
|
|
|
|
|
mods = mports[xport.to_i] || {}
|
|
|
|
|
|
|
|
mods.each_key do |modname|
|
|
|
|
mod = mods[modname]
|
|
|
|
next if minrank and minrank > mod.rank
|
|
|
|
next if (regx and mod.fullname !~ /#{regx}/)
|
|
|
|
mod.datastore['RPORT'] = xport
|
|
|
|
mod.datastore['RHOST'] = xhost
|
|
|
|
|
|
|
|
filtered = false
|
|
|
|
begin
|
2009-12-11 21:46:44 +00:00
|
|
|
::Timeout.timeout(2, ::RuntimeError) do
|
2009-12-11 20:21:18 +00:00
|
|
|
filtered = true if not mod.autofilter()
|
|
|
|
end
|
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
|
|
|
rescue ::Exception
|
|
|
|
filtered = true
|
|
|
|
end
|
|
|
|
|
|
|
|
next if filtered
|
|
|
|
matches[[xport,xprot,xhost,mod.fullname]]=true
|
|
|
|
end
|
|
|
|
|
|
|
|
mods = mservs[xname] || {}
|
|
|
|
mods.each_key do |modname|
|
|
|
|
mod = mods[modname]
|
|
|
|
next if minrank and minrank > mod.rank
|
|
|
|
next if (regx and mod.fullname !~ /#{regx}/)
|
|
|
|
mod.datastore['RPORT'] = xport
|
|
|
|
mod.datastore['RHOST'] = xhost
|
|
|
|
|
|
|
|
filtered = false
|
|
|
|
begin
|
2009-12-11 21:46:44 +00:00
|
|
|
::Timeout.timeout(2, ::RuntimeError) do
|
2009-12-11 20:21:18 +00:00
|
|
|
filtered = true if not mod.autofilter()
|
|
|
|
end
|
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
|
|
|
rescue ::Exception
|
|
|
|
filtered = true
|
2009-11-06 16:01:24 +00:00
|
|
|
end
|
2009-12-11 20:21:18 +00:00
|
|
|
|
|
|
|
next if filtered
|
|
|
|
matches[[xport,xprot,xhost,mod.fullname]]=true
|
2006-09-17 22:07:52 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-12-11 20:21:18 +00:00
|
|
|
rescue ::Exception => e
|
|
|
|
print_status("ERROR: #{e.class} #{e} #{e.backtrace}")
|
|
|
|
return
|
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
|
|
|
|
if (mode & PWN_SHOW != 0)
|
2009-12-03 01:36:17 +00:00
|
|
|
print_status("Analysis completed in #{(Time.now.to_f - stamp).to_i} seconds (#{vcnt} vulns / #{rcnt} refs)")
|
2009-12-03 15:26:30 +00:00
|
|
|
print_status("")
|
2009-12-03 01:36:17 +00:00
|
|
|
print_status("=" * 80)
|
|
|
|
print_status(" " * 28 + "Matching Exploit Modules")
|
|
|
|
print_status("=" * 80)
|
|
|
|
|
|
|
|
matches.each_key do |xref|
|
|
|
|
mod = nil
|
|
|
|
if ((mod = framework.modules.create(xref[3])) == nil)
|
|
|
|
print_status("Failed to initialize #{xref[3]}")
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
if (mode & PWN_SHOW != 0)
|
|
|
|
tport = xref[0] || mod.datastore['RPORT']
|
|
|
|
if(refmatches[xref])
|
|
|
|
print_status(" #{xref[2]}:#{tport} #{xref[3]} (#{refmatches[xref].join(", ")})")
|
|
|
|
else
|
|
|
|
print_status(" #{xref[2]}:#{tport} #{xref[3]} (port match)")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
print_status("=" * 80)
|
|
|
|
print_status("")
|
|
|
|
print_status("")
|
2006-09-17 22:07:52 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2010-06-25 22:32:39 +00:00
|
|
|
ilog("db_autopwn: Matched #{matches.length} modules")
|
2008-11-18 22:01:15 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
idx = 0
|
|
|
|
matches.each_key do |xref|
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
idx += 1
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
begin
|
|
|
|
mod = nil
|
|
|
|
|
|
|
|
if ((mod = framework.modules.create(xref[3])) == nil)
|
|
|
|
print_status("Failed to initialize #{xref[3]}")
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# The code is just a proof-of-concept and will be expanded in the future
|
|
|
|
#
|
|
|
|
if (mode & PWN_EXPL != 0)
|
|
|
|
|
|
|
|
mod.datastore['RHOST'] = xref[2]
|
2009-12-03 01:36:17 +00:00
|
|
|
|
|
|
|
if(xref[0])
|
|
|
|
mod.datastore['RPORT'] = xref[0].to_s
|
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
|
|
|
|
if (code == :bind)
|
|
|
|
mod.datastore['LPORT'] = (rand(0x8fff) + 4000).to_s
|
2009-03-28 21:51:35 +00:00
|
|
|
if(mod.fullname =~ /\/windows\//)
|
|
|
|
mod.datastore['PAYLOAD'] = 'windows/meterpreter/bind_tcp'
|
|
|
|
else
|
|
|
|
mod.datastore['PAYLOAD'] = 'generic/shell_bind_tcp'
|
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
if (code == :conn)
|
|
|
|
mod.datastore['LHOST'] = Rex::Socket.source_address(xref[2])
|
|
|
|
mod.datastore['LPORT'] = (rand(0x8fff) + 4000).to_s
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
if (mod.datastore['LHOST'] == '127.0.0.1')
|
|
|
|
print_status("Failed to determine listener address for target #{xref[2]}...")
|
|
|
|
next
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:51:35 +00:00
|
|
|
if(mod.fullname =~ /\/windows\//)
|
|
|
|
mod.datastore['PAYLOAD'] = 'windows/meterpreter/reverse_tcp'
|
|
|
|
else
|
|
|
|
mod.datastore['PAYLOAD'] = 'generic/shell_reverse_tcp'
|
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
|
|
|
|
2008-11-18 22:01:15 +00:00
|
|
|
if(framework.jobs.keys.length >= mjob)
|
|
|
|
print_status("Job limit reached, waiting on modules to finish...")
|
|
|
|
while(framework.jobs.keys.length >= mjob)
|
2010-05-20 20:42:17 +00:00
|
|
|
::IO.select(nil, nil, nil, 0.25)
|
2008-11-18 22:01:15 +00:00
|
|
|
end
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-12-03 01:36:17 +00:00
|
|
|
print_status("(#{idx}/#{matches.length} [#{framework.sessions.length} sessions]): Launching #{xref[3]} against #{xref[2]}:#{mod.datastore['RPORT']}...")
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2010-11-12 06:19:49 +00:00
|
|
|
autopwn_jobs << framework.threads.spawn("AutoPwnJob#{xref[3]}", false, mod) do |xmod|
|
2010-03-24 00:11:21 +00:00
|
|
|
begin
|
|
|
|
stime = Time.now.to_f
|
|
|
|
::Timeout.timeout(maxtime) do
|
|
|
|
inp = (mode & PWN_SLNT != 0) ? nil : driver.input
|
|
|
|
out = (mode & PWN_SLNT != 0) ? nil : driver.output
|
|
|
|
|
|
|
|
case xmod.type
|
|
|
|
when MODULE_EXPLOIT
|
|
|
|
xmod.exploit_simple(
|
|
|
|
'Payload' => xmod.datastore['PAYLOAD'],
|
|
|
|
'LocalInput' => inp,
|
|
|
|
'LocalOutput' => out,
|
|
|
|
'RunAsJob' => false)
|
|
|
|
when MODULE_AUX
|
|
|
|
xmod.run_simple(
|
|
|
|
'LocalInput' => inp,
|
|
|
|
'LocalOutput' => out,
|
|
|
|
'RunAsJob' => false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
rescue ::Timeout::Error
|
|
|
|
print_status(" >> autopwn module timeout from #{xmod.fullname} after #{Time.now.to_f - stime} seconds")
|
|
|
|
rescue ::Exception
|
|
|
|
print_status(" >> autopwn exception during launch from #{xmod.fullname}: #{$!} ")
|
2006-09-17 22:07:52 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2008-11-18 22:01:15 +00:00
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
2010-03-24 00:11:21 +00:00
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
rescue ::Exception
|
2008-12-19 07:11:08 +00:00
|
|
|
print_status(" >> autopwn exception from #{xref[3]}: #{$!} #{$!.backtrace}")
|
2006-09-17 22:07:52 +00:00
|
|
|
end
|
2008-11-18 22:01:15 +00:00
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
|
2010-03-18 18:26:53 +00:00
|
|
|
# Wait on all the jobs we just spawned
|
|
|
|
while (not autopwn_jobs.empty?)
|
|
|
|
# All running jobs are stored in framework.jobs. If it's
|
|
|
|
# not in this list, it must have completed.
|
2010-03-24 00:11:21 +00:00
|
|
|
autopwn_jobs.delete_if { |j| not j.alive? }
|
2009-12-03 01:36:17 +00:00
|
|
|
|
2010-03-18 18:26:53 +00:00
|
|
|
print_status("(#{matches.length}/#{matches.length} [#{framework.sessions.length} sessions]): Waiting on #{autopwn_jobs.length} launched modules to finish execution...")
|
2010-05-20 20:42:17 +00:00
|
|
|
::IO.select(nil, nil, nil, 5.0)
|
2009-12-03 01:36:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if (mode & PWN_SHOW != 0 and mode & PWN_EXPL != 0)
|
|
|
|
print_status("The autopwn command has completed with #{framework.sessions.length} sessions")
|
|
|
|
if(framework.sessions.length > 0)
|
|
|
|
print_status("Enter sessions -i [ID] to interact with a given session ID")
|
|
|
|
print_status("")
|
|
|
|
print_status("=" * 80)
|
|
|
|
driver.run_single("sessions -l -v")
|
|
|
|
print_status("=" * 80)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
print_line("")
|
2006-09-17 22:07:52 +00:00
|
|
|
# EOM
|
|
|
|
end
|
|
|
|
|
2010-05-17 04:39:07 +00:00
|
|
|
#
|
|
|
|
# Determine if an IP address is inside a given range
|
|
|
|
#
|
|
|
|
def range_include?(ranges, addr)
|
|
|
|
ranges.each do |range|
|
|
|
|
return true if range.include? addr
|
|
|
|
end
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2010-05-24 23:38:45 +00:00
|
|
|
def cmd_db_import_tabs(str, words)
|
|
|
|
tab_complete_filenames(str, words)
|
|
|
|
end
|
|
|
|
|
2011-04-16 22:49:31 +00:00
|
|
|
def cmd_db_import_help
|
|
|
|
print_line "Usage: db_import <filename> [file2...]"
|
|
|
|
print_line
|
2011-07-20 07:51:52 +00:00
|
|
|
print_line "Filenames can be globs like *.xml, or **/*.xml which will search recursively"
|
|
|
|
print_line "Currently supported file types include:"
|
|
|
|
print_line " Acunetix XML"
|
|
|
|
print_line " Amap Log"
|
|
|
|
print_line " Amap Log -m"
|
|
|
|
print_line " Appscan XML"
|
|
|
|
print_line " Burp Session XML"
|
|
|
|
print_line " Foundstone XML"
|
|
|
|
print_line " IP360 ASPL"
|
|
|
|
print_line " IP360 XML v3"
|
|
|
|
print_line " Microsoft Baseline Security Analyzer"
|
|
|
|
print_line " Nessus NBE"
|
|
|
|
print_line " Nessus XML (v1 and v2)"
|
|
|
|
print_line " NetSparker XML"
|
|
|
|
print_line " NeXpose Simple XML"
|
|
|
|
print_line " NeXpose XML Report"
|
|
|
|
print_line " Nmap XML"
|
|
|
|
print_line " OpenVAS Report"
|
|
|
|
print_line " Qualys Asset XML"
|
|
|
|
print_line " Qualys Scan XML"
|
|
|
|
print_line " Retina XML"
|
2011-04-16 22:49:31 +00:00
|
|
|
print_line
|
|
|
|
end
|
|
|
|
|
2009-09-05 04:29:53 +00:00
|
|
|
#
|
2010-01-07 19:06:29 +00:00
|
|
|
# Generic import that automatically detects the file type
|
2009-09-05 04:29:53 +00:00
|
|
|
#
|
2010-01-07 19:06:29 +00:00
|
|
|
def cmd_db_import(*args)
|
2010-05-25 01:32:30 +00:00
|
|
|
return unless active?
|
2010-01-07 21:51:18 +00:00
|
|
|
if (args.include?("-h") or not (args and args.length > 0))
|
2011-04-16 22:49:31 +00:00
|
|
|
cmd_db_import_help
|
2010-01-07 19:06:29 +00:00
|
|
|
return
|
2009-09-05 04:29:53 +00:00
|
|
|
end
|
2010-01-07 21:51:18 +00:00
|
|
|
args.each { |glob|
|
2010-01-10 17:49:28 +00:00
|
|
|
files = Dir.glob(File.expand_path(glob))
|
|
|
|
if files.empty?
|
|
|
|
print_error("No such file #{glob}")
|
|
|
|
next
|
|
|
|
end
|
|
|
|
files.each { |filename|
|
2010-01-07 21:51:18 +00:00
|
|
|
if (not File.readable?(filename))
|
|
|
|
print_error("Could not read file #{filename}")
|
|
|
|
next
|
|
|
|
end
|
|
|
|
begin
|
2011-04-20 18:33:27 +00:00
|
|
|
warnings = 0
|
2010-06-08 19:16:20 +00:00
|
|
|
framework.db.import_file(:filename => filename) do |type,data|
|
|
|
|
case type
|
2011-05-24 19:40:50 +00:00
|
|
|
when :debug
|
|
|
|
print_error("DEBUG: #{data.inspect}")
|
2011-05-27 17:30:11 +00:00
|
|
|
when :vuln
|
|
|
|
inst = data[1] == 1 ? "instance" : "instances"
|
|
|
|
print_status("Importing vulnerability '#{data[0]}' (#{data[1]} #{inst})")
|
2010-06-08 19:16:20 +00:00
|
|
|
when :filetype
|
|
|
|
print_status("Importing '#{data}' data")
|
2011-05-24 19:40:50 +00:00
|
|
|
when :parser
|
|
|
|
print_status("Import: Parsing with '#{data}'")
|
2010-06-08 19:16:20 +00:00
|
|
|
when :address
|
|
|
|
print_status("Importing host #{data}")
|
2010-12-12 17:44:48 +00:00
|
|
|
when :service
|
|
|
|
print_status("Importing service #{data}")
|
|
|
|
when :msf_loot
|
2010-06-11 18:56:16 +00:00
|
|
|
print_status("Importing loot #{data}")
|
2010-12-12 17:44:48 +00:00
|
|
|
when :msf_task
|
2010-06-11 18:56:16 +00:00
|
|
|
print_status("Importing task #{data}")
|
2010-12-12 17:44:48 +00:00
|
|
|
when :msf_report
|
2010-06-11 18:56:16 +00:00
|
|
|
print_status("Importing report #{data}")
|
2010-12-16 21:36:00 +00:00
|
|
|
when :pcap_count
|
|
|
|
print_status("Import: #{data} packets processed")
|
|
|
|
when :record_count
|
|
|
|
print_status("Import: #{data[1]} records processed")
|
2011-04-20 18:33:27 +00:00
|
|
|
when :warning
|
|
|
|
print_error("")
|
|
|
|
data.split("\n").each do |line|
|
|
|
|
print_error(line)
|
|
|
|
end
|
|
|
|
print_error("")
|
|
|
|
warnings += 1
|
2010-06-08 19:16:20 +00:00
|
|
|
end
|
|
|
|
end
|
2010-01-07 21:51:18 +00:00
|
|
|
print_status("Successfully imported #{filename}")
|
2011-08-12 19:09:05 +00:00
|
|
|
|
2011-04-20 18:33:27 +00:00
|
|
|
print_error("Please note that there were #{warnings} warnings") if warnings > 1
|
|
|
|
print_error("Please note that there was one warning") if warnings == 1
|
|
|
|
|
2010-01-07 21:51:18 +00:00
|
|
|
rescue DBImportError
|
2010-01-10 17:49:28 +00:00
|
|
|
print_error("Failed to import #{filename}: #{$!}")
|
|
|
|
elog("Failed to import #{filename}: #{$!.class}: #{$!}")
|
2010-01-07 21:51:18 +00:00
|
|
|
dlog("Call stack: #{$@.join("\n")}", LEV_3)
|
|
|
|
next
|
2010-09-27 15:40:33 +00:00
|
|
|
rescue REXML::ParseException => e
|
|
|
|
print_error("Failed to import #{filename} due to malformed XML:")
|
|
|
|
print_error "#{$!.class}: #{$!}"
|
|
|
|
elog("Failed to import #{filename}: #{$!.class}: #{$!}")
|
|
|
|
dlog("Call stack: #{$@.join("\n")}", LEV_3)
|
|
|
|
next
|
2010-01-07 21:51:18 +00:00
|
|
|
end
|
|
|
|
}
|
|
|
|
}
|
2009-09-05 04:29:53 +00:00
|
|
|
end
|
|
|
|
|
2011-04-16 22:49:31 +00:00
|
|
|
def cmd_db_export_help
|
|
|
|
# Like db_hosts and db_services, this creates a list of columns, so
|
|
|
|
# use its -h
|
|
|
|
cmd_db_export("-h")
|
|
|
|
end
|
|
|
|
|
2010-10-17 04:50:15 +00:00
|
|
|
#
|
|
|
|
# Export an XML
|
|
|
|
#
|
|
|
|
def cmd_db_export(*args)
|
|
|
|
return unless active?
|
|
|
|
|
2010-11-02 19:13:15 +00:00
|
|
|
export_formats = %W{xml pwdump}
|
2010-10-17 04:50:15 +00:00
|
|
|
format = 'xml'
|
|
|
|
output = nil
|
2010-11-02 22:11:45 +00:00
|
|
|
|
2010-10-17 04:50:15 +00:00
|
|
|
while (arg = args.shift)
|
|
|
|
case arg
|
|
|
|
when '-h','--help'
|
|
|
|
print_line("Usage:")
|
2010-11-02 22:11:45 +00:00
|
|
|
print_line(" db_export -f <format> [-a] [filename]")
|
2010-11-02 19:13:15 +00:00
|
|
|
print_line(" Format can be one of: #{export_formats.join(", ")}")
|
2010-10-17 04:50:15 +00:00
|
|
|
when '-f','--format'
|
|
|
|
format = args.shift.to_s.downcase
|
|
|
|
else
|
|
|
|
output = arg
|
|
|
|
end
|
|
|
|
end
|
2011-08-12 19:09:05 +00:00
|
|
|
|
2010-10-17 04:50:15 +00:00
|
|
|
if not output
|
|
|
|
print_error("No output file was specified")
|
|
|
|
return
|
|
|
|
end
|
2011-08-12 19:09:05 +00:00
|
|
|
|
2010-11-02 19:13:15 +00:00
|
|
|
if not export_formats.include?(format)
|
2010-10-17 04:50:15 +00:00
|
|
|
print_error("Unsupported file format: #{format}")
|
2010-11-02 22:11:45 +00:00
|
|
|
print_error("Unsupported file format: '#{format}'. Must be one of: #{export_formats.join(", ")}")
|
2010-10-17 04:50:15 +00:00
|
|
|
return
|
|
|
|
end
|
2011-08-12 19:09:05 +00:00
|
|
|
|
2010-10-17 04:50:15 +00:00
|
|
|
print_status("Starting export of workspace #{framework.db.workspace.name} to #{output} [ #{format} ]...")
|
|
|
|
exporter = Msf::DBManager::Export.new(framework.db.workspace)
|
2010-11-02 19:13:15 +00:00
|
|
|
|
|
|
|
exporter.send("to_#{format}_file".intern,output) do |mtype, mstatus, mname|
|
2010-10-17 04:50:15 +00:00
|
|
|
if mtype == :status
|
|
|
|
if mstatus == "start"
|
|
|
|
print_status(" >> Starting export of #{mname}")
|
|
|
|
end
|
|
|
|
if mstatus == "complete"
|
|
|
|
print_status(" >> Finished export of #{mname}")
|
2011-08-12 19:09:05 +00:00
|
|
|
end
|
2010-10-17 04:50:15 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
print_status("Finished export of workspace #{framework.db.workspace.name} to #{output} [ #{format} ]...")
|
2010-03-03 00:34:16 +00:00
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# Import Nmap data from a file
|
|
|
|
#
|
|
|
|
def cmd_db_nmap(*args)
|
2010-05-25 01:32:30 +00:00
|
|
|
return unless active?
|
2006-09-17 22:07:52 +00:00
|
|
|
if (args.length == 0)
|
|
|
|
print_status("Usage: db_nmap [nmap options]")
|
|
|
|
return
|
2006-09-16 20:08:13 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
|
|
|
nmap =
|
|
|
|
Rex::FileUtils.find_full_path("nmap") ||
|
2007-12-31 04:05:51 +00:00
|
|
|
Rex::FileUtils.find_full_path("nmap.exe")
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2010-01-07 19:06:29 +00:00
|
|
|
if (not nmap)
|
2007-12-31 04:05:51 +00:00
|
|
|
print_error("The nmap executable could not be found")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2006-09-17 22:07:52 +00:00
|
|
|
fd = Tempfile.new('dbnmap')
|
2010-10-12 01:48:42 +00:00
|
|
|
fd.binmode
|
2011-08-12 19:09:05 +00:00
|
|
|
|
2009-11-06 17:27:28 +00:00
|
|
|
fo = Tempfile.new('dbnmap')
|
2010-10-12 01:48:42 +00:00
|
|
|
fo.binmode
|
2008-09-26 05:01:18 +00:00
|
|
|
|
2009-11-06 16:43:14 +00:00
|
|
|
# When executing native Nmap in Cygwin, expand the Cygwin path to a Win32 path
|
2009-11-06 16:03:40 +00:00
|
|
|
if(Rex::Compat.is_cygwin and nmap =~ /cygdrive/)
|
2009-11-06 16:43:14 +00:00
|
|
|
# Custom function needed because cygpath breaks on 8.3 dirs
|
|
|
|
tout = Rex::Compat.cygwin_to_win32(fd.path)
|
2009-11-06 17:27:28 +00:00
|
|
|
fout = Rex::Compat.cygwin_to_win32(fo.path)
|
2009-11-06 16:43:14 +00:00
|
|
|
args.push('-oX', tout)
|
2009-11-06 17:27:28 +00:00
|
|
|
args.push('-oN', fout)
|
2009-09-04 15:04:06 +00:00
|
|
|
else
|
|
|
|
args.push('-oX', fd.path)
|
2009-11-06 17:27:28 +00:00
|
|
|
args.push('-oN', fo.path)
|
2009-09-04 15:04:06 +00:00
|
|
|
end
|
2009-11-06 20:15:12 +00:00
|
|
|
|
2011-03-17 04:12:51 +00:00
|
|
|
begin
|
|
|
|
nmap_pipe = ::Open3::popen3([nmap, "nmap"], *args)
|
|
|
|
temp_nmap_threads = []
|
|
|
|
temp_nmap_threads << framework.threads.spawn("db_nmap-Stdout", false, nmap_pipe[1]) do |np_1|
|
|
|
|
np_1.each_line do |nmap_out|
|
|
|
|
next if nmap_out.strip.empty?
|
|
|
|
print_status "Nmap: #{nmap_out.strip}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
temp_nmap_threads << framework.threads.spawn("db_nmap-Stderr", false, nmap_pipe[2]) do |np_2|
|
2011-07-19 06:09:10 +00:00
|
|
|
np_2.each_line do |nmap_err|
|
2011-03-17 04:12:51 +00:00
|
|
|
next if nmap_err.strip.empty?
|
2011-07-19 06:09:10 +00:00
|
|
|
print_status "Nmap: '#{nmap_err.strip}'"
|
2011-03-17 04:12:51 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
temp_nmap_threads.map {|t| t.join rescue nil}
|
|
|
|
nmap_pipe.each {|p| p.close rescue nil}
|
|
|
|
rescue ::IOError
|
|
|
|
end
|
2006-09-17 22:07:52 +00:00
|
|
|
|
2010-03-03 04:58:12 +00:00
|
|
|
fo.close(true)
|
2010-06-04 14:57:58 +00:00
|
|
|
framework.db.import_nmap_xml_file(:filename => fd.path)
|
2010-03-03 04:58:12 +00:00
|
|
|
fd.close(true)
|
2006-09-16 20:08:13 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
#
|
|
|
|
# Database management
|
|
|
|
#
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
def db_check_driver
|
|
|
|
if(not framework.db.driver)
|
|
|
|
print_error("No database driver has been specified")
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2010-05-10 09:23:53 +00:00
|
|
|
#
|
|
|
|
# Is everything working?
|
|
|
|
#
|
|
|
|
def cmd_db_status(*args)
|
|
|
|
if framework.db.driver
|
|
|
|
if ActiveRecord::Base.connected? and ActiveRecord::Base.connection.active?
|
|
|
|
if ActiveRecord::Base.connection.respond_to? :current_database
|
|
|
|
cdb = ActiveRecord::Base.connection.current_database
|
|
|
|
end
|
|
|
|
print_status("#{framework.db.driver} connected to #{cdb}")
|
|
|
|
else
|
|
|
|
print_status("#{framework.db.driver} selected, no connection")
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print_status("No driver selected")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
def cmd_db_driver(*args)
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
if(args[0])
|
2009-11-14 21:41:38 +00:00
|
|
|
if(args[0] == "-h" || args[0] == "--help")
|
2009-03-28 21:42:30 +00:00
|
|
|
print_status("Usage: db_driver [driver-name]")
|
|
|
|
return
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
if(framework.db.drivers.include?(args[0]))
|
|
|
|
framework.db.driver = args[0]
|
|
|
|
print_status("Using database driver #{args[0]}")
|
|
|
|
else
|
|
|
|
print_error("Invalid driver specified")
|
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
if(framework.db.driver)
|
|
|
|
print_status(" Active Driver: #{framework.db.driver}")
|
|
|
|
else
|
|
|
|
print_status("No Active Driver")
|
|
|
|
end
|
|
|
|
print_status(" Available: #{framework.db.drivers.join(", ")}")
|
2009-11-14 21:41:38 +00:00
|
|
|
print_line("")
|
|
|
|
|
|
|
|
if ! framework.db.drivers.include?('mysql')
|
|
|
|
print_status(" DB Support: Enable the mysql driver with the following command:")
|
|
|
|
print_status(" $ gem install mysql")
|
2009-12-14 23:40:21 +00:00
|
|
|
print_status(" This gem requires mysqlclient headers, which can be installed on Ubuntu with:")
|
|
|
|
print_status(" $ sudo apt-get install libmysqlclient-dev")
|
2009-11-14 21:41:38 +00:00
|
|
|
print_line("")
|
|
|
|
end
|
|
|
|
|
|
|
|
if ! framework.db.drivers.include?('postgresql')
|
|
|
|
print_status(" DB Support: Enable the postgresql driver with the following command:")
|
2010-04-07 16:21:14 +00:00
|
|
|
print_status(" * This requires libpq-dev and a build environment")
|
2010-05-02 02:07:30 +00:00
|
|
|
print_status(" $ gem install postgres")
|
|
|
|
print_status(" $ gem install pg # is an alternative that may work")
|
2009-11-14 21:41:38 +00:00
|
|
|
print_line("")
|
|
|
|
end
|
2009-03-28 21:42:30 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
def cmd_db_driver_tabs(str, words)
|
|
|
|
return framework.db.drivers
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2011-04-16 22:49:31 +00:00
|
|
|
def cmd_db_connect_help
|
|
|
|
# Help is specific to each driver
|
|
|
|
cmd_db_connect("-h")
|
|
|
|
end
|
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
def cmd_db_connect(*args)
|
|
|
|
return if not db_check_driver
|
2010-10-23 23:20:43 +00:00
|
|
|
if (args[0] == "-y")
|
|
|
|
if (args[1] and not File.exists? File.expand_path(args[1]))
|
|
|
|
print_error("File not found")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
file = args[1] || File.join(Msf::Config.get_config_root, "database.yml")
|
|
|
|
if (File.exists? File.expand_path(file))
|
|
|
|
db = YAML.load(File.read(file))['production']
|
|
|
|
cmd_db_driver(db['adapter'])
|
|
|
|
framework.db.connect(db)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
2009-03-28 21:42:30 +00:00
|
|
|
meth = "db_connect_#{framework.db.driver}"
|
|
|
|
if(self.respond_to?(meth))
|
|
|
|
self.send(meth, *args)
|
|
|
|
else
|
2009-04-12 07:09:03 +00:00
|
|
|
print_error("This database driver #{framework.db.driver} is not currently supported")
|
2009-03-28 21:42:30 +00:00
|
|
|
end
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2011-04-16 22:49:31 +00:00
|
|
|
def cmd_db_disconnect_help
|
|
|
|
print_line "Usage: db_disconnect"
|
|
|
|
print_line
|
|
|
|
print_line "Disconnect from the database."
|
|
|
|
print_line
|
|
|
|
end
|
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
def cmd_db_disconnect(*args)
|
|
|
|
return if not db_check_driver
|
2009-11-14 21:41:38 +00:00
|
|
|
|
|
|
|
if(args[0] and (args[0] == "-h" || args[0] == "--help"))
|
2011-04-16 22:49:31 +00:00
|
|
|
cmd_db_disconnect_help
|
2009-11-14 21:41:38 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2010-05-17 04:49:17 +00:00
|
|
|
if (framework.db)
|
|
|
|
framework.db.disconnect()
|
2009-03-28 21:42:30 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2011-04-22 05:08:08 +00:00
|
|
|
#
|
|
|
|
# Set RHOSTS in the +active_module+'s (or global if none) datastore from an array of addresses
|
|
|
|
#
|
|
|
|
# This stores all the addresses to a temporary file and utilizes the
|
|
|
|
# <pre>file:/tmp/filename</pre> syntax to confer the addrs. +rhosts+
|
|
|
|
# should be an Array. NOTE: the temporary file is *not* deleted
|
|
|
|
# automatically.
|
|
|
|
#
|
|
|
|
def set_rhosts_from_addrs(rhosts)
|
|
|
|
if rhosts.empty?
|
|
|
|
print_status "The list is empty, cowardly refusing to set RHOSTS"
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if active_module
|
|
|
|
mydatastore = active_module.datastore
|
|
|
|
else
|
|
|
|
# if there is no module in use set the list to the global variable
|
|
|
|
mydatastore = self.framework.datastore
|
|
|
|
end
|
2011-04-22 18:31:55 +00:00
|
|
|
|
|
|
|
if rhosts.length > 5
|
|
|
|
# Lots of hosts makes 'show options' wrap which is difficult to
|
|
|
|
# read, store to a temp file
|
|
|
|
rhosts_file = Rex::Quickfile.new("msf-db-rhosts-")
|
|
|
|
mydatastore['RHOSTS'] = 'file:'+rhosts_file.path
|
2011-08-12 19:09:05 +00:00
|
|
|
# create the output file and assign it to the RHOSTS variable
|
2011-04-22 18:31:55 +00:00
|
|
|
rhosts_file.write(rhosts.join("\n")+"\n")
|
|
|
|
rhosts_file.close
|
|
|
|
else
|
|
|
|
# For short lists, just set it directly
|
|
|
|
mydatastore['RHOSTS'] = rhosts.join(" ")
|
|
|
|
end
|
2011-04-22 05:08:08 +00:00
|
|
|
|
|
|
|
print_line "RHOSTS => #{mydatastore['RHOSTS']}"
|
2011-07-19 06:09:10 +00:00
|
|
|
print_line
|
2011-04-22 05:08:08 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2009-11-14 21:41:38 +00:00
|
|
|
def db_find_tools(tools)
|
|
|
|
found = true
|
|
|
|
missed = []
|
|
|
|
tools.each do |name|
|
|
|
|
if(! Rex::FileUtils.find_full_path(name))
|
|
|
|
missed << name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if(not missed.empty?)
|
|
|
|
print_error("This database command requires the following tools to be installed: #{missed.join(", ")}")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
2009-12-10 15:12:59 +00:00
|
|
|
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
#
|
|
|
|
# Database management: MySQL
|
|
|
|
#
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
#
|
|
|
|
# Connect to an existing MySQL database
|
|
|
|
#
|
|
|
|
def db_connect_mysql(*args)
|
2009-11-14 21:41:38 +00:00
|
|
|
if(args[0] == nil or args[0] == "-h" or args[0] == "--help")
|
|
|
|
print_status(" Usage: db_connect <user:pass>@<host:port>/<database>")
|
2010-10-23 23:20:43 +00:00
|
|
|
print_status(" OR: db_connect -y [path/to/database.yml]")
|
2009-11-14 21:41:38 +00:00
|
|
|
print_status("Examples:")
|
|
|
|
print_status(" db_connect user@metasploit3")
|
|
|
|
print_status(" db_connect user:pass@192.168.0.2/metasploit3")
|
|
|
|
print_status(" db_connect user:pass@192.168.0.2:1500/metasploit3")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
info = db_parse_db_uri_mysql(args[0])
|
|
|
|
opts = { 'adapter' => 'mysql' }
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
opts['username'] = info[:user] if (info[:user])
|
|
|
|
opts['password'] = info[:pass] if (info[:pass])
|
|
|
|
opts['database'] = info[:name]
|
|
|
|
opts['host'] = info[:host] if (info[:host])
|
|
|
|
opts['port'] = info[:port] if (info[:port])
|
|
|
|
|
2009-11-14 21:41:38 +00:00
|
|
|
opts['host'] ||= 'localhost'
|
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
# This is an ugly hack for a broken MySQL adapter:
|
|
|
|
# http://dev.rubyonrails.org/ticket/3338
|
|
|
|
if (opts['host'].strip.downcase == 'localhost')
|
|
|
|
opts['host'] = Socket.gethostbyname("localhost")[3].unpack("C*").join(".")
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
if (not framework.db.connect(opts))
|
2009-11-14 21:41:38 +00:00
|
|
|
raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
|
2009-03-28 21:42:30 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def db_parse_db_uri_mysql(path)
|
|
|
|
res = {}
|
|
|
|
if (path)
|
|
|
|
auth, dest = path.split('@')
|
|
|
|
(dest = auth and auth = nil) if not dest
|
|
|
|
res[:user],res[:pass] = auth.split(':') if auth
|
|
|
|
targ,name = dest.split('/')
|
|
|
|
(name = targ and targ = nil) if not name
|
|
|
|
res[:host],res[:port] = targ.split(':') if targ
|
|
|
|
end
|
|
|
|
res[:name] = name || 'metasploit3'
|
|
|
|
res
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2010-05-17 04:49:17 +00:00
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
#
|
|
|
|
# Database management: Postgres
|
|
|
|
#
|
|
|
|
|
|
|
|
#
|
|
|
|
# Connect to an existing Postgres database
|
|
|
|
#
|
2009-04-12 07:09:03 +00:00
|
|
|
def db_connect_postgresql(*args)
|
2009-11-14 21:41:38 +00:00
|
|
|
if(args[0] == nil or args[0] == "-h" or args[0] == "--help")
|
|
|
|
print_status(" Usage: db_connect <user:pass>@<host:port>/<database>")
|
2010-10-23 23:20:43 +00:00
|
|
|
print_status(" OR: db_connect -y [path/to/database.yml]")
|
2009-11-14 21:41:38 +00:00
|
|
|
print_status("Examples:")
|
|
|
|
print_status(" db_connect user@metasploit3")
|
|
|
|
print_status(" db_connect user:pass@192.168.0.2/metasploit3")
|
|
|
|
print_status(" db_connect user:pass@192.168.0.2:1500/metasploit3")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2009-04-12 07:09:03 +00:00
|
|
|
info = db_parse_db_uri_postgresql(args[0])
|
2009-03-28 21:42:30 +00:00
|
|
|
opts = { 'adapter' => 'postgresql' }
|
|
|
|
|
|
|
|
opts['username'] = info[:user] if (info[:user])
|
|
|
|
opts['password'] = info[:pass] if (info[:pass])
|
|
|
|
opts['database'] = info[:name]
|
|
|
|
opts['host'] = info[:host] if (info[:host])
|
|
|
|
opts['port'] = info[:port] if (info[:port])
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2009-11-14 21:41:38 +00:00
|
|
|
opts['pass'] ||= ''
|
|
|
|
|
|
|
|
# Do a little legwork to find the real database socket
|
|
|
|
if(! opts['host'])
|
|
|
|
while(true)
|
|
|
|
done = false
|
|
|
|
dirs = %W{ /var/run/postgresql /tmp }
|
|
|
|
dirs.each do |dir|
|
|
|
|
if(::File.directory?(dir))
|
|
|
|
d = ::Dir.new(dir)
|
|
|
|
d.entries.grep(/^\.s\.PGSQL.(\d+)$/).each do |ent|
|
|
|
|
opts['port'] = ent.split('.')[-1].to_i
|
|
|
|
opts['host'] = dir
|
|
|
|
done = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
break if done
|
|
|
|
end
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Default to loopback
|
|
|
|
if(! opts['host'])
|
|
|
|
opts['host'] = '127.0.0.1'
|
|
|
|
end
|
|
|
|
|
2009-03-28 21:42:30 +00:00
|
|
|
if (not framework.db.connect(opts))
|
2009-11-14 21:41:38 +00:00
|
|
|
raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
|
2009-03-28 21:42:30 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-04-12 07:09:03 +00:00
|
|
|
def db_parse_db_uri_postgresql(path)
|
2009-03-28 21:42:30 +00:00
|
|
|
res = {}
|
|
|
|
if (path)
|
|
|
|
auth, dest = path.split('@')
|
|
|
|
(dest = auth and auth = nil) if not dest
|
|
|
|
res[:user],res[:pass] = auth.split(':') if auth
|
|
|
|
targ,name = dest.split('/')
|
|
|
|
(name = targ and targ = nil) if not name
|
|
|
|
res[:host],res[:port] = targ.split(':') if targ
|
|
|
|
end
|
|
|
|
res[:name] = name || 'metasploit3'
|
|
|
|
res
|
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|
2011-07-20 00:33:52 +00:00
|
|
|
|
|
|
|
##
|
|
|
|
# Miscellaneous option helpers
|
|
|
|
##
|
|
|
|
|
|
|
|
#
|
|
|
|
# Parse +arg+ into a RangeWalker and append the result into +host_ranges+
|
|
|
|
#
|
|
|
|
# Returns true if parsing was successful or nil otherwise.
|
|
|
|
#
|
|
|
|
# NOTE: This modifies +host_ranges+
|
|
|
|
#
|
|
|
|
def arg_host_range(arg, host_ranges, required=false)
|
|
|
|
if (!arg and required)
|
|
|
|
print_error("Missing required host argument")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
begin
|
|
|
|
host_ranges << Rex::Socket::RangeWalker.new(arg)
|
|
|
|
rescue
|
|
|
|
print_error "Invalid host parameter, #{arg}."
|
|
|
|
return
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Parse +arg+ into an array of ports and append the result into +port_ranges+
|
|
|
|
#
|
|
|
|
# Returns true if parsing was successful or nil otherwise.
|
|
|
|
#
|
|
|
|
# NOTE: This modifies +port_ranges+
|
|
|
|
#
|
|
|
|
def arg_port_range(arg, port_ranges, required=false)
|
|
|
|
if (!arg and required)
|
|
|
|
print_error("Argument required for -p")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
begin
|
|
|
|
port_ranges << Rex::Socket.portspec_to_portlist(arg)
|
|
|
|
rescue
|
|
|
|
print_error "Invalid port parameter, #{arg}."
|
|
|
|
return
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2011-07-20 04:39:52 +00:00
|
|
|
#
|
|
|
|
# Takes +host_ranges+, an Array of RangeWalkers, and chunks it up into
|
|
|
|
# blocks of 1024.
|
|
|
|
#
|
|
|
|
def each_host_range_chunk(host_ranges, &block)
|
|
|
|
# Chunk it up and do the query in batches. The naive implementation
|
|
|
|
# uses so much memory for a /8 that it's basically unusable (1.6
|
|
|
|
# billion IP addresses take a rather long time to allocate).
|
|
|
|
# Chunking has roughly the same perfomance for small batches, so
|
|
|
|
# don't worry about it too much.
|
|
|
|
host_ranges.each do |range|
|
2011-08-12 19:09:05 +00:00
|
|
|
if range.nil? or range.length.nil?
|
2011-07-20 04:39:52 +00:00
|
|
|
chunk = nil
|
|
|
|
end_of_range = true
|
|
|
|
else
|
|
|
|
chunk = []
|
|
|
|
end_of_range = false
|
|
|
|
# Set up this chunk of hosts to search for
|
|
|
|
while chunk.length < 1024 and chunk.length < range.length
|
|
|
|
n = range.next_ip
|
|
|
|
if n.nil?
|
|
|
|
end_of_range = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
chunk << n
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-08-12 19:09:05 +00:00
|
|
|
# The block will do some
|
2011-07-20 04:39:52 +00:00
|
|
|
yield chunk
|
|
|
|
|
|
|
|
# Restart the loop with the same RangeWalker if we didn't get
|
|
|
|
# to the end of it in this chunk.
|
|
|
|
redo unless end_of_range
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2006-05-30 15:44:48 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2008-10-27 22:47:09 +00:00
|
|
|
end
|
2009-11-06 16:01:24 +00:00
|
|
|
|