mirror of
https://github.com/nmap/nmap.git
synced 2025-12-25 08:59:01 +00:00
Update http-slowloris-check
Summary of changes: * Clarified LIKELY_VULN status, since actual DoS may not be possible (false positive) * Made worker threads closures to simplify/fix testing multiple servers at once. * Added debug statements at script exit locations to clarify status when script terminates early. * Added CVE reference.
This commit is contained in:
@@ -21,6 +21,11 @@ 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 <code>http.useragent</code>
|
||||
script argument.
|
||||
|
||||
@@ -41,15 +46,17 @@ Idea from Qualys blogpost:
|
||||
-- | http-slowloris-check:
|
||||
-- | VULNERABLE:
|
||||
-- | Slowloris DOS attack
|
||||
-- | State: VULNERABLE
|
||||
-- | 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.
|
||||
-- | 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://ha.ckers.org/slowloris/
|
||||
-- |_ http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-6750
|
||||
|
||||
author = "Aleksandar Nikolic"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
@@ -58,63 +65,19 @@ categories = {"vuln", "safe"}
|
||||
|
||||
portrule = shortport.http
|
||||
|
||||
local HalfHTTP
|
||||
local Bestopt
|
||||
local TimeWithout -- time without additional headers
|
||||
local TimeWith -- time with additional headers
|
||||
|
||||
-- does a half http request and waits until timeout
|
||||
local function slowThread1(host,port)
|
||||
-- if no response was received when determining SSL
|
||||
if ( Bestopt == "none" ) then
|
||||
return
|
||||
end
|
||||
local socket,status
|
||||
local catch = function()
|
||||
TimeWithout = nmap.clock()
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
socket = nmap.new_socket()
|
||||
socket:set_timeout(500 * 1000)
|
||||
socket:connect(host.ip, port, Bestopt)
|
||||
socket:send(HalfHTTP)
|
||||
try(socket:receive())
|
||||
TimeWithout = nmap.clock()
|
||||
end
|
||||
|
||||
-- does a half http request but sends another
|
||||
-- header value after 10 seconds
|
||||
local function slowThread2(host,port)
|
||||
-- if no response was received when determining SSL
|
||||
if ( Bestopt == "none" ) then
|
||||
return
|
||||
end
|
||||
local socket,status
|
||||
local catch = function()
|
||||
-- note the time the socket timedout
|
||||
TimeWith = nmap.clock()
|
||||
stdnse.debug1("2 try")
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
socket = nmap.new_socket()
|
||||
socket:set_timeout(500 * 1000)
|
||||
socket:connect(host.ip, port, Bestopt)
|
||||
socket:send(HalfHTTP)
|
||||
stdnse.sleep(10)
|
||||
socket:send("X-a: b\r\n")
|
||||
try(socket:receive())
|
||||
TimeWith = nmap.clock()
|
||||
end
|
||||
|
||||
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.
|
||||
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/',
|
||||
},
|
||||
@@ -127,15 +90,55 @@ so, it starves the http server's resources causing Denial Of Service.
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
slowloris.state = vulns.STATE.NOT_VULN
|
||||
|
||||
local _
|
||||
_, _, Bestopt = comm.tryssl(host, port, "GET / \r\n\r\n", {}) -- first determine if we need ssl
|
||||
HalfHTTP = "POST /" .. tostring(math.random(100000, 900000)) .. " HTTP/1.1\r\n" ..
|
||||
local sd, response, Bestopt = comm.tryssl(host, port, "GET / \r\n\r\n") -- first determine if we need ssl
|
||||
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; " ..
|
||||
"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, host, port)
|
||||
local thread2 = stdnse.new_thread(slowThread2, host, port)
|
||||
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
|
||||
@@ -144,7 +147,8 @@ so, it starves the http server's resources causing Denial Of Service.
|
||||
end
|
||||
-- compare times
|
||||
if ( not(TimeWith) or not(TimeWithout) ) then
|
||||
return
|
||||
stdnse.debug1("Unable to time responses: thread died early.")
|
||||
return nil
|
||||
end
|
||||
local diff = TimeWith - TimeWithout
|
||||
stdnse.debug1("Time difference is: %d",diff)
|
||||
@@ -152,8 +156,7 @@ so, it starves the http server's resources causing Denial Of Service.
|
||||
-- it means that sending additional data prolonged the connection's time
|
||||
-- and the server is vulnerable to slowloris attack
|
||||
if diff >= 10 then
|
||||
stdnse.debug1("Difference is greater or equal to 10 seconds.")
|
||||
slowloris.state = vulns.STATE.VULN
|
||||
slowloris.state = vulns.STATE.LIKELY_VULN
|
||||
end
|
||||
return report:make_output(slowloris)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user