From 1a818ebf77b3b41c02dce3c3143a34201a223505 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sat, 26 Dec 2009 08:51:03 +0000 Subject: [PATCH] Adds a NetBIOS name scanner - can obtain hostname, mac address, logged in user (when Messenger is running), and alternate interface IPs git-svn-id: file:///home/svn/framework3/trunk@7981 4d416f70-5f16-0410-b530-b9f4589650da --- modules/auxiliary/scanner/netbios/nbname.rb | 223 ++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 modules/auxiliary/scanner/netbios/nbname.rb diff --git a/modules/auxiliary/scanner/netbios/nbname.rb b/modules/auxiliary/scanner/netbios/nbname.rb new file mode 100644 index 0000000000..f7e80168aa --- /dev/null +++ b/modules/auxiliary/scanner/netbios/nbname.rb @@ -0,0 +1,223 @@ +## +# $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( + [ + 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 + + # Create an unbound UDP socket + udp_sock = Rex::Socket::Udp.create() + + 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 $! + rescue ::Exception => e + print_status("Unknown error: #{e.class} #{e}") + 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 + + names = " Names:(" + host[:names].map{|n| n[0]}.uniq.join(", ") + ")" + addrs = "" + if(host[:addrs]) + addrs = "Addresses:(" + host[:addrs].map{|n| n[0]}.uniq.join(", ") + ")" + end + + print_status("#{ip} [#{host[:name]}] OS:#{os}#{user}#{names} #{addrs} Mac:#{host[:mac]}") + 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') + 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 + + 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(":") + + @results[addr] = { + :names => names, + :mac => maddr + } + @results[addr][:name] = @results[addr][:names][0][0] if not hname + @results[addr][:name] = hname if hname + @results[addr][:user] = uname if uname + 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 +