description = [[ Finds hostnames that resolve to the target's IP address by querying the online databases: * http://www.bfk.de/bfk_dnslogger.html * http://www.whataremyhosts.com ( Bing Search Results ) Optionally users can return results from a specific provider by using the argument hostmap.provider. The supported provider identifiers are: * BFK * BING The script is in the "external" category because it sends target IPs to a third party in order to query their database. ]] --- -- @args hostmap.prefix If set, saves the output for each host in a file -- called "". The file contains one entry per line. -- @args newtargets If set, add the new hostnames to the scanning queue. -- This the names presumably resolve to the same IP address as the -- original target, this is only useful for services such as HTTP that -- can change their behavior based on hostname. -- @args hostmap.provider If set, hostmap will only return results from -- the given provider. By default it uses all the providers available and -- merges the results. -- Supported providers: BFK, BING -- -- @usage -- nmap --script hostmap --script-args 'hostmap.prefix=hostmap-,hostmap.provider=BING' -- -- @output -- Host script results: -- | hostmap: Saved to hostmap-nmap.org -- | insecure.org -- | 74.207.254.18 -- | web.insecure.org -- | download.insecure.org -- | images.insecure.org -- | www.insecure.org -- | nmap.org -- | www.nmap.org -- | sectools.org -- | mirror.sectools.org -- | www.sectools.org -- |_seclists.org author = {'Ange Gutek', 'Paulino Calderon'} license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"external", "discovery", "intrusive"} require "dns" require "ipOps" require "http" require "stdnse" require "target" local HOSTMAP_BFK_SERVER = "www.bfk.de" local HOSTMAP_BING_SERVER = "www.whataremyhosts.com" local HOSTMAP_DEFAULT_PROVIDER = "ALL" local filename_escape, write_file hostrule = function(host) return not ipOps.isPrivate(host.ip) end local function query_bfk(ip) local query = "/bfk_dnslogger.html?query=" .. ip local response local hostnames = {} response = http.get(HOSTMAP_BFK_SERVER, 80, query) if not response.status then return string.format("Error: could not GET http://%s%s", HOSTMAP_BFK_SERVER, query) end for entry in string.gmatch(response.body, "#result\">([^<]-)") do if not hostnames[entry] then if target.ALLOW_NEW_TARGETS then local status, err = target.add(entry) end hostnames[entry] = true if string.match(entry, "%d+%.%d+%.%d+%.%d+") or dns.query(entry) then hostnames[#hostnames + 1] = entry else hostnames[#hostnames + 1] = entry .. " (cannot resolve)" end end end if #hostnames == 0 then if not string.find(response.body, "

The server returned no hits.

") then return "Error: found no hostnames but not the marker for \"no hostnames found\" (pattern error?)" end end return hostnames end local function query_bing(ip) local query = "/print.php?ip=" .. ip local response local entries response = http.get(HOSTMAP_BING_SERVER, 80, query) local hostnames = {} if not response.status then return string.format("Error: could not GET http://%s%s", HOSTMAP_BING_SERVER, query) end entries = stdnse.strsplit(",", response.body); for _, entry in pairs(entries) do if not hostnames[entry] and entry ~= "" then if target.ALLOW_NEW_TARGETS then local status, err = target.add(entry) end hostnames[#hostnames + 1] = entry end end if #hostnames == 0 then if not string.find(response.body, "no results") then return "Error: found no hostnames but not the marker for \"no hostnames found\" (pattern error?)" end end return hostnames end action = function(host) local filename_prefix = stdnse.get_script_args("hostmap.prefix") local provider = stdnse.get_script_args("hostmap.provider") or HOSTMAP_DEFAULT_PROVIDER local hostnames = {} local hostnames_str, output_str --select provider accordingly if provider == "BFK" then stdnse.print_debug(1, "Using database: %s", HOSTMAP_BFK_SERVER) hostnames = query_bfk(host.ip) elseif provider == "BING" then stdnse.print_debug(1, "Using database: %s", HOSTMAP_BING_SERVER) hostnames = query_bing(host.ip) else stdnse.print_debug(1, "Using all databases") local bing_hostnames = query_bing(host.ip) local bfk_hostnames = query_bfk(host.ip) local found --merge into same table local bing_hosts_type = type(bing_hostnames) local bfk_hosts_type = type(bfk_hostnames) --if one service does not respond, fail gracefully if bing_hosts_type == "table" and bfk_hosts_type == "table" then for k,bfk_host in pairs(bfk_hostnames) do found = false for _,bing_host in pairs(bing_hostnames) do if bfk_host == bing_host then found = true end end if found == false and bfk_host ~= true then table.insert(bing_hostnames, bfk_host) end end hostnames = bing_hostnames elseif bing_hosts_type == "table" and bfk_hosts_type ~= "table" then stdnse.print_debug(1, "BFK did not return results.") hostnames = bing_hostnames elseif bing_hosts_type ~= "table" and bfk_hosts_type == "table" then stdnse.print_debug(1, "BING did not return results.") hostnames = bfk_hostnames end end if type(hostnames) == "string" then return hostnames end hostnames_str = stdnse.strjoin("\n", hostnames) --write to file if filename_prefix then local filename = filename_prefix .. filename_escape(host.targetname or host.ip) local status, err = write_file(filename, hostnames_str .. "\n") if status then output_str = string.format("Saved to %s\n", filename) else output_str = string.format("Error saving to %s: %s\n", filename, err) end else output_str = "\n" end output_str = output_str .. stdnse.strjoin("\n", hostnames) return output_str end -- Escape some potentially unsafe characters in a string meant to be a filename. function filename_escape(s) return string.gsub(s, "[%z/=]", function(c) return string.format("=%02X", string.byte(c)) end) end function write_file(filename, contents) local f, err = io.open(filename, "w") if not f then return f, err end f:write(contents) f:close() return true end