mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-12 11:52:01 +01:00
Fixes #782. Cleans up the various arp and inject methods so they're a little more sane to read, and streamlines the ARP process.
Still would like to a) experiment with keeping a persistent cache (with a cache timeout maybe, like a real arp cache), and b) see how caching negative replies will work out, but that's for another time. git-svn-id: file:///home/svn/framework3/trunk@8280 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
c1bc6a617a
commit
7c4d7c3d71
@ -198,7 +198,7 @@ module Exploit::Capture
|
||||
raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" unless self.capture
|
||||
raise RuntimeError, "Must specify a host to sendto" unless dhost
|
||||
dst_mac,src_mac = lookup_eth(dhost)
|
||||
if dst_mac == "ff:ff:ff:ff:ff:ff" and not bcast
|
||||
if dst_mac == nil and not bcast
|
||||
return false
|
||||
end
|
||||
inject_eth(:payload => payload, :eth_daddr => dst_mac, :eth_saddr => src_mac)
|
||||
@ -251,86 +251,61 @@ module Exploit::Capture
|
||||
# This ascertains the correct Ethernet addresses one should use to
|
||||
# ensure injected IP packets actually get where they are going, and
|
||||
# manages the self.arp_cache hash. It always uses self.arp_capture
|
||||
#
|
||||
# This is done by first generating two packets: First, we try to ARP
|
||||
# the target IP address. If it's on the local network, this will usually
|
||||
# elicit a response of [dst_mac, dst_ip] -- see arp, below.
|
||||
#
|
||||
# We still need to know our own src_mac, though, since we assume we want
|
||||
# a response. To do this, we send a UDP trigger packet with a normal
|
||||
# UDPSocket connect, to either the gateway host (GATEWAY), if known, or to
|
||||
# a random(ish) IP known to be beyond the default gateway. We then listen
|
||||
# for our own UDP packet (via inject_reply) -- it need not get a response
|
||||
# from GATEWAY or the random fake IP.
|
||||
#
|
||||
# Note, if IANA assigns 177/8 in the future, then the default fake-remote
|
||||
# IP address will have to be changed (unless you don't mind sending them
|
||||
# probe packets).
|
||||
#
|
||||
# Finally, if all of this fails to get an adequate response, a default
|
||||
# ethernet address pair of ["ff:ff:ff:ff:ff:ff","00:00:00:00:00:00"] will
|
||||
# be used by the inject_eth function (above), unless the calling module
|
||||
# decides against it. In switched networks, you should always get a correct
|
||||
# pair for a remote address, the same pair for an unroutable address
|
||||
# (eg, 127.0.0.1), or a correct pair for a local address (if the target
|
||||
# responds to ARPs at all).
|
||||
#
|
||||
# TODO: Fix PcapRub to return a subnet mask so I can determine ahead of
|
||||
# time if it's in the local network.
|
||||
#
|
||||
# TODO: Reorganize this.
|
||||
#
|
||||
# do inject and capture packets, and will always first fire off a
|
||||
# UDP packet using the regular socket to learn the source host's
|
||||
# and gateway's mac addresses.
|
||||
def lookup_eth(addr=nil,iface=nil)
|
||||
self.arp_cache = {} unless self.arp_cache.kind_of? Hash
|
||||
eth_pair = [nil,nil] # [dst_mac, src_mac] will go here.
|
||||
raise RuntimeError, "Could not access the capture process." if not self.arp_capture
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
# Try to ARP the real IP first.
|
||||
eth_pair[0] = self.arp_cache[addr] || arp(addr) || self.arp_cache[:gateway]
|
||||
eth_pair[1] = self.arp_cache[Rex::Socket.source_address(addr)]
|
||||
if (eth_pair[0].nil? || eth_pair[1].nil?)
|
||||
dst_host = (datastore['GATEWAY'] || IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET).to_s)
|
||||
dst_port = rand(30000)+1024
|
||||
preamble = [datastore['ARP_SECRET']].pack("N")
|
||||
secret = "#{preamble}#{Rex::Text.rand_text(rand(0xff)+1)}"
|
||||
UDPSocket.open.send(secret,0,dst_host,dst_port)
|
||||
begin
|
||||
Timeout.timeout(to) do
|
||||
while(my_packet = inject_reply(:udp,self.arp_capture))
|
||||
if my_packet[:payload] = secret
|
||||
eth_pair[0] ||= my_packet[:eth].dst_mac
|
||||
eth_pair[1] = my_packet[:eth].src_mac
|
||||
self.arp_cache[:gateway] = my_packet[:eth].dst_mac
|
||||
self.arp_cache[Rex::Socket.source_address(addr)] = my_packet[:eth].src_mac
|
||||
return eth_pair
|
||||
else
|
||||
next
|
||||
end
|
||||
unless self.arp_cache.kind_of? Hash
|
||||
self.arp_cache = {}
|
||||
probe_gateway(addr)
|
||||
end
|
||||
src_mac = self.arp_cache[Rex::Socket.source_address(addr)]
|
||||
unless should_arp?(addr)
|
||||
dst_mac = self.arp_cache[:gateway]
|
||||
else
|
||||
dst_mac = self.arp_cache[addr] || arp(addr)
|
||||
end
|
||||
return [dst_mac,src_mac]
|
||||
end
|
||||
|
||||
def probe_gateway(addr)
|
||||
dst_host = (datastore['GATEWAY'] || IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET).to_s)
|
||||
dst_port = rand(30000)+1024
|
||||
preamble = [datastore['ARP_SECRET']].pack("N")
|
||||
secret = "#{preamble}#{Rex::Text.rand_text(rand(0xff)+1)}"
|
||||
UDPSocket.open.send(secret,0,dst_host,dst_port)
|
||||
begin
|
||||
to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0
|
||||
Timeout.timeout(to) do
|
||||
while(my_packet = inject_reply(:udp,self.arp_capture))
|
||||
if my_packet[:payload] = secret
|
||||
dst_mac = self.arp_cache[:gateway] = my_packet[:eth].dst_mac
|
||||
src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet[:eth].src_mac
|
||||
return [dst_mac,src_mac]
|
||||
else
|
||||
next
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
end
|
||||
else
|
||||
return eth_pair
|
||||
rescue Timeout::Error
|
||||
end
|
||||
end
|
||||
|
||||
# A pure-Ruby ARP exchange. It always uses self.arp_capture.
|
||||
# A pure-Ruby ARP exchange. It uses self.arp_capture to send and recv
|
||||
# packets, rather than self.capture.
|
||||
def arp(target_ip=nil)
|
||||
return self.arp_cache[target_ip] if self.arp_cache[target_ip]
|
||||
return self.arp_cache[:gateway] unless should_arp? target_ip
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
raise RuntimeError, "Could not access the capture process." if not self.arp_capture
|
||||
n = Racket::Racket.new
|
||||
n.l3 = Racket::L3::ARP.new
|
||||
n.l3.opcode = 1
|
||||
n.l3.tpa = target_ip || datastore['RHOST']
|
||||
n.l3.spa = IPAddr.new(datastore['ARP_SECRET'].to_i, Socket::AF_INET).to_s
|
||||
n = arp_packet(target_ip)
|
||||
inject_eth(:eth_type => 0x0806,
|
||||
:payload => n.pack,
|
||||
:pcap => self.arp_capture
|
||||
)
|
||||
:payload => n.pack,
|
||||
:pcap => self.arp_capture,
|
||||
:eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)]
|
||||
)
|
||||
begin
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
Timeout.timeout(to) do
|
||||
while (my_packet = inject_reply(:arp,self.arp_capture))
|
||||
if my_packet[:arp].spa == target_ip
|
||||
@ -345,6 +320,16 @@ module Exploit::Capture
|
||||
end
|
||||
end
|
||||
|
||||
def arp_packet(target_ip)
|
||||
n = Racket::Racket.new
|
||||
n.l3 = Racket::L3::ARP.new
|
||||
n.l3.opcode = 1
|
||||
n.l3.tpa = target_ip || datastore['RHOST']
|
||||
n.l3.spa = IPAddr.new(datastore['ARP_SECRET'].to_i, Socket::AF_INET).to_s
|
||||
n.l3.sha = self.arp_cache[Rex::Socket.source_address(target_ip)]
|
||||
return n
|
||||
end
|
||||
|
||||
# Allow modules to reset their arp caches arbitrarily.
|
||||
def expire_arpcache
|
||||
self.arp_cache = {}
|
||||
|
Loading…
Reference in New Issue
Block a user