Skip to content

Commit bdd5276

Browse files
author
HD Moore
committed
This fixes a number of issues with the Capture mixin
* The use of www.metasploit.com in a datastore option results in a DNS lookup (infoleak). Switch to 8.8.8.8 (TTL=1) * The hackey code around #each_packet is no longer necessary in newer Ruby versions * The arp()/probe_gateway() calls to inject_reply() had broken logic leading to early exit and missed replies * The arp() function now tries up to three times to get a reply (helpful with lossy L2) * GC.start is extraneous and should be removed * Increased timeouts
1 parent 615d71d commit bdd5276

File tree

1 file changed

+44
-58
lines changed

1 file changed

+44
-58
lines changed

lib/msf/core/exploit/capture.rb

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def initialize(info = {})
4242
[
4343
true,
4444
'Send a TTL=1 random UDP datagram to this host to discover the default gateway\'s MAC',
45-
'www.metasploit.com']),
45+
'8.8.8.8']),
4646
OptPort.new('GATEWAY_PROBE_PORT',
4747
[
4848
false,
@@ -143,7 +143,6 @@ def close_pcap
143143
return unless self.capture
144144
self.capture = nil
145145
self.arp_capture = nil
146-
GC.start()
147146
end
148147

149148
def capture_extract_ies(raw)
@@ -163,26 +162,14 @@ def capture_extract_ies(raw)
163162
end
164163

165164
#
166-
# This monstrosity works around a series of bugs in the interrupt
167-
# signal handling of Ruby 1.9
165+
# Loop through each packet
168166
#
169167
def each_packet
170168
return unless capture
171-
begin
172-
@capture_count = 0
173-
reader = framework.threads.spawn("PcapReceiver", false) do
174-
capture.each do |pkt|
175-
yield(pkt)
176-
@capture_count += 1
177-
end
178-
end
179-
reader.join
180-
rescue ::Exception
181-
raise $!
182-
ensure
183-
reader.kill if reader.alive?
169+
capture.each do |pkt|
170+
yield(pkt)
171+
@capture_count += 1
184172
end
185-
186173
@capture_count
187174
end
188175

@@ -242,10 +229,9 @@ def inject_pcap(pcap_file, filter=nil, delay = 0, pcap=self.capture)
242229
pcap.inject(pkt)
243230
Rex.sleep((delay * 1.0)/1000)
244231
end
245-
GC.start
246232
end
247233

248-
# Capture_sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires
234+
# capture_sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires
249235
# a payload and a destination address. To send to the broadcast address, set bcast
250236
# to true (this will guarantee that packets will be sent even if ARP doesn't work
251237
# out).
@@ -262,24 +248,20 @@ def capture_sendto(payload="", dhost=nil, bcast=false, dev=nil)
262248

263249
# The return value either be a PacketFu::Packet object, or nil
264250
def inject_reply(proto=:udp, pcap=self.capture)
265-
reply = nil
266-
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
267-
if not pcap
268-
raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)"
269-
else
270-
begin
271-
::Timeout.timeout(to) do
272-
pcap.each do |r|
273-
packet = PacketFu::Packet.parse(r)
274-
next unless packet.proto.map { |x| x.downcase.to_sym }.include? proto
275-
reply = packet
276-
break
277-
end
251+
# Defaults to ~2 seconds
252+
to = (datastore['TIMEOUT'] * 4) / 1000.0
253+
raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" if not pcap
254+
begin
255+
::Timeout.timeout(to) do
256+
pcap.each do |r|
257+
packet = PacketFu::Packet.parse(r)
258+
next unless packet.proto.map { |x| x.downcase.to_sym }.include? proto
259+
return packet
278260
end
279-
rescue ::Timeout::Error
280261
end
262+
rescue ::Timeout::Error
281263
end
282-
return reply
264+
nil
283265
end
284266

285267
# This ascertains the correct Ethernet addresses one should use to
@@ -328,20 +310,19 @@ def probe_gateway(addr)
328310
end
329311

330312
begin
331-
to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0
313+
to = ((datastore['TIMEOUT'] || 500).to_f * 8) / 1000.0
332314
::Timeout.timeout(to) do
333-
while (my_packet = inject_reply(:udp, self.arp_capture))
334-
if my_packet.payload == secret
335-
dst_mac = self.arp_cache[:gateway] = my_packet.eth_daddr
336-
src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet.eth_saddr
337-
return [dst_mac, src_mac]
338-
else
339-
next
340-
end
315+
while true
316+
my_packet = inject_reply(:udp, self.arp_capture)
317+
next unless my_packet
318+
next unless my_packet.payload == secret
319+
dst_mac = self.arp_cache[:gateway] = my_packet.eth_daddr
320+
src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet.eth_saddr
321+
return [dst_mac, src_mac]
341322
end
342323
end
343324
rescue ::Timeout::Error
344-
# Well, that didn't work (this common on networks where there's no gatway, like
325+
# Well, that didn't work (this is common on networks where there's no gateway, like
345326
# VMWare network interfaces. We'll need to use a fake source hardware address.
346327
self.arp_cache[Rex::Socket.source_address(addr)] = "00:00:00:00:00:00"
347328
end
@@ -354,26 +335,31 @@ def arp(target_ip=nil)
354335
return self.arp_cache[:gateway] unless should_arp? target_ip
355336
source_ip = Rex::Socket.source_address(target_ip)
356337
raise RuntimeError, "Could not access the capture process." unless self.arp_capture
338+
357339
p = arp_packet(target_ip, source_ip)
358-
inject_eth(:eth_type => 0x0806,
359-
:payload => p,
360-
:pcap => self.arp_capture,
361-
:eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)]
362-
)
363-
begin
364-
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
365-
::Timeout.timeout(to) do
366-
while (my_packet = inject_reply(:arp, self.arp_capture))
367-
if my_packet.arp_saddr_ip == target_ip
340+
341+
# Try up to 3 times to get an ARP response
342+
1.upto(3) do
343+
inject_eth(:eth_type => 0x0806,
344+
:payload => p,
345+
:pcap => self.arp_capture,
346+
:eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)]
347+
)
348+
begin
349+
to = ((datastore['TIMEOUT'] || 500).to_f * 8) / 1000.0
350+
::Timeout.timeout(to) do
351+
while true
352+
my_packet = inject_reply(:arp, self.arp_capture)
353+
next unless my_packet
354+
next unless my_packet.arp_saddr_ip == target_ip
368355
self.arp_cache[target_ip] = my_packet.eth_saddr
369356
return self.arp_cache[target_ip]
370-
else
371-
next
372357
end
373358
end
359+
rescue ::Timeout::Error
374360
end
375-
rescue ::Timeout::Error
376361
end
362+
nil
377363
end
378364

379365
# Creates a full ARP packet, mainly for use with inject_eth()

0 commit comments

Comments
 (0)