diff --git a/CHANGELOG b/CHANGELOG index 4cda2ffd2..036b81613 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ #Nmap Changelog ($Id$); -*-text-*- +o [NSE] New script targets-ipv6-eui64 generates target IPv6 addresses from a + user-provided file of MAC addresses, using the EUI-64 method. [Daniel Miller] + o [NSE][GH#2973] New service probes and scripts for MikroTik's WinBox router admin service. mikrotik-routeros-version queries the 'info' and 'list' files to get the RouterOS version. mikrotik-routeros-username-brute brute-forces diff --git a/scripts/script.db b/scripts/script.db index da77f4416..4d750cfbc 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -559,6 +559,7 @@ Entry { filename = "stuxnet-detect.nse", categories = { "discovery", "intrusive" Entry { filename = "supermicro-ipmi-conf.nse", categories = { "exploit", "vuln", } } Entry { filename = "svn-brute.nse", categories = { "brute", "intrusive", } } Entry { filename = "targets-asn.nse", categories = { "discovery", "external", "safe", } } +Entry { filename = "targets-ipv6-eui64.nse", categories = { "discovery", } } Entry { filename = "targets-ipv6-map4to6.nse", categories = { "discovery", } } Entry { filename = "targets-ipv6-multicast-echo.nse", categories = { "broadcast", "discovery", "safe", } } Entry { filename = "targets-ipv6-multicast-invalid-dst.nse", categories = { "broadcast", "discovery", "safe", } } diff --git a/scripts/targets-ipv6-eui64.nse b/scripts/targets-ipv6-eui64.nse new file mode 100644 index 000000000..a72fb0e1b --- /dev/null +++ b/scripts/targets-ipv6-eui64.nse @@ -0,0 +1,121 @@ +local ipOps = require "ipOps" +local io = require "io" +local nmap = require "nmap" +local stdnse = require "stdnse" +local string = require "string" +local target = require "target" + +description = [[ +This script runs in the pre-scanning phase to convert 48-bit MAC addresses to +EUI-64 IPv6 addresses, which are often used for auto-configuration. Generated +addresses may be added to the scan queue. + +The MAC addresses used as input are read from the file named by the +targets-ipv6-eui64.input script-arg. A good source of these +addresses would be an IPv4 host discovery Nmap scan. +]] + +--- +-- @usage +-- nmap -6 --script targets-ipv6-eui64 --script-args newtargets,targets-ipv6-eui64.input=macs.txt,targets-ipv6-subnet={2001:db8:c0ca::/64} +-- +-- @output +-- Pre-scan script results: +-- | targets-ipv6-eui64: +-- |_ 2001:db8:c0ca:0:1322:33ff:fe44:5566 +-- +-- @args targets-ipv6-eui64.input The input file containing 1 MAC address per line +-- +-- @args targets-ipv6-subnet Table/single IPv6 address with prefix +-- (Ex. 2001:db8:c0ca::/48 or +-- { 2001:db8:c0ca::/48, 2001:db8:FEA::/48 }) +-- Default: fe80::/64 +-- +-- @xmloutput +-- 2001:db8:c0ca:0:1322:33ff:fe44:5566 + + +author = "Daniel Miller" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = { + "discovery", +} + +local infile = stdnse.get_script_args(SCRIPT_NAME .. ".input") +local subnets = stdnse.get_script_args("targets-ipv6-subnet") or "fe80::/64" + +prerule = function () + + if nmap.address_family() ~= "inet6" then + stdnse.verbose1("This script is IPv6 only.") + return false + end + + if infile == nil then + stdnse.verbose1( "Missing script-arg %s.input", SCRIPT_NAME) + return false + end + + return true +end + +action = function () + + local file, err = io.open(infile, "r") + if not file then + stdnse.verbose1("Unable to open %s for reading: %s", infile, err) + return nil + end + + local eui64 = {} + for mac in file:lines() do + local raw, err = stdnse.fromhex(mac:gsub("[:-]", "")) + if not raw or #raw ~= 6 then + stdnse.debug1("Invalid MAC: %s", mac) + else + local bytes = {raw:byte(1,-1)} + bytes[1] = bytes[1] ~ 0x2 + local eui = string.pack("BBBBBBBB", + bytes[1], bytes[2], bytes[3], + 0xff, 0xfe, + bytes[4], bytes[5], bytes[6] + ) + eui64[#eui64+1] = eui + end + end + + if type(subnets) == "string" then + subnets = { subnets } + end + + local results = {} + for _, subnet in ipairs(subnets) do + local addr, maskbits = subnet:match("^%s*([:%x]+)/(%d+)%s*$") + if not addr then + stdnse.verbose1("Invalid IPv6 subnet: %s", subnet) + else + if tonumber(maskbits) > 64 then + stdnse.verbose1("Subnet too small for EUI-64 addresses.") + else + local v6bin, err = ipOps.ip_to_str(addr, "inet6") + if not v6bin then + stdnse.verbose1("Error parsing %s as IPv6 address: %s", addr, err) + else + v6bin = v6bin:sub(1, 8) + for _, eui in ipairs(eui64) do + local ip6addr, err = ipOps.str_to_ip(v6bin .. eui, "inet6") + if not ip6addr then + stdnse.debug1("Failed to convert addr to IPv6") + else + results[#results+1] = ip6addr + target.add(ip6addr) + end + end + end + end + end + end + if next(results) then + return results + end +end