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/http-vhosts.nse | 185 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 scripts/http-vhosts.nse (limited to 'scripts/http-vhosts.nse') diff --git a/scripts/http-vhosts.nse b/scripts/http-vhosts.nse new file mode 100644 index 0000000..c5827bc --- /dev/null +++ b/scripts/http-vhosts.nse @@ -0,0 +1,185 @@ +local coroutine = require "coroutine" +local http = require "http" +local io = require "io" +local nmap = require "nmap" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +local table = require "table" +local datafiles = require "datafiles" + +description = [[ +Searches for web virtual hostnames by making a large number of HEAD requests against http servers using common hostnames. + +Each HEAD request provides a different +Host header. The hostnames come from a built-in default +list. Shows the names that return a document. Also shows the location of +redirections. + +The domain can be given as the http-vhosts.domain argument or +deduced from the target's name. For example when scanning www.example.com, +various names of the form .example.com are tried. +]] + +--- +-- @usage +-- nmap --script http-vhosts -p 80,8080,443 +-- +-- @arg http-vhosts.domain The domain that hostnames will be prepended to, for +-- example example.com yields www.example.com, www2.example.com, +-- etc. If not provided, a guess is made based on the hostname. +-- @arg http-vhosts.path The path to try to retrieve. Default /. +-- @arg http-vhosts.collapse The limit to start collapsing results by status code. Default 20 +-- @arg http-vhosts.filelist file with the vhosts to try. Default nselib/data/vhosts-default.lst +-- +-- @output +-- PORT STATE SERVICE REASON +-- 80/tcp open http syn-ack +-- | http-vhosts: +-- | example.com: 301 -> http://www.example.com/ +-- | www.example.com: 200 +-- | docs.example.com: 302 -> https://www.example.com/docs/ +-- |_images.example.com: 200 +-- +-- @internal: see http://seclists.org/nmap-dev/2010/q4/401 and http://seclists.org/nmap-dev/2010/q4/445 +-- +-- +-- @todo feature: add option report and implement it +-- @internal after stripping sensitive info like ip, domain names, hostnames +-- and redirection targets from the result, append it to a file +-- that can then be uploaded. If enough info is gathered, the names +-- will be weighted. It can be shared with metasploit +-- +-- @todo feature: fill nsedoc +-- +-- @todo feature: register results for other scripts (external help needed) +-- +-- @todo feature: grow names list (external help needed) +-- + +author = "Carlos Pantelides" + +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" + +categories = { "discovery", "intrusive" } + +local arg_domain = stdnse.get_script_args(SCRIPT_NAME..".domain") +local arg_path = stdnse.get_script_args(SCRIPT_NAME..".path") or "/" +local arg_filelist = stdnse.get_script_args(SCRIPT_NAME..'.filelist') +local arg_collapse = tonumber(stdnse.get_script_args(SCRIPT_NAME..".collapse")) or 10 + +-- Defines domain to use, first from user and then from host +local defineDomain = function(host) + local name = stdnse.get_hostname(host) + if name and name ~= host.ip then + local pos = string.find (name, ".",1,true) + if not pos then return name end + return string.sub (name, pos + 1) + end +end + +--- +-- Makes a target name with a name and a domain +-- @param name string +-- @param domain string +-- @return string +local makeTargetName = function(name,domain) + if name and name ~= "" then + if domain and domain ~= "" then + return name .. "." .. domain + else + return name + end + elseif domain and domain ~= "" then + return domain + end +end + + +--- +-- Collapses a result +-- key -> table +-- @param result table +-- @return string +local collapse = function(result) + local collapsed = {""} + for code, group in next, result do + if #group > arg_collapse then + table.insert(collapsed, ("%d names had status %s"):format(#group, code)) + else + for _,name in ipairs(group) do + table.insert(collapsed, name) + end + end + end + return table.concat(collapsed,"\n") +end + +local testThread = function(result, host, port, name) + local condvar = nmap.condvar(result) + local targetname = makeTargetName(name , arg_domain) + if targetname ~= nil then + local http_response = http.generic_request(host, port, "HEAD", arg_path, {header={Host=targetname}}) + + if not http_response.status then + result["ERROR"] = result["ERROR"] or {} + table.insert(result["ERROR"], targetname) + else + local status = tostring(http_response.status) + result[status] = result[status] or {} + if ( 300 <= http_response.status and http_response.status < 400 ) then + table.insert(result[status], ("%s : %s -> %s"):format(targetname, status, (http_response.header.location or "(no Location provided)"))) + else + table.insert(result[status], ("%s : %s"):format(targetname, status)) + end + end + end + condvar "signal" +end + +local readFromFile = function(filename) + local database = {} + for l in io.lines(filename) do + table.insert(database, l) + end + return database +end + +portrule = shortport.http + +--- +-- Script action +-- @param host table +-- @param port table +action = function(host, port) + local result, threads, hostnames = {}, {}, {} + local condvar = nmap.condvar(result) + local status + + if arg_filelist then + hostnames = readFromFile(arg_filelist) + else + status, hostnames = datafiles.parse_file("nselib/data/vhosts-default.lst" , {}) + if not status then + stdnse.debug1("Can not open file with vhosts file names list") + return + end + end + + arg_domain = arg_domain or defineDomain(host) + for _,name in ipairs(hostnames) do + local co = stdnse.new_thread(testThread, result, host, port, name) + threads[co] = true + end + + while(next(threads)) do + for t in pairs(threads) do + threads[t] = ( coroutine.status(t) ~= "dead" ) and true or nil + end + if ( next(threads) ) then + condvar "wait" + end + end + + return collapse(result) +end -- cgit v1.2.3