Merge branch 'master' into feature/uuid-registration
commit
a8d111ce89
|
@ -75,8 +75,10 @@ module Metasploit
|
||||||
def to_s
|
def to_s
|
||||||
if realm && realm_key == Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
|
if realm && realm_key == Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
|
||||||
"#{self.realm}\\#{self.public}:#{self.private}"
|
"#{self.realm}\\#{self.public}:#{self.private}"
|
||||||
else
|
elsif self.private
|
||||||
"#{self.public}:#{self.private}#{at_realm}"
|
"#{self.public}:#{self.private}#{at_realm}"
|
||||||
|
else
|
||||||
|
self.public
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -566,7 +566,6 @@ module Auxiliary::AuthBrute
|
||||||
else
|
else
|
||||||
level = opts[:level].to_s.strip
|
level = opts[:level].to_s.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
host_ip = opts[:ip] || opts[:rhost] || opts[:host] || (rhost rescue nil) || datastore['RHOST']
|
host_ip = opts[:ip] || opts[:rhost] || opts[:host] || (rhost rescue nil) || datastore['RHOST']
|
||||||
host_port = opts[:port] || opts[:rport] || (rport rescue nil) || datastore['RPORT']
|
host_port = opts[:port] || opts[:rport] || (rport rescue nil) || datastore['RPORT']
|
||||||
msg = opts[:msg] || opts[:message] || opts[:legacy_msg]
|
msg = opts[:msg] || opts[:message] || opts[:legacy_msg]
|
||||||
|
|
|
@ -12,10 +12,19 @@ module Auxiliary::Report
|
||||||
|
|
||||||
optionally_include_metasploit_credential_creation
|
optionally_include_metasploit_credential_creation
|
||||||
|
|
||||||
|
def db_warning_given?
|
||||||
|
if @warning_issued
|
||||||
|
true
|
||||||
|
else
|
||||||
|
@warning_issued = true
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def create_cracked_credential(opts={})
|
def create_cracked_credential(opts={})
|
||||||
if active_db?
|
if active_db?
|
||||||
super(opts)
|
super(opts)
|
||||||
else
|
elsif !db_warning_given?
|
||||||
vprint_warning('No active DB -- Credential data will not be saved!')
|
vprint_warning('No active DB -- Credential data will not be saved!')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -23,7 +32,7 @@ module Auxiliary::Report
|
||||||
def create_credential(opts={})
|
def create_credential(opts={})
|
||||||
if active_db?
|
if active_db?
|
||||||
super(opts)
|
super(opts)
|
||||||
else
|
elsif !db_warning_given?
|
||||||
vprint_warning('No active DB -- Credential data will not be saved!')
|
vprint_warning('No active DB -- Credential data will not be saved!')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -31,7 +40,7 @@ module Auxiliary::Report
|
||||||
def create_credential_login(opts={})
|
def create_credential_login(opts={})
|
||||||
if active_db?
|
if active_db?
|
||||||
super(opts)
|
super(opts)
|
||||||
else
|
elsif !db_warning_given?
|
||||||
vprint_warning('No active DB -- Credential data will not be saved!')
|
vprint_warning('No active DB -- Credential data will not be saved!')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -39,7 +48,7 @@ module Auxiliary::Report
|
||||||
def invalidate_login(opts={})
|
def invalidate_login(opts={})
|
||||||
if active_db?
|
if active_db?
|
||||||
super(opts)
|
super(opts)
|
||||||
else
|
elsif !db_warning_given?
|
||||||
vprint_warning('No active DB -- Credential data will not be saved!')
|
vprint_warning('No active DB -- Credential data will not be saved!')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -129,6 +129,8 @@ module Msf::DBManager::Import
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Override REXML's expansion text limit to 50k (default: 10240 bytes)
|
||||||
|
REXML::Security.entity_expansion_text_limit = 51200
|
||||||
|
|
||||||
if block
|
if block
|
||||||
import(args.merge(:data => data)) { |type,data| yield type,data }
|
import(args.merge(:data => data)) { |type,data| yield type,data }
|
||||||
|
|
|
@ -30,6 +30,18 @@ class Console::CommandDispatcher::Stdapi::Fs
|
||||||
@@upload_opts = Rex::Parser::Arguments.new(
|
@@upload_opts = Rex::Parser::Arguments.new(
|
||||||
"-h" => [ false, "Help banner." ],
|
"-h" => [ false, "Help banner." ],
|
||||||
"-r" => [ false, "Upload recursively." ])
|
"-r" => [ false, "Upload recursively." ])
|
||||||
|
#
|
||||||
|
# Options for the ls command
|
||||||
|
#
|
||||||
|
@@ls_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner." ],
|
||||||
|
"-S" => [ true, "Search string." ],
|
||||||
|
"-t" => [ false, "Sort by time" ],
|
||||||
|
"-s" => [ false, "Sort by size" ],
|
||||||
|
"-r" => [ false, "Reverse sort order" ],
|
||||||
|
"-x" => [ false, "Show short file names" ],
|
||||||
|
"-l" => [ false, "List in long format (default)" ],
|
||||||
|
"-R" => [ false, "Recursively list subdirectories encountered" ])
|
||||||
|
|
||||||
#
|
#
|
||||||
# List of supported commands.
|
# List of supported commands.
|
||||||
|
@ -231,15 +243,27 @@ class Console::CommandDispatcher::Stdapi::Fs
|
||||||
print_line("Usage: mv oldfile newfile")
|
print_line("Usage: mv oldfile newfile")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
client.fs.file.mv(args[0],args[1])
|
client.fs.file.mv(args[0],args[1])
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
alias :cmd_move :cmd_mv
|
alias :cmd_move :cmd_mv
|
||||||
alias :cmd_rename :cmd_mv
|
alias :cmd_rename :cmd_mv
|
||||||
|
|
||||||
|
#
|
||||||
|
# Move source to destination
|
||||||
|
#
|
||||||
|
def cmd_cp(*args)
|
||||||
|
if (args.length < 2)
|
||||||
|
print_line("Usage: cp oldfile newfile")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
client.fs.file.cp(args[0],args[1])
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
alias :cmd_copy :cmd_cp
|
||||||
|
|
||||||
|
|
||||||
def cmd_download_help
|
def cmd_download_help
|
||||||
print_line("Usage: download [options] src1 src2 src3 ... destination")
|
print_line("Usage: download [options] src1 src2 src3 ... destination")
|
||||||
|
@ -387,7 +411,15 @@ class Console::CommandDispatcher::Stdapi::Fs
|
||||||
|
|
||||||
alias cmd_getlwd cmd_lpwd
|
alias cmd_getlwd cmd_lpwd
|
||||||
|
|
||||||
def list_path(path, columns, sort, order, short, recursive = false, depth = 0)
|
|
||||||
|
def cmd_ls_help
|
||||||
|
print_line "Usage: ls [options]"
|
||||||
|
print_line
|
||||||
|
print_line "Lists contents of directory or file info, searchable"
|
||||||
|
print_line @@ls_opts.usage
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_path(path, columns, sort, order, short, recursive = false, depth = 0, search_term = nil)
|
||||||
|
|
||||||
# avoid infinite recursion
|
# avoid infinite recursion
|
||||||
if depth > 100
|
if depth > 100
|
||||||
|
@ -398,7 +430,8 @@ class Console::CommandDispatcher::Stdapi::Fs
|
||||||
'Header' => "Listing: #{path}",
|
'Header' => "Listing: #{path}",
|
||||||
'SortIndex' => columns.index(sort),
|
'SortIndex' => columns.index(sort),
|
||||||
'SortOrder' => order,
|
'SortOrder' => order,
|
||||||
'Columns' => columns)
|
'Columns' => columns,
|
||||||
|
'SearchTerm' => search_term)
|
||||||
|
|
||||||
items = 0
|
items = 0
|
||||||
|
|
||||||
|
@ -419,8 +452,10 @@ class Console::CommandDispatcher::Stdapi::Fs
|
||||||
row.insert(4, p['FileShortName'] || '') if short
|
row.insert(4, p['FileShortName'] || '') if short
|
||||||
|
|
||||||
if fname != '.' && fname != '..'
|
if fname != '.' && fname != '..'
|
||||||
|
if row.join(' ') =~ /#{search_term}/
|
||||||
tbl << row
|
tbl << row
|
||||||
items += 1
|
items += 1
|
||||||
|
end
|
||||||
|
|
||||||
if recursive && ffstat && ffstat.directory?
|
if recursive && ffstat && ffstat.directory?
|
||||||
if client.fs.file.is_glob?(path)
|
if client.fs.file.is_glob?(path)
|
||||||
|
@ -430,7 +465,7 @@ class Console::CommandDispatcher::Stdapi::Fs
|
||||||
child_path = path + ::File::SEPARATOR + fname
|
child_path = path + ::File::SEPARATOR + fname
|
||||||
end
|
end
|
||||||
begin
|
begin
|
||||||
list_path(child_path, columns, sort, order, short, recursive, depth + 1)
|
list_path(child_path, columns, sort, order, short, recursive, depth + 1, search_term)
|
||||||
rescue RequestError
|
rescue RequestError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -448,39 +483,48 @@ class Console::CommandDispatcher::Stdapi::Fs
|
||||||
# Lists files
|
# Lists files
|
||||||
#
|
#
|
||||||
def cmd_ls(*args)
|
def cmd_ls(*args)
|
||||||
|
# Set defaults
|
||||||
|
path = client.fs.dir.getwd
|
||||||
|
search_term = nil
|
||||||
|
sort = 'Name'
|
||||||
|
short = nil
|
||||||
|
order = :forward
|
||||||
|
recursive = nil
|
||||||
|
|
||||||
# Check sort column
|
# Parse the args
|
||||||
sort = args.include?('-S') ? 'Size' : 'Name'
|
@@ls_opts.parse(args) { |opt, idx, val|
|
||||||
sort = args.include?('-t') ? 'Last modified' : sort
|
case opt
|
||||||
args.delete('-S')
|
# Sort options
|
||||||
args.delete('-t')
|
when '-s'
|
||||||
|
sort = 'Size'
|
||||||
# Check whether to include the short name option
|
when '-t'
|
||||||
short = args.include?('-x')
|
sort = 'Last modified'
|
||||||
args.delete('-x')
|
# Output options
|
||||||
|
when '-x'
|
||||||
# Check sort order
|
short = true
|
||||||
order = args.include?('-r') ? :reverse : :forward
|
when '-l'
|
||||||
args.delete('-r')
|
short = nil
|
||||||
|
when '-r'
|
||||||
# Check for recursive mode
|
order = :reverse
|
||||||
recursive = !args.delete('-R').nil?
|
when '-R'
|
||||||
|
recursive = true
|
||||||
args.delete('-l')
|
# Search
|
||||||
|
when '-S'
|
||||||
# Check for cries of help
|
search_term = val
|
||||||
if args.length > 1 || args.any? { |a| a[0] == '-' }
|
if search_term.nil?
|
||||||
print_line('Usage: ls [dir] [-x] [-S] [-t] [-r]')
|
print_error("Enter a search term")
|
||||||
print_line(' -x Show short file names')
|
|
||||||
print_line(' -S Sort by size')
|
|
||||||
print_line(' -t Sort by time modified')
|
|
||||||
print_line(' -r Reverse sort order')
|
|
||||||
print_line(' -l List in long format (default)')
|
|
||||||
print_line(' -R Recursively list subdirectories encountered.')
|
|
||||||
return true
|
return true
|
||||||
|
else
|
||||||
|
search_term = /#{search_term}/nmi
|
||||||
end
|
end
|
||||||
|
# Help and path
|
||||||
path = args[0] || client.fs.dir.getwd
|
when "-h"
|
||||||
|
cmd_ls_help
|
||||||
|
return 0
|
||||||
|
when nil
|
||||||
|
path = val
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
columns = [ 'Mode', 'Size', 'Type', 'Last modified', 'Name' ]
|
columns = [ 'Mode', 'Size', 'Type', 'Last modified', 'Name' ]
|
||||||
columns.insert(4, 'Short Name') if short
|
columns.insert(4, 'Short Name') if short
|
||||||
|
@ -499,7 +543,7 @@ class Console::CommandDispatcher::Stdapi::Fs
|
||||||
|
|
||||||
stat = client.fs.file.stat(stat_path)
|
stat = client.fs.file.stat(stat_path)
|
||||||
if stat.directory?
|
if stat.directory?
|
||||||
list_path(path, columns, sort, order, short, recursive)
|
list_path(path, columns, sort, order, short, recursive, 0, search_term)
|
||||||
else
|
else
|
||||||
print_line("#{stat.prettymode} #{stat.size} #{stat.ftype[0,3]} #{stat.mtime} #{path}")
|
print_line("#{stat.prettymode} #{stat.size} #{stat.ftype[0,3]} #{stat.mtime} #{path}")
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,6 +51,20 @@ class Console::CommandDispatcher::Stdapi::Net
|
||||||
"-p" => [ true, "The remote port to connect to." ],
|
"-p" => [ true, "The remote port to connect to." ],
|
||||||
"-L" => [ true, "The local host to listen on (optional)." ])
|
"-L" => [ true, "The local host to listen on (optional)." ])
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options for the netstat command.
|
||||||
|
#
|
||||||
|
@@netstat_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner." ],
|
||||||
|
"-S" => [ true, "Search string." ])
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options for ARP command.
|
||||||
|
#
|
||||||
|
@@arp_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-h" => [ false, "Help banner." ],
|
||||||
|
"-S" => [ true, "Search string." ])
|
||||||
|
|
||||||
#
|
#
|
||||||
# List of supported commands.
|
# List of supported commands.
|
||||||
#
|
#
|
||||||
|
@ -107,6 +121,23 @@ class Console::CommandDispatcher::Stdapi::Net
|
||||||
#
|
#
|
||||||
def cmd_netstat(*args)
|
def cmd_netstat(*args)
|
||||||
connection_table = client.net.config.netstat
|
connection_table = client.net.config.netstat
|
||||||
|
search_term = nil
|
||||||
|
@@netstat_opts.parse(args) { |opt, idx, val|
|
||||||
|
case opt
|
||||||
|
when '-S'
|
||||||
|
search_term = val
|
||||||
|
if search_term.nil?
|
||||||
|
print_error("Enter a search term")
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
search_term = /#{search_term}/nmi
|
||||||
|
end
|
||||||
|
when "-h"
|
||||||
|
@@netstat_opts.usage
|
||||||
|
return 0
|
||||||
|
|
||||||
|
end
|
||||||
|
}
|
||||||
tbl = Rex::Ui::Text::Table.new(
|
tbl = Rex::Ui::Text::Table.new(
|
||||||
'Header' => "Connection list",
|
'Header' => "Connection list",
|
||||||
'Indent' => 4,
|
'Indent' => 4,
|
||||||
|
@ -119,7 +150,8 @@ class Console::CommandDispatcher::Stdapi::Net
|
||||||
"User",
|
"User",
|
||||||
"Inode",
|
"Inode",
|
||||||
"PID/Program name"
|
"PID/Program name"
|
||||||
])
|
],
|
||||||
|
'SearchTerm' => search_term)
|
||||||
|
|
||||||
connection_table.each { |connection|
|
connection_table.each { |connection|
|
||||||
tbl << [ connection.protocol, connection.local_addr_str, connection.remote_addr_str,
|
tbl << [ connection.protocol, connection.local_addr_str, connection.remote_addr_str,
|
||||||
|
@ -138,6 +170,23 @@ class Console::CommandDispatcher::Stdapi::Net
|
||||||
#
|
#
|
||||||
def cmd_arp(*args)
|
def cmd_arp(*args)
|
||||||
arp_table = client.net.config.arp_table
|
arp_table = client.net.config.arp_table
|
||||||
|
search_term = nil
|
||||||
|
@@arp_opts.parse(args) { |opt, idx, val|
|
||||||
|
case opt
|
||||||
|
when '-S'
|
||||||
|
search_term = val
|
||||||
|
if search_term.nil?
|
||||||
|
print_error("Enter a search term")
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
search_term = /#{search_term}/nmi
|
||||||
|
end
|
||||||
|
when "-h"
|
||||||
|
@@arp_opts.usage
|
||||||
|
return 0
|
||||||
|
|
||||||
|
end
|
||||||
|
}
|
||||||
tbl = Rex::Ui::Text::Table.new(
|
tbl = Rex::Ui::Text::Table.new(
|
||||||
'Header' => "ARP cache",
|
'Header' => "ARP cache",
|
||||||
'Indent' => 4,
|
'Indent' => 4,
|
||||||
|
@ -146,7 +195,8 @@ class Console::CommandDispatcher::Stdapi::Net
|
||||||
"IP address",
|
"IP address",
|
||||||
"MAC address",
|
"MAC address",
|
||||||
"Interface"
|
"Interface"
|
||||||
])
|
],
|
||||||
|
'SearchTerm' => search_term)
|
||||||
|
|
||||||
arp_table.each { |arp|
|
arp_table.each { |arp|
|
||||||
tbl << [ arp.ip_addr, arp.mac_addr, arp.interface ]
|
tbl << [ arp.ip_addr, arp.mac_addr, arp.interface ]
|
||||||
|
|
|
@ -63,8 +63,8 @@ class Console::CommandDispatcher::Stdapi::Sys
|
||||||
# Options for the 'ps' command.
|
# Options for the 'ps' command.
|
||||||
#
|
#
|
||||||
@@ps_opts = Rex::Parser::Arguments.new(
|
@@ps_opts = Rex::Parser::Arguments.new(
|
||||||
|
"-S" => [ true, "String to search for (converts to regex)" ],
|
||||||
"-h" => [ false, "Help menu." ],
|
"-h" => [ false, "Help menu." ],
|
||||||
"-S" => [ true, "Filters processes on the process name using the supplied RegEx"],
|
|
||||||
"-A" => [ true, "Filters processes on architecture (x86 or x86_64)" ],
|
"-A" => [ true, "Filters processes on architecture (x86 or x86_64)" ],
|
||||||
"-s" => [ false, "Show only SYSTEM processes" ],
|
"-s" => [ false, "Show only SYSTEM processes" ],
|
||||||
"-U" => [ true, "Filters processes on the user using the supplied RegEx" ])
|
"-U" => [ true, "Filters processes on the user using the supplied RegEx" ])
|
||||||
|
@ -422,23 +422,27 @@ class Console::CommandDispatcher::Stdapi::Sys
|
||||||
# Lists running processes.
|
# Lists running processes.
|
||||||
#
|
#
|
||||||
def cmd_ps(*args)
|
def cmd_ps(*args)
|
||||||
|
# Init vars
|
||||||
processes = client.sys.process.get_processes
|
processes = client.sys.process.get_processes
|
||||||
@@ps_opts.parse(args) do |opt, idx, val|
|
search_term = nil
|
||||||
|
|
||||||
|
# Parse opts
|
||||||
|
@@ps_opts.parse(args) { |opt, idx, val|
|
||||||
case opt
|
case opt
|
||||||
when "-h"
|
when '-S'
|
||||||
cmd_ps_help
|
search_term = val
|
||||||
|
if search_term.nil?
|
||||||
|
print_error("Enter a search term")
|
||||||
return true
|
return true
|
||||||
when "-S"
|
|
||||||
print_line "Filtering on process name..."
|
|
||||||
searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new
|
|
||||||
processes.each do |proc|
|
|
||||||
if val.nil? or val.empty?
|
|
||||||
print_line "You must supply a search term!"
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
searched_procs << proc if proc["name"].match(/#{val}/)
|
when '-h'
|
||||||
end
|
print_line "Usage: ps [ options ]"
|
||||||
processes = searched_procs
|
print_line
|
||||||
|
print_line "OPTIONS:"
|
||||||
|
print_line " -S Search string to filter by"
|
||||||
|
print_line " -h This help menu"
|
||||||
|
print_line
|
||||||
|
return 0
|
||||||
when "-A"
|
when "-A"
|
||||||
print_line "Filtering on arch..."
|
print_line "Filtering on arch..."
|
||||||
searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new
|
searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new
|
||||||
|
@ -470,12 +474,44 @@ class Console::CommandDispatcher::Stdapi::Sys
|
||||||
end
|
end
|
||||||
processes = searched_procs
|
processes = searched_procs
|
||||||
end
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
tbl = Rex::Ui::Text::Table.new(
|
||||||
|
'Header' => "Process list",
|
||||||
|
'Indent' => 1,
|
||||||
|
'Columns' =>
|
||||||
|
[
|
||||||
|
"PID",
|
||||||
|
"Name",
|
||||||
|
"Arch",
|
||||||
|
"Session",
|
||||||
|
"User",
|
||||||
|
"Path"
|
||||||
|
],
|
||||||
|
'SearchTerm' => search_term)
|
||||||
|
|
||||||
|
processes.each { |ent|
|
||||||
|
|
||||||
|
session = ent['session'] == 0xFFFFFFFF ? '' : ent['session'].to_s
|
||||||
|
arch = ent['arch']
|
||||||
|
|
||||||
|
# for display and consistency with payload naming we switch the internal 'x86_64' value to display 'x64'
|
||||||
|
if( arch == ARCH_X86_64 )
|
||||||
|
arch = "x64"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
row = [ ent['pid'].to_s, ent['name'], arch, session, ent['user'], ent['path'] ]
|
||||||
|
|
||||||
|
tbl << row #if (search_term.nil? or row.join(' ').to_s.match(search_term))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (processes.length == 0)
|
if (processes.length == 0)
|
||||||
print_line("No running processes were found.")
|
print_line("No running processes were found.")
|
||||||
else
|
else
|
||||||
print_line
|
print_line
|
||||||
print_line(processes.to_table("Indent" => 1).to_s)
|
print("\n" + tbl.to_s + "\n")
|
||||||
print_line
|
print_line
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
@ -672,7 +708,7 @@ class Console::CommandDispatcher::Stdapi::Sys
|
||||||
|
|
||||||
open_key.set_value(value, client.sys.registry.type2str(type), data)
|
open_key.set_value(value, client.sys.registry.type2str(type), data)
|
||||||
|
|
||||||
print_line("Successful set #{value}.")
|
print_line("Successfully set #{value} of #{type}.")
|
||||||
|
|
||||||
when "deleteval"
|
when "deleteval"
|
||||||
if (value == nil)
|
if (value == nil)
|
||||||
|
@ -912,4 +948,3 @@ end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ class Table
|
||||||
self.prefix = opts['Prefix'] || ''
|
self.prefix = opts['Prefix'] || ''
|
||||||
self.postfix = opts['Postfix'] || ''
|
self.postfix = opts['Postfix'] || ''
|
||||||
self.colprops = []
|
self.colprops = []
|
||||||
|
self.scterm = /#{opts['SearchTerm']}/mi if opts['SearchTerm']
|
||||||
|
|
||||||
self.sort_index = opts['SortIndex'] || 0
|
self.sort_index = opts['SortIndex'] || 0
|
||||||
self.sort_order = opts['SortOrder'] || :forward
|
self.sort_order = opts['SortOrder'] || :forward
|
||||||
|
@ -113,7 +114,7 @@ class Table
|
||||||
if (is_hr(row))
|
if (is_hr(row))
|
||||||
str << hr_to_s
|
str << hr_to_s
|
||||||
else
|
else
|
||||||
str << row_to_s(row)
|
str << row_to_s(row) if row_visible(row)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,10 +130,9 @@ class Table
|
||||||
str = ''
|
str = ''
|
||||||
str << ( columns.join(",") + "\n" )
|
str << ( columns.join(",") + "\n" )
|
||||||
rows.each { |row|
|
rows.each { |row|
|
||||||
next if is_hr(row)
|
next if is_hr(row) || !row_visible(row)
|
||||||
str << ( row.map{|x|
|
str << ( row.map{|x|
|
||||||
x = x.to_s
|
x = x.to_s
|
||||||
|
|
||||||
x.gsub(/[\r\n]/, ' ').gsub(/\s+/, ' ').gsub('"', '""')
|
x.gsub(/[\r\n]/, ' ').gsub(/\s+/, ' ').gsub('"', '""')
|
||||||
}.map{|x| "\"#{x}\"" }.join(",") + "\n" )
|
}.map{|x| "\"#{x}\"" }.join(",") + "\n" )
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,10 @@ class Table
|
||||||
raise RuntimeError, 'Invalid number of columns!'
|
raise RuntimeError, 'Invalid number of columns!'
|
||||||
end
|
end
|
||||||
fields.each_with_index { |field, idx|
|
fields.each_with_index { |field, idx|
|
||||||
|
# Remove whitespace and ensure String format
|
||||||
|
field = field.to_s.strip
|
||||||
if (colprops[idx]['MaxWidth'] < field.to_s.length)
|
if (colprops[idx]['MaxWidth'] < field.to_s.length)
|
||||||
|
old = colprops[idx]['MaxWidth']
|
||||||
colprops[idx]['MaxWidth'] = field.to_s.length
|
colprops[idx]['MaxWidth'] = field.to_s.length
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
@ -217,6 +220,51 @@ class Table
|
||||||
#
|
#
|
||||||
# Returns new sub-table with headers and rows maching column names submitted
|
# Returns new sub-table with headers and rows maching column names submitted
|
||||||
#
|
#
|
||||||
|
#
|
||||||
|
# Flips table 90 degrees left
|
||||||
|
#
|
||||||
|
def drop_left
|
||||||
|
tbl = self.class.new(
|
||||||
|
'Columns' => Array.new(self.rows.count+1,' '),
|
||||||
|
'Header' => self.header,
|
||||||
|
'Indent' => self.indent)
|
||||||
|
(self.columns.count+1).times do |ti|
|
||||||
|
row = self.rows.map {|r| r[ti]}.unshift(self.columns[ti]).flatten
|
||||||
|
# insert our col|row break. kind of hackish
|
||||||
|
row[1] = "| #{row[1]}" unless row.all? {|e| e.nil? || e.empty?}
|
||||||
|
tbl << row
|
||||||
|
end
|
||||||
|
return tbl
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Build table from CSV dump
|
||||||
|
#
|
||||||
|
def self.new_from_csv(csv)
|
||||||
|
# Read in or keep data, get CSV or die
|
||||||
|
if csv.is_a?(String)
|
||||||
|
csv = File.file?(csv) ? CSV.read(csv) : CSV.parse(csv)
|
||||||
|
end
|
||||||
|
# Adjust for skew
|
||||||
|
if csv.first == ["Keys", "Values"]
|
||||||
|
csv.shift # drop marker
|
||||||
|
cols = []
|
||||||
|
rows = []
|
||||||
|
csv.each do |row|
|
||||||
|
cols << row.shift
|
||||||
|
rows << row
|
||||||
|
end
|
||||||
|
tbl = self.new('Columns' => cols)
|
||||||
|
rows.in_groups_of(cols.count) {|r| tbl << r.flatten}
|
||||||
|
else
|
||||||
|
tbl = self.new('Columns' => csv.shift)
|
||||||
|
while !csv.empty? do
|
||||||
|
tbl << csv.shift
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tbl
|
||||||
|
end
|
||||||
|
|
||||||
def [](*col_names)
|
def [](*col_names)
|
||||||
tbl = self.class.new('Indent' => self.indent,
|
tbl = self.class.new('Indent' => self.indent,
|
||||||
'Header' => self.header,
|
'Header' => self.header,
|
||||||
|
@ -245,10 +293,18 @@ class Table
|
||||||
attr_accessor :columns, :rows, :colprops # :nodoc:
|
attr_accessor :columns, :rows, :colprops # :nodoc:
|
||||||
attr_accessor :width, :indent, :cellpad # :nodoc:
|
attr_accessor :width, :indent, :cellpad # :nodoc:
|
||||||
attr_accessor :prefix, :postfix # :nodoc:
|
attr_accessor :prefix, :postfix # :nodoc:
|
||||||
attr_accessor :sort_index, :sort_order # :nodoc:
|
attr_accessor :sort_index, :sort_order, :scterm # :nodoc:
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns if a row should be visible or not
|
||||||
|
#
|
||||||
|
def row_visible(row)
|
||||||
|
return true if self.scterm.nil?
|
||||||
|
row_to_s(row).match(self.scterm)
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Defaults cell widths and alignments.
|
# Defaults cell widths and alignments.
|
||||||
#
|
#
|
||||||
|
@ -274,14 +330,15 @@ protected
|
||||||
last_idx = nil
|
last_idx = nil
|
||||||
columns.each_with_index { |col,idx|
|
columns.each_with_index { |col,idx|
|
||||||
if (last_col)
|
if (last_col)
|
||||||
nameline << pad(' ', last_col, last_idx)
|
# This produces clean to_s output without truncation
|
||||||
|
# Preserves full string in cells for to_csv output
|
||||||
remainder = colprops[last_idx]['MaxWidth'] - last_col.length
|
padding = pad(' ', last_col, last_idx)
|
||||||
if (remainder < 0)
|
nameline << padding
|
||||||
remainder = 0
|
remainder = padding.length - cellpad
|
||||||
end
|
remainder = 0 if remainder < 0
|
||||||
barline << (' ' * (cellpad + remainder))
|
barline << (' ' * (cellpad + remainder))
|
||||||
end
|
end
|
||||||
|
|
||||||
nameline << col
|
nameline << col
|
||||||
barline << ('-' * col.length)
|
barline << ('-' * col.length)
|
||||||
|
|
||||||
|
@ -310,7 +367,6 @@ protected
|
||||||
if (idx != 0)
|
if (idx != 0)
|
||||||
line << pad(' ', last_cell.to_s, last_idx)
|
line << pad(' ', last_cell.to_s, last_idx)
|
||||||
end
|
end
|
||||||
# line << pad(' ', cell.to_s, idx)
|
|
||||||
# Limit wide cells
|
# Limit wide cells
|
||||||
if colprops[idx]['MaxChar']
|
if colprops[idx]['MaxChar']
|
||||||
last_cell = cell.to_s[0..colprops[idx]['MaxChar'].to_i]
|
last_cell = cell.to_s[0..colprops[idx]['MaxChar'].to_i]
|
||||||
|
@ -330,8 +386,12 @@ protected
|
||||||
# some text and a column index.
|
# some text and a column index.
|
||||||
#
|
#
|
||||||
def pad(chr, buf, colidx, use_cell_pad = true) # :nodoc:
|
def pad(chr, buf, colidx, use_cell_pad = true) # :nodoc:
|
||||||
remainder = colprops[colidx]['MaxWidth'] - buf.length
|
# Ensure we pad the minimum required amount
|
||||||
val = chr * remainder;
|
max = colprops[colidx]['MaxChar'] || colprops[colidx]['MaxWidth']
|
||||||
|
max = colprops[colidx]['MaxWidth'] if max.to_i > colprops[colidx]['MaxWidth'].to_i
|
||||||
|
remainder = max - buf.length
|
||||||
|
remainder = 0 if remainder < 0
|
||||||
|
val = chr * remainder
|
||||||
|
|
||||||
if (use_cell_pad)
|
if (use_cell_pad)
|
||||||
val << ' ' * cellpad
|
val << ' ' * cellpad
|
||||||
|
|
|
@ -88,8 +88,8 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
rx_title = Rex::Text.html_decode(rx[:title])
|
rx_title = Rex::Text.html_decode(rx[:title])
|
||||||
print_status("[#{target_host}:#{rport}] [C:#{res.code}] [R:#{location_header}] [S:#{server_header}] #{rx_title}") if datastore['SHOW_TITLES'] == true
|
print_status("[#{target_host}:#{rport}] [C:#{res.code}] [R:#{location_header}] [S:#{server_header}] #{rx_title}") if datastore['SHOW_TITLES'] == true
|
||||||
if datastore['STORE_NOTES'] == true
|
if datastore['STORE_NOTES'] == true
|
||||||
notedata = { code: res.code, port: rport, server: server_header, title: rx_title, redirect: location_header }
|
notedata = { code: res.code, port: rport, server: server_header, title: rx_title, redirect: location_header, uri: datastore['TARGETURI'] }
|
||||||
report_note(host: target_host, type: "http.title", data: notedata)
|
report_note(host: target_host, port: rport, type: "http.title", data: notedata, update: :unique_data)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print_error("[#{target_host}:#{rport}] No webpage title") if datastore['SHOW_ERRORS'] == true
|
print_error("[#{target_host}:#{rport}] No webpage title") if datastore['SHOW_ERRORS'] == true
|
||||||
|
|
|
@ -85,7 +85,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential} (Access level: #{result.access_level})"
|
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential} (Access level: #{result.access_level})"
|
||||||
else
|
else
|
||||||
invalidate_login(credential_data)
|
invalidate_login(credential_data)
|
||||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
print_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status})"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -293,13 +293,11 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
capturedtime = Time.now.to_s
|
capturedtime = Time.now.to_s
|
||||||
case ntlm_ver
|
case ntlm_ver
|
||||||
when NTLM_CONST::NTLM_V1_RESPONSE
|
when NTLM_CONST::NTLM_V1_RESPONSE
|
||||||
smb_db_type_hash = "smb_netv1_hash"
|
|
||||||
capturelogmessage =
|
capturelogmessage =
|
||||||
"#{capturedtime}\nNTLMv1 Response Captured from #{host} \n" +
|
"#{capturedtime}\nNTLMv1 Response Captured from #{host} \n" +
|
||||||
"DOMAIN: #{domain} USER: #{user} \n" +
|
"DOMAIN: #{domain} USER: #{user} \n" +
|
||||||
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} \nNTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n"
|
"LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} \nNTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n"
|
||||||
when NTLM_CONST::NTLM_V2_RESPONSE
|
when NTLM_CONST::NTLM_V2_RESPONSE
|
||||||
smb_db_type_hash = "smb_netv2_hash"
|
|
||||||
capturelogmessage =
|
capturelogmessage =
|
||||||
"#{capturedtime}\nNTLMv2 Response Captured from #{host} \n" +
|
"#{capturedtime}\nNTLMv2 Response Captured from #{host} \n" +
|
||||||
"DOMAIN: #{domain} USER: #{user} \n" +
|
"DOMAIN: #{domain} USER: #{user} \n" +
|
||||||
|
@ -310,7 +308,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
when NTLM_CONST::NTLM_2_SESSION_RESPONSE
|
when NTLM_CONST::NTLM_2_SESSION_RESPONSE
|
||||||
# we can consider those as netv1 has they have the same size and i cracked the same way by cain/jtr
|
# we can consider those as netv1 has they have the same size and i cracked the same way by cain/jtr
|
||||||
# also 'real' netv1 is almost never seen nowadays except with smbmount or msf server capture
|
# also 'real' netv1 is almost never seen nowadays except with smbmount or msf server capture
|
||||||
smb_db_type_hash = "smb_netv1_hash"
|
|
||||||
capturelogmessage =
|
capturelogmessage =
|
||||||
"#{capturedtime}\nNTLM2_SESSION Response Captured from #{host} \n" +
|
"#{capturedtime}\nNTLM2_SESSION Response Captured from #{host} \n" +
|
||||||
"DOMAIN: #{domain} USER: #{user} \n" +
|
"DOMAIN: #{domain} USER: #{user} \n" +
|
||||||
|
@ -326,20 +323,19 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
# DB reporting
|
# DB reporting
|
||||||
# Rem : one report it as a smb_challenge on port 445 has breaking those hashes
|
# Rem : one report it as a smb_challenge on port 445 has breaking those hashes
|
||||||
# will be mainly use for psexec / smb related exploit
|
# will be mainly use for psexec / smb related exploit
|
||||||
report_auth_info(
|
opts_report = {
|
||||||
:host => ip,
|
ip: ip,
|
||||||
:port => 445,
|
user: user,
|
||||||
:sname => 'smb_challenge',
|
domain: domain,
|
||||||
:user => user,
|
ntlm_ver: ntlm_ver,
|
||||||
:pass => domain + ":" +
|
lm_hash: lm_hash,
|
||||||
( lm_hash + lm_cli_challenge.to_s ? lm_hash + lm_cli_challenge.to_s : "00" * 24 ) + ":" +
|
nt_hash: nt_hash
|
||||||
( nt_hash + nt_cli_challenge.to_s ? nt_hash + nt_cli_challenge.to_s : "00" * 24 ) + ":" +
|
}
|
||||||
datastore['CHALLENGE'].to_s,
|
opts_report.merge!(lm_cli_challenge: lm_cli_challenge) if lm_cli_challenge
|
||||||
:type => smb_db_type_hash,
|
opts_report.merge!(nt_cli_challenge: nt_cli_challenge) if nt_cli_challenge
|
||||||
:proof => "DOMAIN=#{domain}",
|
|
||||||
:source_type => "captured",
|
report_creds(opts_report)
|
||||||
:active => true
|
|
||||||
)
|
|
||||||
#if(datastore['LOGFILE'])
|
#if(datastore['LOGFILE'])
|
||||||
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
|
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
|
||||||
#end
|
#end
|
||||||
|
@ -406,4 +402,81 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def report_creds(opts)
|
||||||
|
ip = opts[:ip] || rhost
|
||||||
|
user = opts[:user] || nil
|
||||||
|
domain = opts[:domain] || nil
|
||||||
|
ntlm_ver = opts[:ntlm_ver] || nil
|
||||||
|
lm_hash = opts[:lm_hash] || nil
|
||||||
|
nt_hash = opts[:nt_hash] || nil
|
||||||
|
lm_cli_challenge = opts[:lm_cli_challenge] || nil
|
||||||
|
nt_cli_challenge = opts[:nt_cli_challenge] || nil
|
||||||
|
|
||||||
|
case ntlm_ver
|
||||||
|
when NTLM_CONST::NTLM_V1_RESPONSE, NTLM_CONST::NTLM_2_SESSION_RESPONSE
|
||||||
|
hash = [
|
||||||
|
user, '',
|
||||||
|
domain ? domain : 'NULL',
|
||||||
|
lm_hash ? lm_hash : '0' * 48,
|
||||||
|
nt_hash ? nt_hash : '0' * 48,
|
||||||
|
@challenge.unpack('H*')[0]
|
||||||
|
].join(':').gsub(/\n/, '\\n')
|
||||||
|
report_hash(ip, user, 'netntlm', hash)
|
||||||
|
when NTLM_CONST::NTLM_V2_RESPONSE
|
||||||
|
hash = [
|
||||||
|
user, '',
|
||||||
|
domain ? domain : 'NULL',
|
||||||
|
@challenge.unpack('H*')[0],
|
||||||
|
lm_hash ? lm_hash : '0' * 32,
|
||||||
|
lm_cli_challenge ? lm_cli_challenge : '0' * 16
|
||||||
|
].join(':').gsub(/\n/, '\\n')
|
||||||
|
report_hash(ip, user, 'netlmv2', hash)
|
||||||
|
|
||||||
|
hash = [
|
||||||
|
user, '',
|
||||||
|
domain ? domain : 'NULL',
|
||||||
|
@challenge.unpack('H*')[0],
|
||||||
|
nt_hash ? nt_hash : '0' * 32,
|
||||||
|
nt_cli_challenge ? nt_cli_challenge : '0' * 160
|
||||||
|
].join(':').gsub(/\n/, '\\n')
|
||||||
|
report_hash(ip, user, 'netntlmv2', hash)
|
||||||
|
else
|
||||||
|
hash = domain + ':' +
|
||||||
|
( lm_hash + lm_cli_challenge.to_s ? lm_hash + lm_cli_challenge.to_s : '00' * 24 ) + ':' +
|
||||||
|
( nt_hash + nt_cli_challenge.to_s ? nt_hash + nt_cli_challenge.to_s : '00' * 24 ) + ':' +
|
||||||
|
datastore['CHALLENGE'].to_s
|
||||||
|
report_hash(ip, user, nil, hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def report_hash(ip, user, type_hash, hash)
|
||||||
|
service_data = {
|
||||||
|
address: ip,
|
||||||
|
port: 445,
|
||||||
|
service_name: 'smb',
|
||||||
|
protocol: 'tcp',
|
||||||
|
workspace_id: myworkspace_id
|
||||||
|
}
|
||||||
|
|
||||||
|
credential_data = {
|
||||||
|
module_fullname: self.fullname,
|
||||||
|
origin_type: :service,
|
||||||
|
private_data: hash,
|
||||||
|
private_type: :nonreplayable_hash,
|
||||||
|
username: user
|
||||||
|
}.merge(service_data)
|
||||||
|
|
||||||
|
unless type_hash.nil?
|
||||||
|
credential_data.merge!(jtr_format: type_hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
login_data = {
|
||||||
|
core: create_credential(credential_data),
|
||||||
|
status: Metasploit::Model::Login::Status::UNTRIED
|
||||||
|
}.merge(service_data)
|
||||||
|
|
||||||
|
create_credential_login(login_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue