264 lines
6.1 KiB
Ruby
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
|
||
|
|