diff --git a/scripts/script.db b/scripts/script.db index 34f86fb96..9abd37113 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -226,6 +226,9 @@ Entry { filename = "ssl-known-key.nse", categories = { "discovery", "safe", "vul Entry { filename = "sslv2.nse", categories = { "default", "safe", } } Entry { filename = "stuxnet-detect.nse", categories = { "discovery", "intrusive", } } Entry { filename = "svn-brute.nse", categories = { "auth", "intrusive", } } +Entry { filename = "targets-ipv6-multicast-echo.nse", categories = { "discovery", } } +Entry { filename = "targets-ipv6-multicast-invalid-dst.nse", categories = { "discovery", } } +Entry { filename = "targets-ipv6-multicast-slaac.nse", categories = { "discovery", } } Entry { filename = "targets-sniffer.nse", categories = { "broadcast", "discovery", "safe", } } Entry { filename = "targets-traceroute.nse", categories = { "discovery", "safe", } } Entry { filename = "telnet-brute.nse", categories = { "auth", "intrusive", } } diff --git a/scripts/targets-ipv6-multicast-echo.nse b/scripts/targets-ipv6-multicast-echo.nse new file mode 100644 index 000000000..07440001e --- /dev/null +++ b/scripts/targets-ipv6-multicast-echo.nse @@ -0,0 +1,122 @@ +description = [[ +Multicast echo ping. +Do a very fast host discovery on link-local IPv6 network. +]] + +--- +-- @usage +-- ./nmap -6 --script=targets-ipv6-multicast-echo.nse --script-args 'newtargets,interface=eth0,ipv6.src=2001:da8:215:3320:223:aeff:fe5d:3b10' -sL +-- @args newtargets If true, add discovered targets to the scan queue. +-- @args interface The interface to use for host discovery. +-- @args ipv6.src The source IPv6 address in the probe packets. + +author = "David and Weilin" + +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" + +categories = {"discovery"} + +require 'nmap' +require 'target' +require 'packet' +local bit = require 'bit' + +prerule = function() + return nmap.is_privileged() and + (stdnse.get_script_args("interface") or nmap.get_interface()) +end + +catch = function() + dnet:ethernet_close() + pcap:pcap_close() +end +try = nmap.new_try(catch) + +local function get_ipv6_interface_info_by_name(if_name) + local ifaces = nmap.list_interfaces() + local iface + local if_nfo + for _,iface in pairs(ifaces) do + if if_name == iface.device and (#iface.address>15 or string.find(iface.address, "::")) then + if_nfo = iface + return if_nfo + end + end + return nil +end + +local function get_identifier(ip6_addr) + return string.sub(ip6_addr, 9, 16) +end + +action = function() + local if_name = stdnse.get_script_args("targets-ipv6-multicast.interface") or nmap.get_interface() + local if_nfo = get_ipv6_interface_info_by_name(if_name) + if not if_nfo then + stdnse.print_debug("Invalid interface: " .. if_name) + return false + end + local src_mac = if_nfo.mac + local src_ip6 = packet.ip6tobin(stdnse.get_script_args("ipv6.src")) or packet.ip6tobin(if_nfo.address) + local dst_mac = packet.mactobin("33:33:00:00:00:01") + local dst_ip6 = packet.ip6tobin("ff02::1") + local id_set = {} + +---------------------------------------------------------------------------- +--Multicast echo ping probe + + local dnet = nmap.new_dnet() + local pcap = nmap.new_socket() + + try(dnet:ethernet_open(if_name)) + pcap:pcap_open(if_name, 128, false, "icmp6 and ip6[6:1] = 58 and ip6[40:1] = 129") + + local probe = packet.Frame:new() + probe.mac_src = src_mac + probe.mac_dst = dst_mac + probe.ip6_src = src_ip6 + probe.ip6_dst = dst_ip6 + probe.echo_id = 5 + probe.echo_seq = 6 + probe.echo_data = "Nmap host discovery." + probe:build_icmpv6_echo_request() + probe:build_icmpv6_header() + probe:build_ipv6_packet() + probe:build_ether_frame() + + try(dnet:ethernet_send(probe.frame_buf)) + + pcap:set_timeout(1000) + local pcap_timeout_count = 0 + local nse_timeout = 5 + local start_time = nmap:clock() + local cur_time = nmap:clock() + + local found_targets = 0 + + repeat + local status, length, layer2, layer3 = pcap:pcap_receive() + cur_time = nmap:clock() + if not status then + pcap_timeout_count = pcap_timeout_count + 1 + else + local reply = packet.Frame:new(layer2..layer3) + if reply.mac_dst == src_mac then + local identifier = get_identifier(reply.ip6_src) + if not id_set[identifier] then + id_set[identifier] = true + local target_str = packet.toipv6(reply.ip6_src) + target.add(target_str) + found_targets = found_targets + 1 + end + end + end + until pcap_timeout_count >= 2 or cur_time - start_time >= nse_timeout + + stdnse.print_debug(0, "[Echo ping] Found %d targets.", found_targets) + + dnet:ethernet_close() + pcap:pcap_close() + + return true +end diff --git a/scripts/targets-ipv6-multicast-invalid-dst.nse b/scripts/targets-ipv6-multicast-invalid-dst.nse new file mode 100644 index 000000000..1c4ac0667 --- /dev/null +++ b/scripts/targets-ipv6-multicast-invalid-dst.nse @@ -0,0 +1,140 @@ +description = [[ +Multicast invalid destination options ping. +Do a very fast host discovery on link-local IPv6 network. +]] + +--- +-- @usage +-- ./nmap -6 --script=targets-ipv6-multicast-invalid-dst.nse --script-args 'newtargets,interface=eth0,ipv6=2001:da8:215:3320:223:aeff:fe5d:3b10' -sP +-- @args newtargets If true, add discovered targets to the scan queue. +-- @args interface The interface to use for host discovery. +-- @args ipv6.src The source IPv6 address in the probe packets. + +author = "David and Weilin" + +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" + +categories = {"discovery"} + +require 'nmap' +require 'target' +require 'packet' +local bit = require 'bit' + +prerule = function() + return nmap.is_privileged() and + (stdnse.get_script_args("interface") or nmap.get_interface()) +end + +catch = function() + dnet:ethernet_close() + pcap:pcap_close() +end +try = nmap.new_try(catch) + +local function get_ipv6_interface_info_by_name(if_name) + local ifaces = nmap.list_interfaces() + local iface + local if_nfo + for _,iface in pairs(ifaces) do + if if_name == iface.device and (#iface.address>15 or string.find(iface.address, "::")) then + if_nfo = iface + return if_nfo + end + end + return nil +end + +local function get_identifier(ip6_addr) + return string.sub(ip6_addr, 9, 16) +end + +--- Build an IPv6 invalid extension header. +-- @param nxt_hdr integer that stands for next header's type +local function build_invalid_extension_header(nxt_hdr) + local ex_invalid_opt = string.char(0x80,0x01,0xfe,0x18,0xfe,0x18,0xfe,0x18,0x0,0x0,0x0,0x0,0x0,0x0) + local ext_header = + string.char(nxt_hdr) .. --next header + string.char(#ex_invalid_opt/16) .. --length (16bytes) + ex_invalid_opt + return ext_header +end + +action = function() + local if_name = stdnse.get_script_args("targets-ipv6-multicast.interface") or nmap.get_interface() + local if_nfo = get_ipv6_interface_info_by_name(if_name) + if not if_nfo then + stdnse.print_debug("Invalid interface: " .. if_name) + return false + end + local src_mac = if_nfo.mac + local src_ip6 = packet.ip6tobin(stdnse.get_script_args("ipv6.src")) or packet.ip6tobin(if_nfo.address) + local dst_mac = packet.mactobin("33:33:00:00:00:01") + local dst_ip6 = packet.ip6tobin("ff02::1") + local id_set = {} + +---------------------------------------------------------------------------- +--Multicast invalid destination exheader probe + + local dnet = nmap.new_dnet() + local pcap = nmap.new_socket() + + try(dnet:ethernet_open(if_name)) + pcap:pcap_open(if_name, 128, false, "icmp6 and ip6[6:1] = 58 and ip6[40:1] = 4") + + local probe = packet.Frame:new() + probe.mac_src = src_mac + probe.mac_dst = dst_mac + probe.ip6_src = src_ip6 + probe.ip6_dst = dst_ip6 + + probe.echo_id = 5 + probe.echo_seq = 6 + probe.echo_data = "Nmap host discovery." + probe:build_icmpv6_echo_request() + probe:build_icmpv6_header() + + probe.exheader = build_invalid_extension_header(packet.IPPROTO_ICMPV6) + probe.ip6_nxt_hdr = packet.IPPROTO_DSTOPTS + + probe:build_ipv6_packet() + probe:build_ether_frame() + + try(dnet:ethernet_send(probe.frame_buf)) + + pcap:set_timeout(1000) + local pcap_timeout_count = 0 + local nse_timeout = 5 + local start_time = nmap:clock() + local cur_time = nmap:clock() + + local found_targets = 0 + + repeat + local status, length, layer2, layer3 = pcap:pcap_receive() + cur_time = nmap:clock() + if not status then + pcap_timeout_count = pcap_timeout_count + 1 + else + local reply = packet.Frame:new(layer2) + if reply.mac_dst == src_mac then + reply = packet.Packet:new(layer3) + local target_addr = reply.ip6_src + found_targets = found_targets + 1 + local identifier = get_identifier(reply.ip6_src) + if not id_set[identifier] then + id_set[identifier] = true + local target_str = packet.toipv6(target_addr) + target.add(target_str) + end + end + end + until pcap_timeout_count >= 2 or cur_time - start_time >= nse_timeout + + stdnse.print_debug(0, "[Invalid DSTOPTS] Found %d targets.", found_targets) + + dnet:ethernet_close() + pcap:pcap_close() + + return true +end diff --git a/scripts/targets-ipv6-multicast-slaac.nse b/scripts/targets-ipv6-multicast-slaac.nse new file mode 100644 index 000000000..f69c9a6a5 --- /dev/null +++ b/scripts/targets-ipv6-multicast-slaac.nse @@ -0,0 +1,179 @@ +description = [[ +SLAAC-based host discovery. +Do a very fast host discovery on link-local IPv6 network. +]] + +--- +-- @usage +-- ./nmap -6 --script=slaac_host_discovery.nse --script-args 'newtargets,interface=eth0,ipv6="2001:da8:215:3320:223:aeff:fe5d:3b10"' -sP + +author = "David and Weilin" + +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" + +categories = {"discovery"} + +require 'nmap' +require 'target' +require 'packet' +require "os" +require "math" + +prerule = function() + return nmap.is_privileged() and + (stdnse.get_script_args("targets-ipv6-multicast.interface") or nmap.get_interface()) +end + +catch = function() + dnet:ethernet_close() + pcap:pcap_close() +end +try = nmap.new_try(catch) + +local function get_ipv6_interface_info_by_name(if_name) + local ifaces = nmap.list_interfaces() + local iface + local if_nfo + for _,iface in pairs(ifaces) do + if if_name == iface.device and iface.address and (#iface.address>15 or string.find(iface.address, "::")) then + if_nfo = iface + return if_nfo + end + end + return nil +end + +local function get_identifier(ip6_addr) + return string.sub(ip6_addr, 9, 16) +end + +--- Get a Unique-local Address with random global ID. +-- @param local_scope The scope of the address, local or reserved. +-- @return A 16-byte string of IPv6 address, and the length of the prefix. +local function get_radom_ula_prefix(local_scope) + local ula_prefix + math.randomseed(os.time()) + local global_id = string.char(math.random(256)-1,math.random(256)-1,math.random(256)-1,math.random(256)-1,math.random(256)-1) + + if local_scope then + ula_prefix = packet.ip6tobin("fd00::") + else + ula_prefix = packet.ip6tobin("fc00::") + end + ula_prefix = string.sub(ula_prefix,1,1) .. global_id .. string.sub(ula_prefix,7,-1) + return ula_prefix,64 +end + +--- Build an ICMPv6 payload of Router Advertisement. +-- @param mac_src six-byte string of the source MAC address. +-- @param prefix 16-byte string of IPv6 address. +-- @param prefix_len integer that represents the length of the prefix. +-- @param valid_time integer that represents the valid time of the prefix. +-- @param preferred_time integer that represents the preferred time of the prefix. +local function build_router_advert(mac_src,prefix,prefix_len,valid_time,preferred_time) + local ra_msg = string.char(0x0, --cur hop limit + 0x08, --flags + 0x00,0x00, --router lifetime + 0x00,0x00,0x00,0x00, --reachable time + 0x00,0x00,0x00,0x00) --retrans timer + local prefix_option_msg = string.char(prefix_len, + 0xc0) .. --flags: Onlink, Auto + packet.set_u32("....",0,valid_time) .. + packet.set_u32("....",0,preferred_time) .. + string.char(0,0,0,0) .. --unknown + prefix + local icmpv6_prefix_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_PREFIX_INFORMATION,prefix_option_msg) + local icmpv6_src_link_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_SOURCE_LINKADDR,mac_src) + local icmpv6_payload = ra_msg .. icmpv6_prefix_option .. icmpv6_src_link_option + return icmpv6_payload +end + +action = function() + local if_name = stdnse.get_script_args("targets-ipv6-multicast.interface") or nmap.get_interface() + local if_nfo = get_ipv6_interface_info_by_name(if_name) + if not if_nfo then + stdnse.print_debug("Invalid interface: " .. if_name) + return false + end + local src_mac = if_nfo.mac + local src_ip6 = packet.ip6tobin(stdnse.get_script_args("ipv6.src")) or packet.ip6tobin(if_nfo.address) + local dst_mac = packet.mactobin("33:33:00:00:00:01") + local dst_ip6 = packet.ip6tobin("ff02::1") + local id_set = {} + +---------------------------------------------------------------------------- +--SLAAC-based host discovery probe + + local dnet = nmap.new_dnet() + local pcap = nmap.new_socket() + + try(dnet:ethernet_open(if_name)) + pcap:pcap_open(if_name, 128, true, "src ::0/128 and dst net ff02::1:0:0/96 and icmp6 and ip6[6:1] = 58 and ip6[40:1] = 135") + + local actual_prefix = string.sub(src_ip6,1,8) + local ula_prefix, prefix_len = get_radom_ula_prefix() + + -- preferred_lifetime <= valid_lifetime. + -- Nmap will get the whole IPv6 addresses of each host if the two parameters are both longer than 5 seconds. + -- Sometimes it makes sense to regard the several addresses of a host as different hosts, as the host's administrator may apply different firewall configurations on them. + local valid_lifetime = 6 + local preferred_lifetime = 6 + + local probe = packet.Frame:new() + + probe.ip6_src = packet.mac_to_lladdr(src_mac) + probe.ip6_dst = dst_ip6 + probe.mac_src = src_mac + probe.mac_dst = packet.mactobin("33:33:00:00:00:01") + + local icmpv6_payload = build_router_advert(src_mac,ula_prefix,prefix_len,valid_lifetime,preferred_lifetime) + probe:build_icmpv6_header(packet.ND_ROUTER_ADVERT, 0, icmpv6_payload) + probe:build_ipv6_packet() + probe:build_ether_frame() + + try(dnet:ethernet_send(probe.frame_buf)) + + local expected_mac_dst_prefix = packet.mactobin("33:33:ff:00:00:00") + local expected_ip6_src = packet.ip6tobin("::") + local expected_ip6_dst_prefix = packet.ip6tobin("ff02::1:0:0") + + pcap:set_timeout(1000) + local pcap_timeout_count = 0 + local nse_timeout = 5 + local start_time = nmap:clock() + local cur_time = nmap:clock() + + local found_targets = 0 + + repeat + local status, length, layer2, layer3 = pcap:pcap_receive() + cur_time = nmap:clock() + if not status then + pcap_timeout_count = pcap_timeout_count + 1 + else + local reply = packet.Frame:new(layer2) + if string.sub(reply.mac_dst, 1, 3) == string.sub(expected_mac_dst_prefix, 1, 3) then + reply = packet.Packet:new(layer3) + if reply.ip6_src == expected_ip6_src and + string.sub(expected_ip6_dst_prefix,1,12) == string.sub(reply.ip6_dst,1,12) then + local ula_target_addr_str = packet.toipv6(reply.ns_target) + local identifier = get_identifier(reply.ns_target) + found_targets = found_targets + 1 + --Filter out the reduplicative identifiers. + --A host will send several NS packets with the same interface identifier if it receives several RA packets with different prefix during the discovery phase. + if not id_set[identifier] then + id_set[identifier] = true + local actual_addr_str = packet.toipv6(actual_prefix .. identifier) + target.add(actual_addr_str) + end + end + end + end + until pcap_timeout_count >= 2 or cur_time - start_time >= nse_timeout + + stdnse.print_debug(0, "[SLAAC] Found %d targets.", found_targets) + + dnet:ethernet_close() + pcap:pcap_close() + return true +end