1local datafiles = require "datafiles" 2local nmap = require "nmap" 3local stdnse = require "stdnse" 4local string = require "string" 5local table = require "table" 6 7description = [[ 8Shows extra information about IPv6 addresses, such as embedded MAC or IPv4 addresses when available. 9 10Some IP address formats encode extra information; for example some IPv6 11addresses encode an IPv4 address or MAC address. This script can decode 12these address formats: 13* IPv4-compatible IPv6 addresses, 14* IPv4-mapped IPv6 addresses, 15* Teredo IPv6 addresses, 16* 6to4 IPv6 addresses, 17* IPv6 addresses using an EUI-64 interface ID, 18* IPv4-embedded IPv6 addresses, 19* IPv4-translated IPv6 addresses and 20* ISATAP Modified EUI-64 IPv6 addresses. 21 22See RFC 4291 for general IPv6 addressing architecture and the 23definitions of some terms. 24]] 25 26--- 27-- @output 28-- Nmap scan report for ::1.2.3.4 29-- Host script results: 30-- | address-info: 31-- | IPv4-compatible: 32-- |_ IPv4 address: 1.2.3.4 33-- 34-- Nmap scan report for ::ffff:1.2.3.4 35-- Host script results: 36-- | address-info: 37-- | IPv4-mapped: 38-- |_ IPv4 address: 1.2.3.4 39-- 40-- Nmap scan report for 2001:0:506:708:282a:3d75:fefd:fcfb 41-- Host script results: 42-- | address-info: 43-- | Teredo: 44-- | Server IPv4 address: 5.6.7.8 45-- | Client IPv4 address: 1.2.3.4 46-- |_ UDP port: 49802 47-- 48-- Nmap scan report for 2002:102:304::1 49-- Host script results: 50-- | address-info: 51-- | 6to4: 52-- |_ IPv4 address: 1.2.3.4 53-- 54-- Nmap scan report for fe80::a8bb:ccff:fedd:eeff 55-- Host script results: 56-- | address-info: 57-- | IPv6 EUI-64: 58-- | MAC address: 59-- | address: aa:bb:cc:dd:ee:ff 60-- |_ manuf: Unknown 61-- 62-- Nmap scan report for 64:ff9b::c000:221 63-- Host script results: 64-- | address-info: 65-- | IPv4-embedded IPv6 address: 66-- |_ IPv4 address: 192.0.2.33 67-- 68-- Nmap scan report for ::ffff:0:c0a8:101 69-- Host script results: 70-- | address-info: 71-- | IPv4-translated IPv6 address: 72-- |_ IPv4 address: 192.168.1.1 73 74-- * ISATAP. RFC 5214. 75-- XXXX:XXXX:XXXX:XX00:0000:5EFE:a.b.c.d 76 77--- 78--@xmloutput 79-- <table key="IPv4-mapped"> 80-- <elem key="IPv4 address">1.2.3.4</elem> 81-- </table> 82-- 83-- <table key="IPv4-compatible"> 84-- <elem key="IPv4 address">1.2.3.4</elem> 85-- </table> 86-- 87-- <table key="Teredo"> 88-- <elem key="Server IPv4 address">5.6.7.8</elem> 89-- <elem key="Client IPv4 address">1.2.3.4</elem> 90-- <elem key="UDP port">49802</elem> 91-- </table> 92-- 93-- <table key="6to4"> 94-- <elem key="IPv4 address">1.2.3.4</elem> 95-- </table> 96-- 97-- <table key="IPv6 EUI-64"> 98-- <table key="MAC address"> 99-- <elem key="address">aa:bb:cc:dd:ee:ff</elem> 100-- <elem key="manuf">Unknown</elem> 101-- </table> 102-- </table> 103-- 104-- <table key="IPv4-embedded IPv6 address"> 105-- <elem key="IPv4 address">192.0.2.33</elem> 106-- </table> 107-- 108-- <table key="IPv4-translated IPv6 address"> 109-- <elem key="IPv4 address">192.168.1.1</elem> 110-- </table> 111 112author = "David Fifield" 113 114license = "Same as Nmap--See https://nmap.org/book/man-legal.html" 115 116categories = {"default", "safe"} 117 118 119hostrule = function(host) 120 return true 121end 122 123-- Match an address (array of bytes) against a hex-encoded pattern. "XX" in the 124-- pattern is a wildcard. 125local function matches(addr, pattern) 126 local octet_patterns 127 128 octet_patterns = {} 129 for op in pattern:gmatch("([%xX][%xX])") do 130 octet_patterns[#octet_patterns + 1] = op 131 end 132 133 if #addr ~= #octet_patterns then 134 return false 135 end 136 137 for i = 1, #addr do 138 local a, op 139 140 a = addr[i] 141 op = octet_patterns[i] 142 if not (op == "XX" or a == tonumber(op, 16)) then 143 return false 144 end 145 end 146 147 return true 148end 149 150local function get_manuf(mac) 151 local catch = function() return "Unknown" end 152 local try = nmap.new_try(catch) 153 local mac_prefixes = try(datafiles.parse_mac_prefixes()) 154 local prefix = string.upper(string.format("%02x%02x%02x", mac[1], mac[2], mac[3])) 155 return mac_prefixes[prefix] or "Unknown" 156end 157 158local function format_mac(mac) 159 local out = stdnse.output_table() 160 out.address = stdnse.format_mac(string.char(table.unpack(mac))) 161 out.manuf = get_manuf(mac) 162 return out 163end 164 165local function format_ipv4(ipv4) 166 local octets 167 168 octets = {} 169 for _, v in ipairs(ipv4) do 170 octets[#octets + 1] = string.format("%d", v) 171 end 172 173 return table.concat(octets, ".") 174end 175 176local function do_ipv4(addr) 177end 178 179-- EUI-64 from MAC, RFC 4291. 180local function decode_eui_64(eui_64) 181 if eui_64[4] == 0xff and eui_64[5] == 0xfe then 182 return { (eui_64[1] ~ 0x02), 183 eui_64[2], eui_64[3], eui_64[6], eui_64[7], eui_64[8] } 184 end 185end 186 187local function do_ipv6(addr) 188 local label 189 local output 190 191 output = stdnse.output_table() 192 193 if matches(addr, "0000:0000:0000:0000:0000:0000:0000:0001") then 194 -- ::1 is localhost. Not much to report. 195 return nil 196 elseif matches(addr, "0000:0000:0000:0000:0000:0000:XXXX:XXXX") then 197 -- RFC 4291 2.5.5.1. 198 local ipv4 = { addr[13], addr[14], addr[15], addr[16] } 199 return {["IPv4-compatible"]= { ["IPv4 address"] = format_ipv4(ipv4) } } 200 elseif matches(addr, "0000:0000:0000:0000:0000:ffff:XXXX:XXXX") then 201 -- RFC 4291 2.5.5.2. 202 local ipv4 = { addr[13], addr[14], addr[15], addr[16] } 203 return {["IPv4-mapped"]= { ["IPv4 address"] = format_ipv4(ipv4) } } 204 elseif matches(addr, "2001:0000:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then 205 -- Teredo, RFC 4380. 206 local server_ipv4 = { addr[5], addr[6], addr[7], addr[8] } 207 -- RFC 5991 makes the flags mostly meaningless. 208 local flags = addr[9] * 256 + addr[10] 209 local obs_port = addr[11] * 256 + addr[12] 210 local obs_client_ipv4 = { addr[13], addr[14], addr[15], addr[16] } 211 local port, client_ipv4 212 213 -- Invert obs_port. 214 port = obs_port ~ 0xffff 215 216 -- Invert obs_client_ipv4. 217 client_ipv4 = {} 218 for _, octet in ipairs(obs_client_ipv4) do 219 client_ipv4[#client_ipv4 + 1] = octet ~ 0xff 220 end 221 222 output["Server IPv4 address"] = format_ipv4(server_ipv4) 223 output["Client IPv4 address"] = format_ipv4(client_ipv4) 224 output["UDP port"] = tostring(port) 225 226 return {["Teredo"] = output} 227 elseif matches(addr, "0064:ff9b:XXXX:XXXX:00XX:XXXX:XXXX:XXXX") then 228 --IPv4-embedded IPv6 addresses. RFC 6052, Section 2 229 230 --skip addr[9] 231 if matches(addr,"0064:ff9b:0000:0000:0000:0000:XXXX:XXXX") then 232 local ipv4 = {addr[13], addr[14], addr[15], addr[16]} 233 return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}} 234 elseif addr[5] ~= 0x01 then 235 local ipv4 = {addr[5], addr[6], addr[7], addr[8]} 236 return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}} 237 elseif addr[6] ~= 0x22 then 238 local ipv4 = {addr[6], addr[7], addr[8], addr[10]} 239 return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}} 240 elseif addr[7] ~= 0x03 then 241 local ipv4 = {addr[7], addr[8], addr[10], addr[11]} 242 return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}} 243 elseif addr[8] ~= 0x44 then 244 local ipv4 = {addr[8], addr[10], addr[11], addr[12]} 245 return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}} 246 elseif addr[10] == 0x00 and addr[11] == 0x00 and addr[12] == 0x00 then 247 local ipv4 = {addr[13], addr[14], addr[15], addr[16]} 248 return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}} 249 end 250 elseif matches(addr, "0000:0000:0000:0000:ffff:0000:XXXX:XXXX") then 251 -- IPv4-translated IPv6 addresses. RFC 2765, Section 2.1 252 return {["IPv4-translated IPv6 address"]= 253 {["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}} 254 elseif matches(addr, "XXXX:XXXX:XXXX:XX00:0000:5efe:XXXX:XXXX") then 255 -- ISATAP. RFC 5214, Appendix A 256 -- XXXX:XXXX:XXXX:XX00:0000:5EFE:a.b.c.d 257 return {["ISATAP Modified EUI-64 IPv6 Address"]= 258 {["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}} 259 end 260 261 -- These following use common handling for the Interface ID part 262 -- (last 64 bits). 263 264 if matches(addr, "2002:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then 265 -- 6to4, RFC 3056. 266 local ipv4 = { addr[3], addr[4], addr[5], addr[6] } 267 268 label = "6to4" 269 output["IPv4 address"] = format_ipv4(ipv4) 270 end 271 272 local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12], 273 addr[13], addr[14], addr[15], addr[16] }) 274 if mac then 275 output["MAC address"] = format_mac(mac) 276 if not label then 277 label = "IPv6 EUI-64" 278 end 279 end 280 281 if label then 282 return {[label]= output} 283 end 284 -- else no match 285end 286 287action = function(host) 288 local addr_s, addr_t 289 290 addr_s = host.bin_ip 291 addr_t = { string.byte(addr_s, 1, #addr_s) } 292 293 if #addr_t == 4 then 294 return do_ipv4(addr_t) 295 elseif #addr_t == 16 then 296 return do_ipv6(addr_t) 297 end 298end 299