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-chrono.nse | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 scripts/http-chrono.nse (limited to 'scripts/http-chrono.nse') diff --git a/scripts/http-chrono.nse b/scripts/http-chrono.nse new file mode 100644 index 0000000..897ae1e --- /dev/null +++ b/scripts/http-chrono.nse @@ -0,0 +1,136 @@ +local http = require "http" +local httpspider = require "httpspider" +local math = require "math" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +local tab = require "tab" +local table = require "table" +local url = require "url" + +description = [[ +Measures the time a website takes to deliver a web page and returns +the maximum, minimum and average time it took to fetch a page. + +Web pages that take longer time to load could be abused by attackers in DoS or +DDoS attacks due to the fact that they are likely to consume more resources on +the target server. This script could help identifying these web pages. +]] + +--- +-- @usage +-- nmap --script http-chrono +-- +-- @output +-- PORT STATE SERVICE +-- 80/tcp open http +-- |_http-chrono: Request times for /; avg: 2.98ms; min: 2.63ms; max: 3.62ms +-- +-- PORT STATE SERVICE +-- 80/tcp open http +-- | http-chrono: +-- | page avg min max +-- | /admin/ 1.91ms 1.65ms 2.05ms +-- | /manager/status 2.14ms 2.03ms 2.24ms +-- | /manager/html 2.26ms 2.09ms 2.53ms +-- | /examples/servlets/ 2.43ms 1.97ms 3.62ms +-- | /examples/jsp/snp/snoop.jsp 2.75ms 2.59ms 3.13ms +-- | / 2.78ms 2.54ms 3.36ms +-- | /docs/ 3.14ms 2.61ms 3.53ms +-- | /RELEASE-NOTES.txt 3.70ms 2.97ms 5.58ms +-- | /examples/jsp/ 4.93ms 3.39ms 8.30ms +-- |_/docs/changelog.html 10.76ms 10.14ms 11.46ms +-- +-- @args http-chrono.maxdepth the maximum amount of directories beneath +-- the initial url to spider. A negative value disables the limit. +-- (default: 3) +-- @args http-chrono.maxpagecount the maximum amount of pages to visit. +-- A negative value disables the limit (default: 1) +-- @args http-chrono.url the url to start spidering. This is a URL +-- relative to the scanned host eg. /default.html (default: /) +-- @args http-chrono.withinhost only spider URLs within the same host. +-- (default: true) +-- @args http-chrono.withindomain only spider URLs within the same +-- domain. This widens the scope from withinhost and can +-- not be used in combination. (default: false) +-- @args http-chrono.tries the number of times to fetch a page based on which +-- max, min and average calculations are performed. + + +author = "Ange Gutek" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"discovery", "intrusive"} + + +portrule = shortport.http + +action = function(host, port) + + local maxpages = stdnse.get_script_args(SCRIPT_NAME .. ".maxpagecount") or 1 + local tries = stdnse.get_script_args(SCRIPT_NAME .. ".tries") or 5 + + local dump = {} + local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME, maxpagecount = tonumber(maxpages) } ) + crawler:set_timeout(10000) + + -- launch the crawler + while(true) do + local start = stdnse.clock_ms() + local status, r = crawler:crawl() + if ( not(status) ) then + break + end + local chrono = stdnse.clock_ms() - start + dump[chrono] = tostring(r.url) + end + + -- retest each page x times to find an average speed + -- a significant diff between instant and average may be an evidence of some weakness + -- either on the webserver or its database + local average,count,page_test + local results = {} + for result, page in pairs (dump) do + local url_host, url_page = page:match("//(.-)/(.*)") + url_host = string.gsub(url_host,":%d*","") + + local min, max, page_test + local bulk_start = stdnse.clock_ms() + for i = 1,tries do + local start = stdnse.clock_ms() + if ( url_page:match("%?") ) then + page_test = http.get(url_host,port,"/"..url_page.."&test="..math.random(100), { no_cache = true }) + else + page_test = http.get(url_host,port,"/"..url_page.."?test="..math.random(100), { no_cache = true }) + end + local count = stdnse.clock_ms() - start + if ( not(max) or max < count ) then + max = count + end + if ( not(min) or min > count ) then + min = count + end + end + + local count = stdnse.clock_ms() - bulk_start + table.insert(results, { min = min, max = max, avg = (count / tries), page = url.parse(page).path }) + end + + local output + if ( #results > 1 ) then + table.sort(results, function(a, b) return a.avg < b.avg end) + output = tab.new(4) + tab.addrow(output, "page", "avg", "min", "max") + for _, entry in ipairs(results) do + tab.addrow(output, entry.page, ("%.2fms"):format(entry.avg), ("%.2fms"):format(entry.min), ("%.2fms"):format(entry.max)) + end + output = "\n" .. tab.dump(output) + else + local entry = results[1] + output = ("Request times for %s; avg: %.2fms; min: %.2fms; max: %.2fms"):format(entry.page, entry.avg, entry.min, entry.max) + end + return output +end + + + + -- cgit v1.2.3