From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- scripts/url-snarf.nse | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 scripts/url-snarf.nse (limited to 'scripts/url-snarf.nse') diff --git a/scripts/url-snarf.nse b/scripts/url-snarf.nse new file mode 100644 index 0000000..473eb00 --- /dev/null +++ b/scripts/url-snarf.nse @@ -0,0 +1,146 @@ +local io = require "io" +local nmap = require "nmap" +local os = require "os" +local packet = require "packet" +local stdnse = require "stdnse" +local stringaux = require "stringaux" +local table = require "table" +local url = require "url" + +description=[[ +Sniffs an interface for HTTP traffic and dumps any URLs, and their +originating IP address. Script output differs from other script as +URLs are written to stdout directly. There is also an option to log +the results to file. + +The script can be limited in time by using the timeout argument or run until a +ctrl+break is issued, by setting the timeout to 0. +]] + +--- +-- @usage +-- nmap --script url-snarf -e +-- +-- @output +-- | url-snarf: +-- |_ Sniffed 169 URLs in 5 seconds +-- +-- @args url-snarf.timeout runs the script until the timeout is reached. +-- a timeout of 0s can be used to run until ctrl+break. (default: 30s) +-- @args url-snarf.nostdout doesn't write any output to stdout while running +-- @args url-snarf.outfile filename to which all discovered URLs are written +-- @args url-snarf.interface interface on which to sniff (overrides -e) +-- + +author = "Patrik Karlsson" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"safe"} + + +local arg_iface = nmap.get_interface() or stdnse.get_script_args(SCRIPT_NAME .. ".interface") + +prerule = function() + local has_interface = ( arg_iface ~= nil ) + if not nmap.is_privileged() then + stdnse.verbose1("not running for lack of privileges.") + return false + end + if ( not(has_interface) ) then + stdnse.verbose1("no network interface was supplied, aborting ...") + return false + end + return true +end + +-- we should probably leverage code from the http library, but those functions +-- are all declared local. +local function get_url(data) + + local headers, body = table.unpack(stringaux.strsplit("\r\n\r\n", data)) + if ( not(headers) ) then + return + end + headers = stringaux.strsplit("\r\n", headers) + if ( not(headers) or 1 > #headers ) then + return + end + local parsed = {} + parsed.path = headers[1]:match("^[^s%s]+ ([^%s]*) HTTP/1%.%d$") + if ( not(parsed.path) ) then + return + end + for _, v in ipairs(headers) do + parsed.host, parsed.port = v:match("^Host: (.*):?(%d?)$") + if ( parsed.host ) then + break + end + end + if ( not(parsed.host) ) then + return + end + parsed.port = ( #parsed.port ~= 0 ) and parsed.port or nil + parsed.scheme = "http" + local u = url.build(parsed) + if ( not(u) ) then + return + end + return u +end + +local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout")) +arg_timeout = arg_timeout or 30 +local arg_nostdout= stdnse.get_script_args(SCRIPT_NAME..".nostdout") +local arg_outfile = stdnse.get_script_args(SCRIPT_NAME..".outfile") + +local function log_entry(src_ip, url) + local outfd = io.open(arg_outfile, "a") + if ( outfd ) then + local entry = ("%s\t%s\r\n"):format(src_ip, url) + outfd:write(entry) + outfd:close() + end +end + +action = function() + local counter = 0 + + if ( arg_outfile ) then + local outfd = io.open(arg_outfile, "a") + if ( not(outfd) ) then + return ("\n ERROR: Failed to open outfile (%s)"):format(arg_outfile) + end + outfd:close() + end + + local socket = nmap.new_socket() + socket:set_timeout(1000) + socket:pcap_open(arg_iface, 1500, true, "tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)") + + local start, stop = os.time() + repeat + local status, len, _, l3 = socket:pcap_receive() + if ( status ) then + local p = packet.Packet:new( l3, #l3 ) + local pos = p.tcp_data_offset + 1 + local http_data = p.buf:sub(pos) + + local url = get_url(http_data) + if ( url ) then + counter = counter + 1 + if ( not(arg_nostdout) ) then + print(p.ip_src, url) + end + if ( arg_outfile ) then + log_entry(p.ip_src, url) + end + end + end + if ( arg_timeout and arg_timeout > 0 and arg_timeout <= os.time() - start ) then + stop = os.time() + break + end + until(false) + if ( counter > 0 ) then + return ("\n Sniffed %d URLs in %d seconds"):format(counter, stop - start) + end +end -- cgit v1.2.3