local nmap = require "nmap" local packet = require "packet" local ipOps = require "ipOps" local bin = require "bin" local stdnse = require "stdnse" local string = require "string" local target = require "target" local table = require "table" description = [[ Queries a target router for multicast information. This works by sending a DVMRP Ask Neighbors 2 request to the target and listening for the DVMRP Neighbors 2 response that contains local addresses and the multicast neighbors on each one. It is similar to the mrinfo utility included with Windows (Linux/UNIX versions are also available). ]] --- -- @args mrinfo.timeout Time to wait for a response in seconds. -- Defaults to 5 seconds. -- --@usage -- nmap --script mrinfo -- --@output -- Host script results: -- | mrinfo: -- | Version 12.4 -- | Local address: 192.168.2.2 -- | Neighbor: 192.168.2.4 -- | Neighbor: 192.168.2.3 -- | Local address: 192.168.13.1 -- | Neighbor: 192.168.13.3 -- |_ Use the newtargets script-arg to add the results as targets author = "Hani Benhabiles" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"discovery", "safe"} hostrule = function(host) if nmap.address_family() ~= 'inet' then stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME) return false end return true end -- Parses a DVMRP Ask Neighbor 2 raw data and returns -- a structured response. -- @param data raw data. local mrinfoParse = function(data) local index, interface, neighbor local response = {} -- first byte should be IGMP type == 0x013 (DVMRP) if data:byte(1) ~= 0x013 then return end -- DVMRP Code index, response.code = bin.unpack(">C", data, 2) -- Checksum index, response.checksum = bin.unpack(">S", data, index) -- Capabilities (Skip one reserved byte) index, response.capabilities = bin.unpack(">C", data, index + 1) -- Major and minor version index, response.minver = bin.unpack(">C", data, index) index, response.majver = bin.unpack(">C", data, index) response.interfaces = {} -- Iterate over target local addresses (interfaces) while index < #data do if data:byte(index) == 0x00 then break end interface = {} -- Local address index, interface.address = bin.unpack("C", data, index) -- Treshold index, interface.treshold= bin.unpack(">C", data, index) -- Flags index, interface.flags = bin.unpack(">C", data, index) -- Number of neighbors index, interface.ncount = bin.unpack(">C", data, index) interface.neighbors = {} -- Iterate over neighbors for i = 1, interface.ncount do index, neighbor = bin.unpack("C", 0x13) -- Code: Ask Neighbor v2 mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x05) -- Checksum: Calculated later mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x0000) -- Reserved mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x000a) -- Version == Cisco IOS 12.4 -- Minor version: 4 mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x04) -- Major version: 12 mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x0c) -- Calculate checksum mrinfo_raw = mrinfo_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(mrinfo_raw)) .. mrinfo_raw:sub(5) return mrinfo_raw end -- Function that sends a DVMRP query. --@param mrinfo_raw Raw DVMRP packet. --@param scrip Source IP of the packet. --@param dstip Destination IP to send to. local mrinfoQuery = function(mrinfo_raw, srcip, dstip) local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. mrinfo_raw -- Less ugly way to do it ? local mrinfo_packet = packet.Packet:new(ip_raw, ip_raw:len()) mrinfo_packet:ip_set_bin_src(ipOps.ip_to_str(srcip)) mrinfo_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip)) mrinfo_packet:ip_set_len(ip_raw:len()) local sock = nmap.new_dnet() sock:ip_open() sock:ip_send(mrinfo_packet.buf) sock:ip_close() end action = function(host) local timeout = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".timeout")) or 5 local mrinfo_raw, dstip, srcip, interface local results = {} timeout = timeout * 1000 dstip = host.ip interface = nmap.get_interface_info(host.interface) srcip = interface.address -- Thread that listens for responses stdnse.new_thread(mrinfoListen, interface, host, timeout, results) -- Send request stdnse.sleep(0.5) mrinfo_raw = mrinfoRaw() mrinfoQuery(mrinfo_raw, srcip, dstip) local condvar = nmap.condvar(results) condvar("wait") if #results > 0 then local output, ifoutput = {} local response = results[1] table.insert(output, ("Version %s.%s"):format(response.majver, response.minver)) for _, interface in pairs(response.interfaces) do ifoutput = {} ifoutput.name = "Local address: " .. interface.address for _, neighbor in pairs(interface.neighbors) do if target.ALLOW_NEW_TARGETS then target.add(neighbor) end table.insert(ifoutput, "Neighbor: " .. neighbor) end table.insert(output, ifoutput) end if not target.ALLOW_NEW_TARGETS then table.insert(output,"Use the newtargets script-arg to add the results as targets") end return stdnse.format_output(true, output) end end