/modules/auxiliary/scanner/discovery/ipv6_neighbor.rb
Ruby | 229 lines | 165 code | 55 blank | 9 comment | 27 complexity | 4bfd6bef7f588dc8e4561def2d314c3b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
- ##
- # $Id$
- ##
- ##
- # This file is part of the Metasploit Framework and may be subject to
- # redistribution and commercial restrictions. Please see the Metasploit
- # Framework web site for more information on licensing and terms of use.
- # http://metasploit.com/framework/
- ##
- require 'msf/core'
- class Metasploit3 < Msf::Auxiliary
- 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', [true, "Source MAC Address"]),
- ], self.class)
- deregister_options('SNAPLEN', 'FILTER')
- end
- def run_batch_size
- datastore['BATCHSIZE'] || 256
- end
- def run_batch(hosts)
- print_status("Discovering IPv4 nodes via ARP...")
- print_status("")
- shost = datastore['SHOST']
- smac = datastore['SMAC']
- addrs = []
- open_pcap({'SNAPLEN' => 68, 'FILTER' => "arp[6:2] == 0x0002"})
- begin
- found = {}
- hosts.each do |dhost|
- shost = datastore['SHOST'] || Rex::Socket.source_address(dhost)
- probe = buildprobe(datastore['SHOST'], datastore['SMAC'], dhost)
- capture.inject(probe)
- while(reply = getreply())
- next if not reply[:arp]
- if not found[reply[:arp].spa]
- print_status(sprintf(" %16s ALIVE",reply[:arp].spa))
- addrs << [reply[:arp].spa, reply[:arp].sha]
- report_host(:host => reply[:arp].spa, :mac=>reply[:arp].sha)
- found[reply[:arp].spa] = true
- end
- end
- end
- etime = ::Time.now.to_f + (hosts.length * 0.05)
- while (::Time.now.to_f < etime)
- while(reply = getreply())
- next if not reply[:arp]
- if not found[reply[:arp].spa]
- print_status(sprintf(" %16s ALIVE",reply[:arp].spa))
- addrs << [reply[:arp].spa, reply[:arp].sha]
- report_host(:host => reply[:arp].spa, :mac=>reply[:arp].sha)
- found[reply[:arp].spa] = 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 if not adv[:eth].src_mac.eql? mac_addr
- ipv6_addr = Racket::L3::Misc.long2ipv6(adv[:ipv6].src_ip)
- 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 = datastore['SMAC']
- open_pcap({'SNAPLEN' => 68, 'FILTER' => "icmp6"})
- begin
- neighs.each do |neigh|
- host, dmac = neigh
- shost = Racket::L3::Misc.linklocaladdr(smac)
- neigh = Racket::L3::Misc.linklocaladdr(dmac)
- probe = buildsolicitation(smac, shost, neigh)
- capture.inject(probe)
- while(adv = getadvertisement())
- next if not adv[:icmpv6]
- addr = map_neighbor(neighs, adv)
- next if not addr
- print_status(sprintf(" %16s maps to %s",addr[:ipv4], addr[:ipv6]))
- end
- end
- etime = ::Time.now.to_f + (neighs.length * 0.5)
- while (::Time.now.to_f < etime)
- while(adv = getadvertisement())
- next if not adv[:icmpv6]
- 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)
- n = Racket::Racket.new
- n.l2 = Racket::L2::Ethernet.new(Racket::Misc.randstring(14))
- n.l2.src_mac = smac
- n.l2.dst_mac = 'ff:ff:ff:ff:ff:ff'
- n.l2.ethertype = 0x0806
- n.l3 = Racket::L3::ARP.new
- n.l3.opcode = Racket::L3::ARP::ARPOP_REQUEST
- n.l3.sha = n.l2.src_mac
- n.l3.tha = n.l2.dst_mac
- n.l3.spa = shost
- n.l3.tpa = dhost
- n.pack
- end
- def getreply
- pkt = capture.next
- return if not pkt
- eth = Racket::L2::Ethernet.new(pkt)
- return if not eth.ethertype == 0x0806
- arp = Racket::L3::ARP.new(eth.payload)
- return if not arp.opcode == Racket::L3::ARP::ARPOP_REPLY
- {:raw => pkt, :eth => eth, :arp => arp}
- end
- def buildsolicitation(smac, shost, neigh)
- dmac = Racket::L3::Misc.soll_mcast_mac(neigh)
- dhost = Racket::L3::Misc.soll_mcast_addr6(neigh)
- n = Racket::Racket.new
- n.l2 = Racket::L2::Ethernet.new(Racket::Misc.randstring(14))
- n.l2.src_mac = smac
- n.l2.dst_mac = dmac
- n.l2.ethertype = 0x86dd
- n.l3 = Racket::L3::IPv6.new
- n.l3.src_ip = Racket::L3::Misc.ipv62long(shost)
- n.l3.dst_ip = Racket::L3::Misc.ipv62long(dhost)
- n.l3.nhead = 0x3a
- n.l3.ttl = 0xff
- n.l4 = Racket::L4::ICMPv6NeighborSolicitation.new
- n.l4.address = Racket::L3::Misc.ipv62long(neigh)
- n.l4.slla = smac
- n.l4.fix!(n.l3.src_ip, n.l3.dst_ip)
- n.pack
- end
- def getadvertisement
- pkt = capture.next
- return if not pkt
- eth = Racket::L2::Ethernet.new(pkt)
- return if not eth.ethertype == 0x86dd
- ipv6 = Racket::L3::IPv6.new(eth.payload)
- return if not ipv6.nhead == 0x3a
- icmpv6 = Racket::L4::ICMPv6.new(ipv6.payload)
- return if not icmpv6.type == Racket::L4::ICMPv6::ICMPv6_TYPE_NEIGHBOR_ADVERTISEMENT
- icmpv6 = Racket::L4::ICMPv6NeighborAdvertisement.new(ipv6.payload)
- {:raw => pkt, :eth => eth, :ipv6 => ipv6, :icmpv6 => icmpv6}
- end
- end