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