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-slowloris-check.nse | 164 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 scripts/http-slowloris-check.nse (limited to 'scripts/http-slowloris-check.nse') diff --git a/scripts/http-slowloris-check.nse b/scripts/http-slowloris-check.nse new file mode 100644 index 0000000..d8482aa --- /dev/null +++ b/scripts/http-slowloris-check.nse @@ -0,0 +1,164 @@ +local coroutine = require "coroutine" +local math = require "math" +local nmap = require "nmap" +local shortport = require "shortport" +local stdnse = require "stdnse" +local comm = require "comm" +local vulns = require "vulns" +local http = require "http" + + +description = [[ +Tests a web server for vulnerability to the Slowloris DoS attack without +actually launching a DoS attack. + +Slowloris was described at Defcon 17 by RSnake +(see http://ha.ckers.org/slowloris/). + +This script opens two connections to the server, each without the final CRLF. +After 10 seconds, second connection sends additional header. Both connections +then wait for server timeout. If second connection gets a timeout 10 or more +seconds after the first one, we can conclude that sending additional header +prolonged its timeout and that the server is vulnerable to slowloris DoS +attack. + +A "LIKELY VULNERABLE" result means a server is subject to timeout-extension +attack, but depending on the http server's architecture and resource limits, a +full denial-of-service is not always possible. Complete testing requires +triggering the actual DoS condition and measuring server responsiveness. + +You can specify custom http User-agent field with http.useragent +script argument. + +Idea from Qualys blogpost: +* https://community.qualys.com/blogs/securitylabs/2011/07/07/identifying-slow-http-attack-vulnerabilities-on-web-applications + +]] + +--- +-- @usage +-- nmap --script http-slowloris-check +-- +-- @output +-- PORT STATE SERVICE REASON +-- 80/tcp open http syn-ack +-- | http-slowloris-check: +-- | VULNERABLE: +-- | Slowloris DOS attack +-- | State: LIKELY VULNERABLE +-- | IDs: CVE:CVE-2007-6750 +-- | Slowloris tries to keep many connections to the target web server open and hold +-- | them open as long as possible. It accomplishes this by opening connections to +-- | the target web server and sending a partial request. By doing so, it starves +-- | the http server's resources causing Denial Of Service. +-- | +-- | Disclosure date: 2009-09-17 +-- | References: +-- | http://ha.ckers.org/slowloris/ +-- |_ http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-6750 +-- +-- @see http-slowloris.nse + +author = "Aleksandar Nikolic" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"vuln", "safe"} + + +portrule = shortport.http + +action = function(host,port) + + local slowloris = { + title = "Slowloris DOS attack", + description = [[ +Slowloris tries to keep many connections to the target web server open and hold +them open as long as possible. It accomplishes this by opening connections to +the target web server and sending a partial request. By doing so, it starves +the http server's resources causing Denial Of Service. +]], + IDS = { + CVE = 'CVE-2007-6750', + }, + references = { + 'http://ha.ckers.org/slowloris/', + }, + dates = { + disclosure = {year = '2009', month = '09', day = '17'}, + }, + exploit_results = {}, + } + + local report = vulns.Report:new(SCRIPT_NAME, host, port) + slowloris.state = vulns.STATE.NOT_VULN + + local sd, response, Bestopt = comm.tryssl(host, port, "GET / HTTP/1.0\r\n\r\n") -- first determine if we need ssl + if sd then sd:close() end + if Bestopt == "none" then + stdnse.debug1("Error determining SSL: %s", response) + return nil + end + local HalfHTTP = ( + "POST /" .. tostring(math.random(100000, 900000)) .. " HTTP/1.1\r\n" .. + "Host: " .. host.ip .. "\r\n" .. + "User-Agent: " .. http.USER_AGENT .. "\r\n" .. + "Content-Length: 42\r\n" + ) + local TimeWithout -- time without additional headers + + -- does a half http request and waits until timeout + local function slowThread1() + local socket = nmap.new_socket() + local try = nmap.new_try(function() + TimeWithout = nmap.clock() + socket:close() + end) + try(socket:connect(host, port, Bestopt)) + try(socket:send(HalfHTTP)) + socket:set_timeout(500 * 1000) + try(socket:receive()) + TimeWithout = nmap.clock() + end + + local TimeWith -- time with additional headers + + -- does a half http request but sends another + -- header value after 10 seconds + local function slowThread2() + local socket = nmap.new_socket() + local try = nmap.new_try(function() + TimeWith = nmap.clock() + socket:close() + end) + try(socket:connect(host, port, Bestopt)) + try(socket:send(HalfHTTP)) + stdnse.sleep(10) + try(socket:send("X-a: b\r\n")) + socket:set_timeout(500 * 1000) + try(socket:receive()) + TimeWith = nmap.clock() + end + + -- both threads run at the same time + local thread1 = stdnse.new_thread(slowThread1) + local thread2 = stdnse.new_thread(slowThread2) + while true do -- wait for both threads to die + if coroutine.status(thread1) == "dead" and coroutine.status(thread2) == "dead" then + break + end + stdnse.sleep(1) + end + -- compare times + if ( not(TimeWith) or not(TimeWithout) ) then + stdnse.debug1("Unable to time responses: thread died early.") + return nil + end + local diff = TimeWith - TimeWithout + stdnse.debug1("Time difference is: %.f",diff) + -- if second connection died 10 or more seconds after the first + -- it means that sending additional data prolonged the connection's time + -- and the server is vulnerable to slowloris attack + if diff >= 10 then + slowloris.state = vulns.STATE.LIKELY_VULN + end + return report:make_output(slowloris) +end -- cgit v1.2.3