/modules/auxiliary/scanner/discovery/ipv6_neighbor.rb
Ruby | 222 lines | 173 code | 43 blank | 6 comment | 20 complexity | 6d90785f62ef31be52b796d8ce01d2e6 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, LGPL-2.1, GPL-2.0, MIT
- ##
- # 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',
- '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