diff --git a/lib/msf/core/exploit/capture.rb b/lib/msf/core/exploit/capture.rb index 13c1ea6a65..8f073562e4 100644 --- a/lib/msf/core/exploit/capture.rb +++ b/lib/msf/core/exploit/capture.rb @@ -32,6 +32,12 @@ module Exploit::Capture ], Msf::Exploit::Capture ) + register_advanced_options( + [ + OptInt.new('ARP_SECRET', [true, 'The 32-bit cookie for ARP and UDP probe requests.', 1297303091]) + ], Msf::Exploit::Capture + ) + require 'racket' begin @@ -44,19 +50,19 @@ module Exploit::Capture end - def stats_recv - return(0) if not self.capture - self.capture.stats['recv'] + def stats_recv(pcap=self.capture) + return(0) if not pcap + pcap.stats['recv'] end - def stats_drop - return(0) if not self.capture - self.capture.stats['drop'] + def stats_drop(pcap=self.capture) + return(0) if not pcap + pcap.stats['drop'] end - def stats_ifdrop - return(0) if not self.capture - self.capture.stats['ifdrop'] + def stats_ifdrop(pcap=self.capture) + return(0) if not pcap + pcap.stats['ifdrop'] end # @@ -74,7 +80,7 @@ module Exploit::Capture len = (opts['SNAPLEN'] || datastore['SNAPLEN'] || 65535).to_i tim = (opts['TIMEOUT'] || datastore['TIMEOUT'] || 0).to_i fil = opts['FILTER'] || datastore['FILTER'] - arp = opts['ARPCAP'] || false + arp = opts['ARPCAP'] || true # Look for a PCAP file cap = datastore['PCAPFILE'] || '' @@ -88,7 +94,12 @@ module Exploit::Capture dev = datastore['INTERFACE'] || ::Pcap.lookupdev system("ifconfig", dev, "up") self.capture = ::Pcap.open_live(dev, len, true, tim) - self.arp_capture = ::Pcap.open_live(dev, 512, true, tim) if arp + if arp + self.arp_capture = ::Pcap.open_live(dev, 512, true, tim) + preamble = datastore['ARP_SECRET'].to_i + arp_filter = "arp host #{IPAddr.new(preamble, Socket::AF_INET).to_s} or (udp[8:4] = #{preamble})" + self.arp_capture.setfilter(arp_filter) + end end if (not self.capture) @@ -151,11 +162,11 @@ module Exploit::Capture # Injects a packet on the wire. For all injection-related functions, it's # on the module to open up a capture device first (this way, we don't # needlessly spawn new capture devices). - def inject(pkt) - if not self.capture + def inject(pkt="",pcap=self.capture) + if not pcap raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" else - self.capture.inject(pkt) + pcap.inject(pkt) end end @@ -165,6 +176,7 @@ module Exploit::Capture eth_saddr = args[:eth_saddr] || "00:00:00:00:00:00" eth_type = args[:eth_type] || 0x0800 # IP default payload = args[:payload] + pcap = args[:pcap] || self.capture n = Racket::Racket.new n.l2 = Racket::L2::Ethernet.new n.l2.dst_mac = eth_daddr @@ -172,10 +184,16 @@ module Exploit::Capture n.l2.ethertype = eth_type pkt = n.pack pkt += payload if payload - inject pkt + inject pkt,pcap end + # Sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires + # a payload and a destination address. To send to the broadcast address, set bcast + # to true (this will guarantee that packets will be sent even if ARP doesn't work + # out). def sendto(payload="", dhost=nil, bcast=false) + 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 return false @@ -185,15 +203,15 @@ module Exploit::Capture # Depending on what kind of packet you get, the resultant hash returned will # contain one or several Racket objects. - def readreply(proto=:udp) + def inject_reply(proto=:udp,pcap=self.capture) reply = nil to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 - if not self.capture + if not pcap raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" else begin Timeout.timeout(to) do - self.capture.each do |r| + pcap.each do |r| eth = Racket::L2::Ethernet.new(r) case proto when :arp @@ -228,8 +246,8 @@ module Exploit::Capture end # This ascertains the correct Ethernet addresses one should use to - # ensure injected IP packets actually get where they are going. It - # also manages the 'ARP_CACHE' data store. + # 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 @@ -239,7 +257,7 @@ module Exploit::Capture # 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 readreply) -- it need not get a response + # 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 @@ -262,7 +280,7 @@ module Exploit::Capture 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.capture + 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] @@ -270,11 +288,12 @@ module Exploit::Capture 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 - secret = "#{Rex::Text.rand_text(rand(0xff)+1)}" + 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 = readreply(:udp)) + 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 @@ -293,20 +312,23 @@ module Exploit::Capture end end - # A pure-Ruby ARP exchange. + # A pure-Ruby ARP exchange. It always uses self.arp_capture. def arp(target_ip=nil) return self.arp_cache[target_ip] if self.arp_cache[target_ip] to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 - raise RuntimeError, "Could not access the capture process." if not self.capture + 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 inject_eth(:eth_type => 0x0806, - :payload => n.pack) + :payload => n.pack, + :pcap => self.arp_capture + ) begin Timeout.timeout(to) do - while (my_packet = readreply(:arp)) + while (my_packet = inject_reply(:arp,self.arp_capture)) if my_packet[:arp].spa == target_ip self.arp_cache[target_ip] = my_packet[:arp].sha return self.arp_cache[target_ip]