Add support for link-local scopes

unstable
HD Moore 2011-12-10 13:24:20 -06:00
parent 9c887eb457
commit e46745b761
5 changed files with 85 additions and 43 deletions

View File

@ -0,0 +1,9 @@
class AddScopeToHosts < ActiveRecord::Migration
def self.up
add_column :hosts, :scope, :text
end
def self.down
remove_column :hosts, :scope
end
end

View File

@ -235,6 +235,8 @@ class DBManager
if wspace.kind_of? String
wspace = find_workspace(wspace)
end
address, scope = address.split('%', 2)
return wspace.hosts.find_by_address(address)
end
@ -259,6 +261,7 @@ class DBManager
# :os_lang -- something like "English", "French", or "en-US"
# :arch -- one of the ARCH_* constants
# :mac -- the host's MAC address
# :scope -- interface identifier for link-local IPv6
#
def report_host(opts)
@ -275,6 +278,9 @@ class DBManager
if not addr.kind_of? Host
addr = normalize_host(addr)
addr, scope = addr.split('%', 2)
opts[:scope] = scope if scope
unless ipv46_validator(addr)
raise ::ArgumentError, "Invalid IP address in report_host(): #{addr}"
end
@ -313,7 +319,7 @@ class DBManager
host.state = HostState::Alive if not host.state
host.comm = '' if not host.comm
host.workspace = wspace if not host.workspace
if host.changed?
msf_import_timestamps(opts,host)
host.save!
@ -1222,6 +1228,7 @@ class DBManager
# Deletes a host and associated data matching this address/comm
#
def del_host(wspace, address, comm='')
address, scope = address.split('%', 2)
host = wspace.hosts.find_by_address_and_comm(address, comm)
host.destroy if host
end
@ -1255,6 +1262,7 @@ class DBManager
# Look for an address across all comms
#
def has_host?(wspace,addr)
address, scope = addr.split('%', 2)
wspace.hosts.find_by_address(addr)
end
@ -5220,8 +5228,9 @@ class DBManager
norm_host = nil
if (host.kind_of? String)
# If it's an IPv4 addr with a host on the end, strip the port
if host =~ /((\d{1,3}\.){3}\d{1,3}):\d+/
# If it's an IPv4 addr with a port on the end, strip the port
if Rex::Socket.is_ipv4?(host) and host =~ /((\d{1,3}\.){3}\d{1,3}):\d+/
norm_host = $1
else
norm_host = host

View File

