local ipOps = require "ipOps" local nmap = require "nmap" local packet = require "packet" local stdnse = require "stdnse" local string = require "string" local table = require "table" local target = require "target" -- -*- mode: lua -*-: -- vim: set filetype=lua : description = [[ Sniffs the local network for a configurable amount of time (10 seconds by default) and prints discovered addresses. If the newtargets script argument is set, discovered addresses are added to the scan queue. Requires root privileges. Either the targets-sniffer.interface script argument or -e Nmap option to define which interface to use. ]] --- -- @usage -- nmap -sL --script=targets-sniffer --script-args=newtargets,targets-sniffer.timeout=5s,targets-sniffer.interface=eth0 -- @args targets-sniffer.timeout The amount of time to listen for packets. Default 10s. -- @args targets-sniffer.interface The interface to use for sniffing. -- @output -- Pre-scan script results: -- | targets-sniffer: -- | 192.168.0.1 -- | 192.168.0.3 -- | 192.168.0.35 -- |_192.168.0.100 -- Thanks to everyone for the feedback and especially Henri Doreau for his detailed feedback and suggestions author = "Nick Nikolaou" categories = {"broadcast", "discovery", "safe"} license = "Same as Nmap--See https://nmap.org/book/man-legal.html" local interface_info local all_addresses= {} local unique_addresses = {} --Make sure the IP is not a broadcast or the local address local function check_if_valid(address) local broadcast = interface_info.broadcast local local_address = interface_info.address if address == local_address or address == broadcast or address == "255.255.255.255" or address:match('^ff') --IPv6 Multicast addrs then return false else return true end end -- Returns an array of address strings. local function get_ip_addresses(layer3) local ip = packet.Packet:new(layer3, layer3:len()) return { ipOps.str_to_ip(ip.ip_bin_src), ipOps.str_to_ip(ip.ip_bin_dst) } end prerule = function() return nmap.is_privileged() end local function collect_interface(if_table) if not interface_info and if_table.up == "up" and if_table.link ~= "loopback" then interface_info = if_table end end action = function() local sock = nmap.new_socket() local packet_counter = 0 local ip_counter = 0 local timeout = stdnse.parse_timespec(stdnse.get_script_args("targets-sniffer.timeout")) timeout = (timeout or 10) * 1000 -- TODO: sniff on all interfaces -- NOTE: targets-sniffer.iface script-arg name is non-standard, but left for compatibility. local interface = stdnse.get_script_args("targets-sniffer.iface") if interface then interface_info = nmap.get_interface_info(interface) else stdnse.get_script_interfaces(collect_interface) end if interface_info==nil then -- Check if we have the interface information stdnse.debug1("Error: Unable to get interface info. Did you specify the correct interface using 'targets-sniffer.interface=' or '-e '?") return end if sock==nil then stdnse.debug1("Error - unable to open socket using interface %s",interface) return else sock:pcap_open(interface, 104, true, "ip or ip6") stdnse.debug1("Will sniff for %s seconds on interface %s.", (timeout/1000),interface) repeat local start_time = nmap.clock_ms() -- Used for script timeout sock:set_timeout(timeout) local status, _, _, layer3 = sock:pcap_receive() if status then local addresses packet_counter=packet_counter+1 addresses = get_ip_addresses(layer3) stdnse.debug1("Got IP addresses %s", table.concat(addresses, " ")) for _, addr in ipairs(addresses) do if check_if_valid(addr) == true then if not unique_addresses[addr] then unique_addresses[addr] = true table.insert(all_addresses, addr) end end end end -- Update timeout timeout = timeout - (nmap.clock_ms() - start_time) until timeout <= 0 sock:pcap_close() end if target.ALLOW_NEW_TARGETS == true then if nmap.address_family() == 'inet6' then for _,v in pairs(all_addresses) do if v:match(':') then target.add(v) end end else for _,v in pairs(all_addresses) do if not v:match(':') then target.add(v) end end end else stdnse.debug1("Not adding targets to newtargets. If you want to do that use the 'newtargets' script argument.") end if #all_addresses>0 then stdnse.debug1("Added %s address(es) to newtargets", #all_addresses) end return string.format("Sniffed %s address(es). \n", #all_addresses) .. table.concat(all_addresses, "\n") end