summaryrefslogtreecommitdiffstats
path: root/scripts/url-snarf.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/url-snarf.nse')
-rw-r--r--scripts/url-snarf.nse146
1 files changed, 146 insertions, 0 deletions
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 <interface>
+--
+-- @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 <code>-e</code>)
+--
+
+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