## # $Id$ ## ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/ ## require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::Ipv6 include Msf::Exploit::Remote::Capture include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner def initialize super( 'Name' => 'IPv6 Local Neighbor Discovery', 'Version' => '$Revision$', 'Description' => %q{ Enumerate local IPv6 hosts which respond to Neighbor Solicitations with a link-local address. Note, that like ARP scanning, this usually cannot be performed beyond the local broadcast network. }, 'Author' => 'belch', 'License' => MSF_LICENSE ) register_options( [ OptString.new('SHOST', [false, "Source IP Address"]), OptString.new('SMAC', [false, "Source MAC Address"]), ], self.class) deregister_options('SNAPLEN', 'FILTER') end def run_batch_size datastore['BATCHSIZE'] || 256 end def run_batch(hosts) open_pcap({'SNAPLEN' => 68, 'FILTER' => "arp[6:2] == 0x0002"}) @netifaces = true if not netifaces_implemented? print_error("WARNING : Pcaprub is not uptodate, some functionality will not be available") @netifaces = false end print_status("Discovering IPv4 nodes via ARP...") @interface = datastore['INTERFACE'] || Pcap.lookupdev @shost = datastore['SHOST'] @shost ||= get_ipv4_addr(@interface) if @netifaces raise RuntimeError ,'SHOST should be defined' unless @shost @smac = datastore['SMAC'] @smac ||= get_mac(@interface) if @netifaces raise RuntimeError ,'SMAC should be defined' unless @smac addrs = [] begin found = {} hosts.each do |dhost| probe = buildprobe(@shost, @smac, dhost) capture.inject(probe) while(reply = getreply()) next unless reply.is_arp? if not found[reply.arp_saddr_ip] print_status(sprintf(" %16s ALIVE",reply.arp_saddr_ip)) addrs << [reply.arp_saddr_ip, reply.arp_saddr_mac] report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac) found[reply.arp_saddr_ip] = true end end end etime = ::Time.now.to_f + (hosts.length * 0.05) while (::Time.now.to_f < etime) while(reply = getreply()) next unless reply.is_arp? if not found[reply.arp_saddr_ip] print_status(sprintf(" %16s ALIVE",reply.arp_saddr_ip)) addrs << [reply.arp_saddr_ip, reply.arp_saddr_mac] report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac) found[reply.arp_saddr_ip] = true end end ::IO.select(nil, nil, nil, 0.50) end ensure close_pcap() end neighbor_discovery(addrs) end def map_neighbor(nodes, adv) nodes.each do |node| ipv4_addr, mac_addr = node next unless adv.eth_saddr == mac_addr ipv6_addr = adv.ipv6_saddr return {:eth => mac_addr, :ipv4 => ipv4_addr, :ipv6 => ipv6_addr} end nil end def neighbor_discovery(neighs) print_status("Discovering IPv6 addresses for IPv4 nodes...") print_status("") smac = @smac open_pcap({'SNAPLEN' => 68, 'FILTER' => "icmp6"}) begin neighs.each do |neigh| host, dmac = neigh shost = ipv6_linklocaladdr(smac) neigh = ipv6_linklocaladdr(dmac) probe = buildsolicitation(smac, shost, neigh) capture.inject(probe) Kernel.select(nil,nil,nil,0.1) while(adv = getadvertisement()) next unless adv.is_ipv6? addr = map_neighbor(neighs, adv) next if not addr print_status(sprintf(" %16s maps to %s",addr[:ipv4], addr[:ipv6])) report_note( :host => addr[:ipv4], :type => 'host.ipv4.ipv6.mapping', :data => "system with IPv4 address #{addr[:ipv4]} matches to IPv6 address #{addr[:ipv6]}" ) # with this we have the results in our database end end etime = ::Time.now.to_f + (neighs.length * 0.5) while (::Time.now.to_f < etime) while(adv = getadvertisement()) next if not adv addr = map_neighbor(neighs, adv) next if not addr print_status(sprintf(" %16s maps to %s",addr[:ipv4], addr[:ipv6])) end ::IO.select(nil, nil, nil, 0.50) end ensure close_pcap() end end def buildprobe(shost, smac, dhost) p = PacketFu::ARPPacket.new p.eth_saddr = smac p.eth_daddr = "ff:ff:ff:ff:ff:ff" p.arp_opcode = 1 p.arp_saddr_mac = p.eth_saddr p.arp_daddr_mac = p.eth_daddr p.arp_saddr_ip = shost p.arp_daddr_ip = dhost p.to_s end def getreply pkt = capture.next Kernel.select(nil,nil,nil,0.1) return if not pkt p = PacketFu::Packet.parse(pkt) return unless p.is_arp? return unless p.arp_opcode == 2 p end def buildsolicitation(smac, shost, neigh) dmac = ipv6_soll_mcast_mac(neigh) dhost = ipv6_soll_mcast_addr6(neigh) p = PacketFu::IPv6Packet.new p.eth_saddr = smac p.eth_daddr = dmac p.ipv6_saddr = shost p.ipv6_daddr = dhost p.ipv6_next = 0x3a p.ipv6_hop = 255 p.payload = ipv6_neighbor_solicitation( IPAddr.new(neigh).to_i, p.eth_src ) p.ipv6_len = p.payload.size ipv6_checksum!(p) p.to_s end def getadvertisement pkt = capture.next Kernel.select(nil,nil,nil,0.1) return if not pkt p = PacketFu::Packet.parse(pkt) return unless p.is_ipv6? return unless p.ipv6_next == 0x3a return unless p.payload[0,2] == "\x88\x00" p end end