metasploit-framework/lib/msf/core/exploit/ipv6.rb

264 lines
6.1 KiB
Ruby

##
# $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