147 lines
4.1 KiB
Lua
147 lines
4.1 KiB
Lua
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
|