diff --git a/nselib/http.lua b/nselib/http.lua
index 5fdfa5028..cf1f0b5b2 100644
--- a/nselib/http.lua
+++ b/nselib/http.lua
@@ -35,9 +35,6 @@ local comm = require 'comm'
---Use ssl if we have it
local have_ssl = (nmap.have_ssl() and pcall(require, "openssl"))
--- The 404 used for URL checks
-local URL_404 = '/Nmap404Check' .. os.time(os.date('*t'))
-
-- Recursively copy a table.
-- Only recurs when a value is a table, other values are copied by assignment.
local function tcopy (t)
@@ -1260,10 +1257,13 @@ end
-- scan. If the server responds with a 404 status code, as it is supposed to, then this function simply returns 404. If it
-- contains one of a series of common status codes, including unauthorized, moved, and others, it is returned like a 404.
--
--- If, however, the 404 page returns a 200 status code, it gets interesting. First, it attempts to clean the returned
--- body (see clean_404 for details). Once any dynamic-looking data has been removed from the string, another
--- 404 page is requested. If the response isn't identical to the first 404 page, an error is returned. The reason is,
--- obviously, because we now have no way to tell a valid page from an invalid one.
+-- I (Ron Bowes) have observed one host that responds differently for three scenarios:
+-- * A non-existent page, all lowercase (a login page)
+-- * A non-existent page, with uppercase (a weird error page that says, "Filesystem is corrupt.")
+-- * A page in a non-existent directory (a login page with different font colours)
+--
+-- As a result, I've devised three different 404 tests, one to check each of these conditions. They all have to match,
+-- the tests can proceed; if any of them are different, we can't check 404s properly.
--
--@param host The host object.
--@param port The port to which we are establishing the connection.
@@ -1273,7 +1273,12 @@ function identify_404(host, port)
local data
local bad_responses = { 301, 302, 401, 403, 499, 501 }
- data = http.get(host, port, URL_404)
+ -- The URLs used to check 404s
+ local URL_404_1 = '/nmaplowercheck' .. os.time(os.date('*t'))
+ local URL_404_2 = '/NmapUpperCheck' .. os.time(os.date('*t'))
+ local URL_404_3 = '/Nmap/folder/check' .. os.time(os.date('*t'))
+
+ data = http.get(host, port, URL_404_1)
if(data == nil) then
stdnse.print_debug(1, "http-enum.nse: Failed while testing for 404 status code")
@@ -1290,11 +1295,12 @@ function identify_404(host, port)
-- Clean up the body (for example, remove the URI). This makes it easier to validate later
if(data.body) then
- -- Obtain another 404, with a different URI, to make sure things are consistent -- if they aren't, there's little hope
- local data2 = http.get(host, port, URL_404 .. "-2")
- if(data2 == nil) then
- stdnse.print_debug(1, "http-enum.nse: Failed while testing for second 404 error message")
- return false, "Failed while testing for second 404 error message"
+ -- Obtain a couple more 404 pages to test different conditions
+ local data2 = http.get(host, port, URL_404_2)
+ local data3 = http.get(host, port, URL_404_3)
+ if(data2 == nil or data3 == nil) then
+ stdnse.print_debug(1, "http-enum.nse: Failed while testing for extra 404 error messages")
+ return false, "Failed while testing for extra 404 error messages"
end
-- Check if the return code became something other than 200
@@ -1302,19 +1308,35 @@ function identify_404(host, port)
if(data2.status == nil) then
data2.status = ""
end
- stdnse.print_debug(1, "http-enum.nse: HTTP 404 status changed during request (become %d; server is acting very strange).", data2.status)
- return false, string.format("HTTP 404 status changed during request (became %d; server is acting very strange).", data2.status)
+ stdnse.print_debug(1, "http-enum.nse: HTTP 404 status changed for second request (became %d).", data2.status)
+ return false, string.format("HTTP 404 status changed for second request (became %d).", data2.status)
end
- -- Check if the returned body (once cleaned up) matches the first returned body
+ -- Check if the return code became something other than 200
+ if(data3.status ~= 200) then
+ if(data3.status == nil) then
+ data3.status = ""
+ end
+ stdnse.print_debug(1, "http-enum.nse: HTTP 404 status changed for third request (became %d).", data3.status)
+ return false, string.format("HTTP 404 status changed for third request (became %d).", data3.status)
+ end
+
+ -- Check if the returned bodies (once cleaned up) matches the first returned body
local clean_body = clean_404(data.body)
local clean_body2 = clean_404(data2.body)
+ local clean_body3 = clean_404(data3.body)
if(clean_body ~= clean_body2) then
stdnse.print_debug(1, "http-enum.nse: Two known 404 pages returned valid and different pages; unable to identify valid response.")
stdnse.print_debug(1, "http-enum.nse: If you investigate the server and it's possible to clean up the pages, please post to nmap-dev mailing list.")
return false, string.format("Two known 404 pages returned valid and different pages; unable to identify valid response.")
end
+ if(clean_body ~= clean_body3) then
+ stdnse.print_debug(1, "http-enum.nse: Two known 404 pages returned valid and different pages; unable to identify valid response (happened when checking a folder).")
+ stdnse.print_debug(1, "http-enum.nse: If you investigate the server and it's possible to clean up the pages, please post to nmap-dev mailing list.")
+ return false, string.format("Two known 404 pages returned valid and different pages; unable to identify valid response (happened when checking a folder).")
+ end
+
return true, 200, clean_body
end