## # $Id$ ## module Msf ### # # This module provides common tools for IPv6 # ### module Exploit::Remote::Ipv6 require 'racket' # # Initializes an instance of an exploit module that captures traffic # def initialize(info = {}) super register_options( [ OptString.new('INTERFACE', [false, 'The name of the interface']), OptString.new("SMAC", [ false, "The source MAC address"]), OptAddress.new("SHOST", [ false, "The source IPv6 address" ] ), OptInt.new("TIMEOUT", [ true, "Timeout when waiting for host response.", 5]) ], Msf::Exploit::Remote::Ipv6 ) begin require 'pcaprub' @pcaprub_loaded = true rescue ::Exception => e @pcaprub_loaded = false @pcaprub_error = e end end # # Shortcut method for resolving our local interface name # def ipv6_interface(opts={}) opts['INTERFACE'] || datastore['INTERFACE'] || ::Pcap.lookupdev end # # Shortcut method for determining our link-local address # def ipv6_link_address(opts={}) Rex::Socket.ipv6_link_address(ipv6_interface(opts)) end # # Shortcut method for determining our MAC address # def ipv6_mac(opts={}) Rex::Socket.ipv6_mac(ipv6_interface(opts)) end # # Opens a pcaprub capture interface to inject packets, and sniff ICMPv6 packets # def open_icmp_pcap(opts = {}) check_pcaprub_loaded dev = ipv6_interface(opts) len = 65535 tim = 0 @ipv6_icmp6_capture = ::Pcap.open_live(dev, len, true, tim) @ipv6_icmp6_capture.setfilter("icmp6") end # # Close the capture interface # def close_icmp_pcap() check_pcaprub_loaded return if not @ipv6_icmp6_capture @ipv6_icmp6_capture = nil GC.start() end # # Send out a ICMPv6 neighbor solicitation, and # return the associated MAC address # def solicit_ipv6_mac(dhost, opts = {}) check_pcaprub_loaded dhost_intf = dhost + '%' + ipv6_interface(opts) smac = opts['SMAC'] || datastore['SMAC'] || ipv6_mac shost = opts['SHOST'] || datastore['SHOST'] || Rex::Socket.source_address(dhost_intf) timeout = opts['TIMEOUT'] || datastore['TIMEOUT'] || 3 open_icmp_pcap() p = Racket::Racket.new() p.l2 = Racket::L2::Ethernet.new() p.l2.src_mac = smac p.l2.dst_mac = Racket::L3::Misc.soll_mcast_mac(dhost) p.l2.ethertype = Racket::L2::Ethernet::ETHERTYPE_IPV6 p.l3 = Racket::L3::IPv6.new() p.l3.src_ip = Racket::L3::Misc.ipv62long(shost) p.l3.dst_ip = Racket::L3::Misc.ipv62long(Racket::L3::Misc.soll_mcast_addr6(dhost)) p.l3.ttl = 255 p.l3.nhead = 0x3a p.l3.fix!() p.l4 = Racket::L4::ICMPv6NeighborSolicitation.new() p.l4.address = Racket::L3::Misc.ipv62long(dhost) p.l4.add_option(0x01, Racket::L2::Misc.mac2string(p.l2.src_mac)) p.l4.fix!(p.l3.src_ip, p.l3.dst_ip) @ipv6_icmp6_capture.inject(p.pack()) # Wait for a response max_epoch = ::Time.now.to_i + timeout while(::Time.now.to_i < max_epoch) pkt = @ipv6_icmp6_capture.next() next if not pkt eth = Racket::L2::Ethernet.new(pkt) next if eth.ethertype != Racket::L2::Ethernet::ETHERTYPE_IPV6 ipv6 = Racket::L3::IPv6.new(eth.payload) next if ipv6.nhead != 0x3a icmpv6 = Racket::L4::ICMPv6.new(ipv6.payload) next if icmpv6.type != Racket::L4::ICMPv6Generic::ICMPv6_TYPE_NEIGHBOR_ADVERTISEMENT icmpv6 = Racket::L4::ICMPv6NeighborAdvertisement.new(ipv6.payload) if(icmpv6 and ipv6.dst_ip == Racket::L3::Misc.ipv62long(shost) and ipv6.src_ip == Racket::L3::Misc.ipv62long(dhost)) icmpv6options = icmpv6.get_options() icmpv6options.each() do |opt| id = opt[1] if(id == ICMPv6OptionLinkAddress::ICMPv6_OPTION_TYPE_ID) addr = ICMPv6OptionLinkAddress.new(opt[2]).lladdr close_icmp_pcap() return(addr) end end # If there is no addr option, return the ethernet mac close_icmp_pcap() return(eth.src_mac) end end close_icmp_pcap() return(nil) end # # Send a ICMPv6 Echo Request, and wait for the # associated ICMPv6 Echo Response # def ping6(dhost, opts={}) check_pcaprub_loaded dhost_intf = dhost + '%' + ipv6_interface(opts) smac = opts['SMAC'] || datastore['SMAC'] || ipv6_mac shost = opts['SHOST'] || datastore['SHOST'] || Rex::Socket.source_address(dhost_intf) dmac = opts['DMAC'] || solicit_ipv6_mac(dhost) timeout = opts['TIMEOUT'] || datastore['TIMEOUT'] wait = opts['WAIT'] if(wait.eql?(nil)) wait = true end dmac.eql?(nil) and return false open_icmp_pcap() # Create ICMPv6 Request p = Racket::Racket.new() p.l2 = Racket::L2::Ethernet.new() p.l2.src_mac = smac p.l2.dst_mac = dmac p.l2.ethertype = Racket::L2::Ethernet::ETHERTYPE_IPV6 p.l3 = Racket::L3::IPv6.new() p.l3.src_ip = Racket::L3::Misc.ipv62long(shost) p.l3.dst_ip = Racket::L3::Misc.ipv62long(dhost) p.l3.nhead = 0x3a p.l3.fix!() p.l4 = Racket::L4::ICMPv6EchoRequest.new() p.l4.id = rand(65000) p.l4.sequence = 1 p.l4.payload = Rex::Text.rand_text(8) p.l4.fix!(p.l3.src_ip, p.l3.dst_ip) @ipv6_icmp6_capture.inject(p.pack()) if(wait.eql?(true)) print_status("Waiting for ping reply...") print_line("") # Wait for a response max_epoch = ::Time.now.to_i + timeout while(::Time.now.to_i < max_epoch) pkt = @ipv6_icmp6_capture.next() next if not pkt eth = Racket::L2::Ethernet.new(pkt) next if eth.ethertype != Racket::L2::Ethernet::ETHERTYPE_IPV6 ipv6 = Racket::L3::IPv6.new(eth.payload) next if ipv6.nhead != 0x3a icmpv6 = Racket::L4::ICMPv6.new(ipv6.payload) next if icmpv6.type != Racket::L4::ICMPv6Generic::ICMPv6_TYPE_ECHO_REPLY icmpv6 = Racket::L4::ICMPv6EchoReply.new(ipv6.payload) if(icmpv6 and ipv6.dst_ip == p.l3.src_ip and ipv6.src_ip == p.l3.dst_ip and icmpv6.id == p.l4.id and icmpv6.sequence == p.l4.sequence) close_icmp_pcap() return(true) end end # End while end close_icmp_pcap() return(false) end def check_pcaprub_loaded unless @pcaprub_loaded print_status("The Pcaprub module is not available: #{@pcaprub_error}") raise RuntimeError, "Pcaprub not available" else true end end class ICMPv6OptionLinkAddress < RacketPart ICMPv6_OPTION_TYPE_ID = 1 hex_octets :lladdr, 48 def initialize(*args) super(*args) end end end end