Created
August 3, 2016 00:39
-
-
Save jhart-r7/79b5c4cd82795bcfede2532c8de9bf74 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ jhart@jhart-laptop (08/02/16 17:37:52) ~/rapid7/metasploit-framework/modules/auxiliary/scanner/netbios ] | |
$ diff nbname.rb nbname_probe.rb -uN | |
--- nbname.rb 2016-05-16 17:27:36.000000000 -0700 | |
+++ nbname_probe.rb 2016-08-02 17:31:57.000000000 -0700 | |
@@ -10,70 +10,125 @@ | |
class MetasploitModule < Msf::Auxiliary | |
include Msf::Auxiliary::Report | |
- include Msf::Auxiliary::UDPScanner | |
+ include Msf::Auxiliary::Scanner | |
+ include Msf::Module::Deprecated | |
+ | |
+ deprecated(Date.new(2016, 9, 1), 'auxiliary/scanner/netbios/nbname') | |
def initialize | |
super( | |
- 'Name' => 'NetBIOS Information Discovery', | |
- 'Description' => 'Discover host information through NetBIOS', | |
- 'Author' => 'hdm', | |
+ 'Name' => 'NetBIOS Information Discovery Prober', | |
+ 'Description' => 'Discover host information using sequential NetBIOS Probes', | |
+ 'Author' => ['hdm', 'todb'], | |
'License' => MSF_LICENSE | |
) | |
register_options( | |
[ | |
+ Opt::CHOST, | |
Opt::RPORT(137) | |
], self.class) | |
end | |
- def scanner_prescan(batch) | |
- print_status("Sending NetBIOS requests to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)") | |
- @results = {} | |
+ def rport | |
+ datastore['RPORT'].to_i | |
end | |
- def scan_host(ip) | |
- scanner_send(create_netbios_status(ip), ip, datastore['RPORT']) | |
- end | |
+ # Fingerprint a single host | |
+ def run_host(ip) | |
- def scanner_postscan(batch) | |
+ @thost = ip | |
- cnt = 0 | |
+ @results = {} | |
+ begin | |
+ udp_sock = nil | |
- # Perform a second pass based on responsive hosts | |
- @results.keys.each do |ip| | |
- next if not @results[ip][:name] | |
- scanner_send(create_netbios_lookup(@results[ip][:name]), ip, datastore['RPORT']) | |
- cnt += 1 | |
- end | |
+ # Create an unbound UDP socket if no CHOST is specified, otherwise | |
+ # create a UDP socket bound to CHOST (in order to avail of pivoting) | |
+ udp_sock = Rex::Socket::Udp.create( { | |
+ 'LocalHost' => datastore['CHOST'] || nil, | |
+ 'PeerHost' => ip, 'PeerPort' => rport, | |
+ 'Context' => {'Msf' => framework, 'MsfExploit' => self} | |
+ }) | |
+ add_socket(udp_sock) | |
+ | |
+ begin | |
+ data = create_netbios_status(ip) | |
+ udp_sock.put(data) | |
+ rescue ::Interrupt | |
+ raise $! | |
+ rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused | |
+ nil | |
+ end | |
- # Wait for the final replies to trickle in | |
- scanner_recv(10) if cnt > 0 | |
+ while (r = udp_sock.recvfrom(65535, 0.1) and r[1]) | |
+ parse_reply(r) | |
+ end | |
- @results.keys.each do |ip| | |
+ 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.put(data) | |
+ rescue ::Interrupt | |
+ raise $! | |
+ rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused | |
+ nil | |
+ end | |
+ | |
+ while (r = udp_sock.recvfrom(65535, 0.1) and r[1]) | |
+ parse_reply(r) | |
+ end | |
+ | |
+ end | |
+ | |
+ while (r = udp_sock.recvfrom(65535, 3) and r[1]) | |
+ parse_reply(r) | |
+ end | |
+ | |
+ rescue ::Interrupt | |
+ raise $! | |
+ 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) | |
+ rescue ::Exception => e | |
+ print_error("Unknown error: #{e.class} #{e}") | |
+ end | |
+ @results.keys.each do |ip| | |
+ next unless inside_workspace_boundary?(ip) | |
host = @results[ip] | |
user = "" | |
os = "Windows" | |
- if (host[:user] and host[:mac] != "00:00:00:00:00:00") | |
+ 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") | |
+ if(host[:mac] == "00:00:00:00:00:00") | |
os = "Unix" | |
end | |
names = "" | |
- if (host[:names]) | |
+ if host[:names] | |
names = " Names:(" + host[:names].map{|n| n[0]}.uniq.join(", ") + ")" | |
end | |
addrs = "" | |
- if (host[:addrs]) | |
+ if(host[:addrs]) | |
addrs = "Addresses:(" + host[:addrs].map{|n| n[0]}.uniq.join(", ") + ")" | |
end | |
- if (host[:mac] != "00:00:00:00:00:00") | |
+ if(host[:mac] != "00:00:00:00:00:00") | |
report_host(:host => ip, :mac => host[:mac]) | |
else | |
report_host(:host => ip) | |
@@ -101,7 +156,7 @@ | |
virtual = 'Virtual Computer Inc' | |
end | |
- if (virtual) | |
+ if(virtual) | |
extra = "Virtual Machine:#{virtual}" | |
report_note( | |
:host => ip, | |
@@ -110,7 +165,7 @@ | |
) | |
end | |
- if (host[:addrs]) | |
+ if(host[:addrs]) | |
aliases = [] | |
host[:addrs].map{|n| n[0]}.uniq.each do |addr| | |
next if addr == ip | |
@@ -133,7 +188,16 @@ | |
end | |
- def scanner_process(data, shost, sport) | |
+ 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) | |
@@ -151,8 +215,6 @@ | |
hname = nil | |
uname = nil | |
- @results[shost] ||= {} | |
- | |
case rtype | |
when 0x21 | |
rcnt = buff.slice!(0,1).unpack("C")[0] | |
@@ -166,15 +228,17 @@ | |
end | |
maddr = buff.slice!(0,6).unpack("C*").map{|c| "%.2x" % c }.join(":") | |
- @results[shost][:names] = names | |
- @results[shost][:mac] = maddr | |
+ @results[addr] = { | |
+ :names => names, | |
+ :mac => maddr | |
+ } | |
- if (!hname and @results[shost][:names].length > 0) | |
- @results[shost][:name] = @results[shost][:names][0][0] | |
+ if (!hname and @results[addr][:names].length > 0) | |
+ @results[addr][:name] = @results[addr][:names][0][0] | |
end | |
- @results[shost][:name] = hname if hname | |
- @results[shost][:user] = uname if uname | |
+ @results[addr][:name] = hname if hname | |
+ @results[addr][:user] = uname if uname | |
inf = '' | |
names.each do |name| | |
@@ -188,23 +252,24 @@ | |
end | |
inf << maddr | |
- report_service( | |
- :host => shost, | |
- :mac => (maddr and maddr != '00:00:00:00:00:00') ? maddr : nil, | |
- :host_name => (hname) ? hname.downcase : nil, | |
- :port => datastore['RPORT'], | |
- :proto => 'udp', | |
- :name => 'netbios', | |
- :info => inf | |
- ) | |
- | |
+ if inside_workspace_boundary?(addr) | |
+ report_service( | |
+ :host => addr, | |
+ :mac => (maddr and maddr != '00:00:00:00:00:00') ? maddr : nil, | |
+ :host_name => (hname) ? hname.downcase : nil, | |
+ :port => pkt[2], | |
+ :proto => 'udp', | |
+ :name => 'netbios', | |
+ :info => inf | |
+ ) | |
+ end | |
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[shost][:addrs] = names | |
+ @results[addr][:addrs] = names | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment