Merge branch 'master' into feature/uuid-registration

bug/bundler_fix
HD Moore 2015-05-20 19:48:39 -05:00
commit a8d111ce89
11 changed files with 429 additions and 155 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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