1
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:
Tod Beardsley 2010-01-27 22:46:41 +00:00
parent c1bc6a617a
commit 7c4d7c3d71

View File

@ -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 = {}