@ -168,7 +168,7 @@ class Db
default_columns = ::Msf::DBManager::Host.column_names.sort
virtual_columns = [ 'svcs', 'vulns', 'workspace' ]
col_search = [ 'address', 'mac', 'name', 'os_name', 'os_flavor', 'os_sp', 'purpose', 'info', 'comments' ]
col_search = [ 'address', 'mac', 'name', 'os_name', 'os_flavor', 'os_sp', 'purpose', 'info', 'comments']
default_columns.delete_if {|v| (v[-2,2] == "id")}
while (arg = args.shift)
@ -235,7 +235,8 @@ class Db
print_status("Time: #{host.created_at} Host: host=#{host.address}")
if set_rhosts
# only unique addresses
rhosts << host.address unless rhosts.include?(host.address)
addr = (host.scope ? host.address + '%' + host.scope : host.address )
rhosts << addr unless rhosts.include?(addr)
end
end
end
@ -271,8 +272,8 @@ class Db
tbl << columns
if set_rhosts
# only unique addresses
rhosts << host.address unless rhosts.include?(host.address)
addr = (host.scope ? host.address + '%' + host.scope : host.address )
rhosts << addr unless rhosts.include?(addr)
end
if mode == :delete
host.destroy
@ -363,7 +364,7 @@ class Db
print_error("Invalid output filename")
return
end
output_file = File.expand_path(output_file)
output_file = ::File.expand_path(output_file)
when '-R','--rhosts'
set_rhosts = true
rhosts = []
@ -443,9 +444,10 @@ class Db
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)
addr = (host.scope ? host.address + '%' + host.scope : host.address )
rhosts << addr unless rhosts.include?(addr)
end
if (mode == :delete)
service.destroy
delete_count += 1
@ -458,7 +460,7 @@ class Db
print_line tbl.to_s
else
# create the output file
File.open(output_file, "wb") { |f| f.write(tbl.to_csv) }
::File.open(output_file, "wb") { |f| f.write(tbl.to_csv) }
print_status("Wrote services to #{output_file}")
end
@ -621,7 +623,7 @@ class Db
print_error("Invalid output filename")
return
end
output_file = File.expand_path(output_file)
output_file = ::File.expand_path(output_file)
when "-p","--port"
unless (arg_port_range(args.shift, port_ranges, true))
return
@ -737,8 +739,8 @@ class Db
cred.destroy
end
if set_rhosts
# only unique addresses
rhosts << cred.service.host.address unless rhosts.include?(cred.service.host.address)
addr = (cred.service.host.scope ? cred.service.host.address + '%' + cred.service.host.scope : cred.service.host.address )
rhosts << addr unless rhosts.include?(addr)
end
creds_returned += 1
end
@ -748,7 +750,7 @@ class Db
print_line tbl.to_s
else
# create the output file
File.open(output_file, "wb") { |f| f.write(tbl.to_csv) }
::File.open(output_file, "wb") { |f| f.write(tbl.to_csv) }
print_status("Wrote services to #{output_file}")
end
@ -852,8 +854,8 @@ class Db
host = note.host
msg << " host=#{note.host.address}"
if set_rhosts
# only unique addresses
rhosts << host.address unless rhosts.include?(host.address)
addr = (host.scope ? host.address + '%' + host.scope : host.address )
rhosts << addr unless rhosts.include?(addr)
end
end
if (note.service)
@ -1003,13 +1005,13 @@ class Db
return
end
args.each { |glob|
files = Dir.glob(File.expand_path(glob))
files = ::Dir.glob(::File.expand_path(glob))
if files.empty?
print_error("No such file #{glob}")
next
end
files.each { |filename|
if (not File.readable?(filename))
if (not ::File.readable?(filename))
print_error("Could not read file #{filename}")
next
end
@ -1285,13 +1287,13 @@ class Db
def cmd_db_connect(*args)
return if not db_check_driver
if (args[0] == "-y")
if (args[1] and not File.exists? File.expand_path(args[1]))
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']
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

View File

@ -239,10 +239,12 @@ module Socket
# on Windows.
#
def self.gethostbyname(host)
if (dotted_ip?(host))
if (is_ipv4?(host))
return [ host, host, 2, host.split('.').map{ |c| c.to_i }.pack("C4") ]
end
if (is_ipv4?(host))
return [ host, [], 2, host.split('.').map{ |c| c.to_i }.pack("C4") ]
end
if is_ipv6?(host)
host, scope__id = host.split('%', 2)
end
::Socket.gethostbyname(host)
@ -278,7 +280,7 @@ module Socket
# Resolves a host to raw network-byte order.
#
def self.resolv_nbo(host)
self.gethostbyname(Rex::Socket.getaddress(host, true))[3]
self.gethostbyname( Rex::Socket.getaddress(host, true) )[3]
end
#

View File

@ -53,25 +53,37 @@ class RangeWalker
return nil if not parseme
ranges = []
parseme.split(', ').map{ |a| a.split(' ') }.flatten.each { |arg|
opts = {}
# Handle IPv6 first (support ranges, but not CIDR)
if arg.include?(":")
addrs = arg.split('-', 2)
# Handle a single address
if addrs.length == 1
# IPv6 ranges are not yet supported (or useful)
return false unless Rex::Socket.is_ipv6?(arg)
addr = Rex::Socket.addr_atoi(arg)
ranges.push [addr, addr, true]
addr, scope_id = addrs[0].split('%')
opts[:scope_id] = scope_id if scope_id
return false unless Rex::Socket.is_ipv6?(addr)
addr = Rex::Socket.addr_atoi(addr)
ranges.push [addr, addr, true, opts]
next
end
addr1, scope_id = addrs[0].split('%')
opts[:scope_id] = scope_id if scope_id
addr2, scope_id = addrs[0].split('%')
( opts[:scope_id] ||= scope_id ) if scope_id
return false if not (Rex::Socket.is_ipv6?(addr1) and Rex::Socket.is_ipv6?(addr2))
# Handle IPv6 ranges in the form of 2001::1-2001::10
return false if not (Rex::Socket.is_ipv6?(addrs[0]) and Rex::Socket.is_ipv6?(addrs[1]))
addr1 = Rex::Socket.addr_atoi(addrs[0])
addr2 = Rex::Socket.addr_atoi(addrs[1])
ranges.push [addr1, addr2, true]
addr1 = Rex::Socket.addr_atoi(addr1)
addr2 = Rex::Socket.addr_atoi(addr2)
ranges.push [addr1, addr2, true, opts]
next
# Handle IPv4 CIDR
elsif arg.include?("/")
@ -90,7 +102,7 @@ class RangeWalker
expanded = expand_cidr(arg)
if expanded
ranges += expanded
ranges.push [ expanded[0], expanded[1], false, {} ]
else
return false
end
@ -99,7 +111,7 @@ class RangeWalker
elsif arg =~ /[^-0-9,.*]/
# Then it's a domain name and we should send it on to addr_atoi
# unmolested to force a DNS lookup.
Rex::Socket.addr_atoi_list(arg).each { |addr| ranges.push [addr, addr] }
Rex::Socket.addr_atoi_list(arg).each { |addr| ranges.push [addr, addr, false, opts] }
# Handle IPv4 ranges
elsif arg =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})-([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/
@ -108,19 +120,22 @@ class RangeWalker
begin
addrs = [Rex::Socket.addr_atoi($1), Rex::Socket.addr_atoi($2)]
return false if addrs[0] > addrs[1] # The end is greater than the beginning.
ranges.push [addrs[0], addrs[1]]
ranges.push [addrs[0], addrs[1], false, opts]
rescue Resolv::ResolvError # Something's broken, forget it.
return false
end
else
expanded = expand_nmap(arg)
if expanded
ranges += expanded
ranges.push [ expanded[0], expanded[1], false, {} ]
else
return false
end
end
}
# Remove any duplicate ranges
ranges = ranges.uniq
return ranges
end
@ -150,6 +165,11 @@ class RangeWalker
@curr_addr = @ranges[@curr_range][0]
end
addr = Rex::Socket.addr_itoa(@curr_addr, @ranges[@curr_range][2])
if @ranges[@curr_range][3][:scope_id]
addr = addr + '%' + @ranges[@curr_range][3][:scope_id]
end
@curr_addr += 1
return addr
end