From d25a2420e450af83bf4fdd7c23a5a286aa5ff614 Mon Sep 17 00:00:00 2001 From: kroosec Date: Wed, 15 Aug 2012 10:24:31 +0000 Subject: [PATCH] Added broadcast-pim-discovery.nse script. --- CHANGELOG | 3 + scripts/broadcast-pim-discovery.nse | 190 ++++++++++++++++++++++++++++ scripts/script.db | 1 + 3 files changed, 194 insertions(+) create mode 100644 scripts/broadcast-pim-discovery.nse diff --git a/CHANGELOG b/CHANGELOG index 2ca5d07b9..d4fa7f480 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added broadcast-pim-discovery script which discovers routers that are + running PIM (Protocol Independant Multicast). [Hani Benhabiles] + o [NSE] Added mtrace script which queries for the multicast path from a source to a destination host. [Hani Benhabiles] diff --git a/scripts/broadcast-pim-discovery.nse b/scripts/broadcast-pim-discovery.nse new file mode 100644 index 000000000..30e3c6910 --- /dev/null +++ b/scripts/broadcast-pim-discovery.nse @@ -0,0 +1,190 @@ +local nmap = require "nmap" +local packet = require "packet" +local ipOps = require "ipOps" +local bin = require "bin" +local stdnse = require "stdnse" +local target = require "target" +local table = require "table" + +description = [[ +Discovers routers that are running PIM (Protocol Independant Multicast). + +This works by sending a PIM Hello message to the PIM multicast address +224.0.0.13 and listening for Hello messages from other routers. +]] + +-- +-- @args broadcast-pim-discovery.timeout Time to wait for responses in seconds. +-- Defaults to 5 seconds. +-- +--@usage +-- nmap --script broadcast-pim-discovery +-- +-- nmap --script broadcast-pim-discovery -e eth1 +-- --script-args 'broadcast-pim-discovery.timeout=10' +-- +--@output +-- Pre-scan script results: +-- | broadcast-pim-discovery: +-- | 172.16.0.12 +-- | 172.16.0.31 +-- | 172.16.0.44 +-- |_ 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", "broadcast"} + +prerule = function() + if nmap.address_family() ~= 'inet' then + stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME) + return false + end + if not nmap.is_privileged() then + stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME) + return false + end + return true +end + +-- Generates a raw PIM Hello message. +--@return hello Raw PIM Hello message +local helloRaw = function() + -- Version: 2, Type: Hello (0) + local hello_raw = bin.pack(">C", 0x20) + -- Reserved + hello_raw = hello_raw.. bin.pack(">C", 0x00) + -- Checksum: Calculated later + hello_raw = hello_raw.. bin.pack(">S", 0x0000) + -- Options (TLVs) + -- Hold time 1 second + hello_raw = hello_raw.. bin.pack(">SSS", 0x01, 0x02, 0x01) + -- Generation ID: Random + hello_raw = hello_raw.. bin.pack(">SSI", 0x14, 0x04, math.random(23456)) + -- DR Priority: 1 + hello_raw = hello_raw.. bin.pack(">SSI", 0x13, 0x04, 0x01) + -- State fresh capable: Version = 1, interval = 0, Reserved + hello_raw = hello_raw.. bin.pack(">SSCCS", 0x15, 0x04, 0x01, 0x00, 0x00) + -- Calculate checksum + hello_raw = hello_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(hello_raw)) .. hello_raw:sub(5) + + return hello_raw +end + +-- Sends a PIM Hello message. +--@param interface Network interface to use. +--@param dstip Destination IP to which send the Hello. +local helloQuery = function(interface, dstip) + local hello_packet, sock, eth_hdr + local srcip = interface.address + + local hello_raw = helloRaw() + local ip_raw = bin.pack("H", "45c00040ed780000016718bc0a00c8750a00c86b") .. hello_raw + hello_packet = packet.Packet:new(ip_raw, ip_raw:len()) + hello_packet:ip_set_bin_src(ipOps.ip_to_str(srcip)) + hello_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip)) + hello_packet:ip_set_len(ip_raw:len()) hello_packet:ip_count_checksum() + + sock = nmap.new_dnet() + sock:ethernet_open(interface.device) + -- Ethernet multicast for PIM, our ethernet address and packet type IP + eth_hdr = bin.pack(">HAS", "01 00 5e 00 00 0d", interface.mac, 0x0800) + sock:ethernet_send(eth_hdr .. hello_packet.buf) + sock:ethernet_close() +end + +-- Listens for PIM Hello messages. +--@param interface Network interface to listen on. +--@param timeout Time to listen for a response. +--@param responses table to insert responders' IPs into. +local helloListen = function(interface, timeout, responses) + local condvar = nmap.condvar(responses) + local start = nmap.clock_ms() + local listener = nmap.new_socket() + local p, hello_raw, status, l3data + + -- PIM packets that are sent to 224.0.0.13 and not coming from our host + local filter = 'ip proto 103 and dst host 224.0.0.13 and src host not ' .. interface.address + listener:set_timeout(100) + listener:pcap_open(interface.device, 1024, true, filter) + + while (nmap.clock_ms() - start) < timeout do + status, _, _, l3data = listener:pcap_receive() + if status then + p = packet.Packet:new(l3data, #l3data) + hello_raw = string.sub(l3data, p.ip_hl*4 + 1) + -- Check that PIM Type is Hello + if p and hello_raw:byte(1) == 0x20 then + table.insert(responses, p.ip_src) + end + end + end + condvar("signal") +end + +--- Returns the network interface used to send packets to the destination host. +--@param destination host to which the interface is used. +--@return interface Network interface used for destination host. +local getInterface = function(destination) + -- First, create dummy UDP connection to get interface + local sock = nmap.new_socket() + local status, err = sock:connect(destination, "12345", "udp") + if not status then + stdnse.print_verbose("%s: %s", SCRIPT_NAME, err) + return + end + local status, address, _, _, _ = sock:get_info() + if not status then + stdnse.print_verbose("%s: %s", SCRIPT_NAME, err) + return + end + for _, interface in pairs(nmap.list_interfaces()) do + if interface.address == address then + return interface + end + end +end + +action = function() + local timeout = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".timeout")) or 5 + local responses = {} + timeout = timeout * 1000 + local mcast = "224.0.0.13" + + -- Get the network interface to use + local interface = nmap.get_interface() + if interface then + interface = nmap.get_interface_info(interface) + else + interface = getInterface(mcast) + end + if not interface then + return ("\n ERROR: Couldn't get interface for %s"):format(mcast) + end + + stdnse.print_debug("%s: will send via %s interface.", SCRIPT_NAME, interface.shortname) + + -- Launch listener + stdnse.new_thread(helloListen, interface, timeout, responses) + + -- Send Hello after small sleep so the listener doesn't miss any responses + stdnse.sleep(0.1) + helloQuery(interface, mcast) + local condvar = nmap.condvar(responses) + condvar("wait") + + if #responses > 0 then + table.sort(responses) + if target.ALLOW_NEW_TARGETS then + for _, response in pairs(responses) do + target.add(response) + end + else + table.insert(responses,"Use the newtargets script-arg to add the results as targets") + end + return stdnse.format_output(true, responses) + end +end diff --git a/scripts/script.db b/scripts/script.db index 0ee110570..1d92b0427 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -39,6 +39,7 @@ Entry { filename = "broadcast-networker-discover.nse", categories = { "broadcast Entry { filename = "broadcast-novell-locate.nse", categories = { "broadcast", "safe", } } Entry { filename = "broadcast-pc-anywhere.nse", categories = { "broadcast", "safe", } } Entry { filename = "broadcast-pc-duo.nse", categories = { "broadcast", "safe", } } +Entry { filename = "broadcast-pim-discovery.nse", categories = { "broadcast", "discovery", "safe", } } Entry { filename = "broadcast-ping.nse", categories = { "broadcast", "discovery", "safe", } } Entry { filename = "broadcast-pppoe-discover.nse", categories = { "broadcast", "safe", } } Entry { filename = "broadcast-rip-discover.nse", categories = { "broadcast", "safe", } }