2009-12-26 08:51:03 +00:00
|
|
|
##
|
|
|
|
# $Id$
|
|
|
|
##
|
|
|
|
|
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
|
|
# Framework web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/framework/
|
|
|
|
##
|
|
|
|
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
|
|
|
|
include Msf::Auxiliary::Report
|
|
|
|
include Msf::Auxiliary::Scanner
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
super(
|
|
|
|
'Name' => 'NetBIOS Information Discovery',
|
|
|
|
'Version' => '$Revision$',
|
|
|
|
'Description' => 'Discover host information through NetBIOS',
|
|
|
|
'Author' => 'hdm',
|
|
|
|
'License' => MSF_LICENSE
|
|
|
|
)
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
2010-02-09 16:47:38 +00:00
|
|
|
Opt::CHOST,
|
2009-12-26 08:51:03 +00:00
|
|
|
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
|
|
|
|
Opt::RPORT(137)
|
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Define our batch size
|
|
|
|
def run_batch_size
|
|
|
|
datastore['BATCHSIZE'].to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def rport
|
|
|
|
datastore['RPORT'].to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
# Fingerprint a single host
|
|
|
|
def run_batch(batch)
|
|
|
|
|
|
|
|
|
|
|
|
print_status("Sending NetBIOS status requests to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
|
|
|
|
|
|
|
|
@results = {}
|
|
|
|
begin
|
|
|
|
udp_sock = nil
|
|
|
|
idx = 0
|
2010-03-22 00:20:09 +00:00
|
|
|
|
2010-02-09 16:47:38 +00:00
|
|
|
# Create an unbound UDP socket if no CHOST is specified, otherwise
|
|
|
|
# create a UDP socket bound to CHOST (in order to avail of pivoting)
|
2010-05-01 16:26:24 +00:00
|
|
|
udp_sock = Rex::Socket::Udp.create( { 'LocalHost' => datastore['CHOST'] || nil, 'Context' => {'Msf' => framework, 'MsfExploit' => self} })
|
|
|
|
add_socket(udp_sock)
|
2009-12-26 08:51:03 +00:00
|
|
|
|
|
|
|
batch.each do |ip|
|
|
|
|
begin
|
|
|
|
data = create_netbios_status(ip)
|
|
|
|
udp_sock.sendto(data, ip, rport, 0)
|
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
|
|
|
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if (idx % 30 == 0)
|
|
|
|
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
|
|
|
|
parse_reply(r)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
idx += 1
|
|
|
|
end
|
|
|
|
|
|
|
|
while (r = udp_sock.recvfrom(65535, 3) and r[1])
|
|
|
|
parse_reply(r)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Second pass to find additional IPs per host name
|
|
|
|
|
|
|
|
@results.keys.each do |ip|
|
|
|
|
next if not @results[ip][:name]
|
|
|
|
begin
|
|
|
|
data = create_netbios_lookup(@results[ip][:name])
|
|
|
|
udp_sock.sendto(data, ip, rport, 0)
|
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
|
|
|
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if (idx % 30 == 0)
|
|
|
|
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
|
|
|
|
parse_reply(r)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
idx += 1
|
|
|
|
end
|
|
|
|
|
|
|
|
while (r = udp_sock.recvfrom(65535, 3) and r[1])
|
|
|
|
parse_reply(r)
|
|
|
|
end
|
|
|
|
|
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
2010-04-27 14:38:17 +00:00
|
|
|
rescue ::Errno::ENOBUFS
|
|
|
|
print_status("Socket buffers are full, waiting for them to flush...")
|
|
|
|
while (r = udp_sock.recvfrom(65535, 0.1) and r[1])
|
|
|
|
parse_reply(r)
|
|
|
|
end
|
|
|
|
select(nil, nil, nil, 0.25)
|
2009-12-26 08:51:03 +00:00
|
|
|
rescue ::Exception => e
|
2010-12-21 02:22:56 +00:00
|
|
|
print_error("Unknown error: #{e.class} #{e} #{e.backtrace}")
|
2009-12-26 08:51:03 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
@results.keys.each do |ip|
|
|
|
|
host = @results[ip]
|
|
|
|
user = ""
|
|
|
|
os = "Windows"
|
|
|
|
|
|
|
|
if(host[:user] and host[:mac] != "00:00:00:00:00:00")
|
|
|
|
user = " User:#{host[:user]}"
|
|
|
|
end
|
|
|
|
|
|
|
|
if(host[:mac] == "00:00:00:00:00:00")
|
|
|
|
os = "Unix"
|
|
|
|
end
|
|
|
|
|
2011-01-27 16:39:19 +00:00
|
|
|
names = ""
|
|
|
|
if(host[:names])
|
|
|
|
names = " Names:(" + host[:names].map{|n| n[0]}.uniq.join(", ") + ")"
|
|
|
|
end
|
|
|
|
|
2009-12-26 08:51:03 +00:00
|
|
|
addrs = ""
|
|
|
|
if(host[:addrs])
|
|
|
|
addrs = "Addresses:(" + host[:addrs].map{|n| n[0]}.uniq.join(", ") + ")"
|
|
|
|
end
|
|
|
|
|
2010-01-15 15:49:13 +00:00
|
|
|
if(host[:mac] != "00:00:00:00:00:00")
|
2010-03-22 00:20:09 +00:00
|
|
|
report_host(:host => ip, :mac => host[:mac])
|
2010-01-15 15:49:13 +00:00
|
|
|
else
|
2010-03-22 00:20:09 +00:00
|
|
|
report_host(:host => ip)
|
2010-01-15 15:49:13 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
extra = ""
|
|
|
|
|
|
|
|
virtual = nil
|
|
|
|
case host[:mac]
|
|
|
|
when /^00:13:07/i
|
|
|
|
virtual = 'ParaVirtual'
|
|
|
|
when /^(00:1C:14|00:50:56|00:05:69|00:0c:29)/i
|
|
|
|
virtual = 'VMWare'
|
|
|
|
when /^00:1C:42/
|
|
|
|
virtual = "Parallels"
|
|
|
|
when /^00:18:51/
|
|
|
|
virtual = "SWsoft Virtuozzo"
|
|
|
|
when /^00:21:F6/i
|
|
|
|
virtual = 'Virtual Iron'
|
|
|
|
when /^00:16:3e/
|
|
|
|
virtual = 'Xen'
|
|
|
|
when /^(54:52:00|DE:AD:BE)/
|
|
|
|
virtual = 'QEMU (unofficial)'
|
|
|
|
when /^00:24:0B/i
|
|
|
|
virtual = 'Virtual Computer Inc'
|
|
|
|
end
|
|
|
|
|
|
|
|
if(virtual)
|
|
|
|
extra = "Virtual Machine:#{virtual}"
|
|
|
|
report_note(
|
|
|
|
:host => ip,
|
|
|
|
:type => 'host.virtual_machine',
|
|
|
|
:data => {:vendor => virtual, :method => 'netbios'}
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
if(host[:addrs])
|
|
|
|
aliases = []
|
|
|
|
host[:addrs].map{|n| n[0]}.uniq.each do |addr|
|
|
|
|
next if addr == ip
|
|
|
|
aliases << addr
|
|
|
|
end
|
|
|
|
|
|
|
|
if not aliases.empty?
|
|
|
|
report_note(
|
|
|
|
:host => ip,
|
|
|
|
:proto => 'udp',
|
|
|
|
:port => 137,
|
|
|
|
:type => 'netbios.addresses',
|
|
|
|
:data => {:addresses => aliases}
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
print_status("#{ip} [#{host[:name]}] OS:#{os}#{user}#{names} #{addrs} Mac:#{host[:mac]} #{extra}")
|
2009-12-26 08:51:03 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def parse_reply(pkt)
|
|
|
|
# Ignore "empty" packets
|
|
|
|
return if not pkt[1]
|
|
|
|
|
|
|
|
addr = pkt[1]
|
|
|
|
if(addr =~ /^::ffff:/)
|
|
|
|
addr = addr.sub(/^::ffff:/, '')
|
|
|
|
end
|
|
|
|
|
|
|
|
data = pkt[0]
|
|
|
|
|
|
|
|
head = data.slice!(0,12)
|
|
|
|
|
|
|
|
xid, flags, quests, answers, auths, adds = head.unpack('n6')
|
2009-12-29 04:26:46 +00:00
|
|
|
|
2009-12-26 08:51:03 +00:00
|
|
|
return if quests != 0
|
|
|
|
return if answers == 0
|
|
|
|
|
|
|
|
qname = data.slice!(0,34)
|
|
|
|
rtype,rclass,rttl,rlen = data.slice!(0,10).unpack('nnNn')
|
|
|
|
buff = data.slice!(0,rlen)
|
|
|
|
|
|
|
|
names = []
|
|
|
|
|
|
|
|
hname = nil
|
|
|
|
uname = nil
|
|
|
|
|
2010-12-21 02:22:56 +00:00
|
|
|
@results[addr] ||= {}
|
|
|
|
|
2009-12-26 08:51:03 +00:00
|
|
|
case rtype
|
|
|
|
when 0x21
|
|
|
|
rcnt = buff.slice!(0,1).unpack("C")[0]
|
|
|
|
1.upto(rcnt) do
|
|
|
|
tname = buff.slice!(0,15).gsub(/\x00.*/, '').strip
|
|
|
|
ttype = buff.slice!(0,1).unpack("C")[0]
|
|
|
|
tflag = buff.slice!(0,2).unpack('n')[0]
|
|
|
|
names << [ tname, ttype, tflag ]
|
|
|
|
hname = tname if ttype == 0x20
|
|
|
|
uname = tname if ttype == 0x03
|
|
|
|
end
|
|
|
|
maddr = buff.slice!(0,6).unpack("C*").map{|c| "%.2x" % c }.join(":")
|
|
|
|
|
2010-12-21 02:22:56 +00:00
|
|
|
@results[addr][:names] = names
|
|
|
|
@results[addr][:mac] = maddr
|
|
|
|
|
2009-12-26 12:13:18 +00:00
|
|
|
if (!hname and @results[addr][:names].length > 0)
|
|
|
|
@results[addr][:name] = @results[addr][:names][0][0]
|
|
|
|
end
|
|
|
|
|
2009-12-26 08:51:03 +00:00
|
|
|
@results[addr][:name] = hname if hname
|
|
|
|
@results[addr][:user] = uname if uname
|
2009-12-29 04:26:46 +00:00
|
|
|
|
|
|
|
inf = ''
|
|
|
|
names.each do |name|
|
|
|
|
inf << name[0]
|
|
|
|
inf << ":<%.2x>" % name[1]
|
|
|
|
if (name[2] & 0x8000 == 0)
|
|
|
|
inf << ":U :"
|
|
|
|
else
|
|
|
|
inf << ":G :"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
inf << maddr
|
|
|
|
|
|
|
|
report_service(
|
|
|
|
:host => addr,
|
2011-05-03 03:03:38 +00:00
|
|
|
:mac => (maddr and maddr != '00:00:00:00:00:00') ? maddr : nil,
|
2009-12-29 04:26:46 +00:00
|
|
|
:host_name => (hname) ? hname.downcase : nil,
|
|
|
|
:port => pkt[2],
|
|
|
|
:proto => 'udp',
|
|
|
|
:name => 'NetBIOS',
|
|
|
|
:info => inf
|
|
|
|
)
|
2009-12-26 08:51:03 +00:00
|
|
|
when 0x20
|
|
|
|
1.upto(rlen / 6.0) do
|
|
|
|
tflag = buff.slice!(0,2).unpack('n')[0]
|
|
|
|
taddr = buff.slice!(0,4).unpack("C*").join(".")
|
|
|
|
names << [ taddr, tflag ]
|
|
|
|
end
|
|
|
|
@results[addr][:addrs] = names
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_netbios_status(ip)
|
|
|
|
data =
|
|
|
|
[rand(0xffff)].pack('n')+
|
|
|
|
"\x00\x00\x00\x01\x00\x00\x00\x00"+
|
|
|
|
"\x00\x00\x20\x43\x4b\x41\x41\x41"+
|
|
|
|
"\x41\x41\x41\x41\x41\x41\x41\x41"+
|
|
|
|
"\x41\x41\x41\x41\x41\x41\x41\x41"+
|
|
|
|
"\x41\x41\x41\x41\x41\x41\x41\x41"+
|
|
|
|
"\x41\x41\x41\x00\x00\x21\x00\x01"
|
|
|
|
|
|
|
|
return data
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_netbios_lookup(name)
|
|
|
|
name = [name].pack("A15") + "\x00"
|
|
|
|
|
|
|
|
data =
|
|
|
|
[rand(0xffff)].pack('n') +
|
|
|
|
"\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +
|
|
|
|
"\x20" +
|
|
|
|
Rex::Proto::SMB::Utils.nbname_encode(name) +
|
|
|
|
"\x00" +
|
|
|
|
"\x00\x20\x00\x01"
|
|
|
|
|
|
|
|
return data
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|