diff --git a/nselib/http.lua b/nselib/http.lua index 920d4cc40..7b8282fec 100644 --- a/nselib/http.lua +++ b/nselib/http.lua @@ -1128,39 +1128,48 @@ end -- --@param host The host object. --@param port The port to use -- note that SSL will automatically be used, if necessary. ---@param result_404 [optional] The result when an unknown page is requested. This is returned by identify_404. --- If the 404 page returns a '200' code, then we disable HEAD requests. +--@param result_404 [optional] The result when an unknown page is requested. This is returned by +-- identify_404. If the 404 page returns a '200' code, then we +-- disable HEAD requests. +--@param path [optional] The path to request; by default, '/' is used. --@return A boolean value: true if HEAD is usable, false otherwise. -function can_use_head(host, port, result_404) +--@return If HEAD is usable, the result of the HEAD request is returned (so potentially, a script can +-- avoid an extra call to HEAD +function can_use_head(host, port, result_404, path) -- If the 404 result is 200, don't use HEAD. if(result_404 == 200) then return false end + -- Default path + if(path == nil) then + path = '/' + end + -- Perform a HEAD request and see what happens. - local data = http.head( host, port, '/' ) + local data = http.head( host, port, path ) if data then if data.status and data.status == 302 and data.header and data.header.location then - stdnse.print_debug(1, "http-enum.nse: Warning: Host returned 302 and not 200 when performing HEAD.") + stdnse.print_debug(1, "HTTP: Warning: Host returned 302 and not 200 when performing HEAD.") return false end if data.status and data.status == 200 and data.header then -- check that a body wasn't returned if string.len(data.body) > 0 then - stdnse.print_debug(1, "http-enum.nse: Warning: Host returned data when performing HEAD.") + stdnse.print_debug(1, "HTTP: Warning: Host returned data when performing HEAD.") return false end - stdnse.print_debug(1, "http-enum.nse: Host supports HEAD.") - return true + stdnse.print_debug(1, "HTTP: Host supports HEAD.") + return true, data end - stdnse.print_debug(1, "http-enum.nse: Didn't receive expected response to HEAD request (got %s).", get_status_string(data)) + stdnse.print_debug(1, "HTTP: Didn't receive expected response to HEAD request (got %s).", get_status_string(data)) return false end - stdnse.print_debug(1, "http-enum.nse: HEAD request completely failed.") + stdnse.print_debug(1, "HTTP: HEAD request completely failed.") return false end @@ -1278,17 +1287,17 @@ function identify_404(host, port) 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") + stdnse.print_debug(1, "HTTP: Failed while testing for 404 status code") return false, "Failed while testing for 404 error message" end if(data.status and data.status == 404) then - stdnse.print_debug(1, "http-enum.nse: Host returns proper 404 result.") + stdnse.print_debug(1, "HTTP: Host returns proper 404 result.") return true, 404 end if(data.status and data.status == 200) then - stdnse.print_debug(1, "http-enum.nse: Host returns 200 instead of 404.") + stdnse.print_debug(1, "HTTP: Host returns 200 instead of 404.") -- Clean up the body (for example, remove the URI). This makes it easier to validate later if(data.body) then @@ -1296,7 +1305,7 @@ function identify_404(host, port) 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") + stdnse.print_debug(1, "HTTP: Failed while testing for extra 404 error messages") return false, "Failed while testing for extra 404 error messages" end @@ -1305,7 +1314,7 @@ function identify_404(host, port) if(data2.status == nil) then data2.status = "" end - stdnse.print_debug(1, "http-enum.nse: HTTP 404 status changed for second request (became %d).", data2.status) + stdnse.print_debug(1, "HTTP: 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 @@ -1314,7 +1323,7 @@ function identify_404(host, port) 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) + stdnse.print_debug(1, "HTTP: 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 @@ -1323,28 +1332,28 @@ function identify_404(host, port) 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.") + stdnse.print_debug(1, "HTTP: Two known 404 pages returned valid and different pages; unable to identify valid response.") + stdnse.print_debug(1, "HTTP: 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.") + stdnse.print_debug(1, "HTTP: Two known 404 pages returned valid and different pages; unable to identify valid response (happened when checking a folder).") + stdnse.print_debug(1, "HTTP: 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 - stdnse.print_debug(1, "http-enum.nse: The 200 response didn't contain a body.") + stdnse.print_debug(1, "HTTP: The 200 response didn't contain a body.") return true, 200 end -- Loop through any expected error codes for _,code in pairs(bad_responses) do if(data.status and data.status == code) then - stdnse.print_debug(1, "http-enum.nse: Host returns %s instead of 404 File Not Found.", get_status_string(data)) + stdnse.print_debug(1, "HTTP: Host returns %s instead of 404 File Not Found.", get_status_string(data)) return true, code end end @@ -1376,17 +1385,17 @@ function page_exists(data, result_404, known_404, page, displayall) -- If the 404 response is also "200", deal with it (check if the body matches) if(string.len(data.body) == 0) then -- I observed one server that returned a blank string instead of an error, on some occasions - stdnse.print_debug(1, "http-enum.nse: Page returned a totally empty body; page likely doesn't exist") + stdnse.print_debug(1, "HTTP: Page returned a totally empty body; page likely doesn't exist") return false elseif(clean_404(data.body) ~= known_404) then - stdnse.print_debug(1, "http-enum.nse: Page returned a body that doesn't match known 404 body, therefore it exists (%s)", page) + stdnse.print_debug(1, "HTTP: Page returned a body that doesn't match known 404 body, therefore it exists (%s)", page) return true else return false end else -- If 404s return something other than 200, and we got a 200, we're good to go - stdnse.print_debug(1, "http-enum.nse: Page was '%s', it exists! (%s)", get_status_string(data), page) + stdnse.print_debug(1, "HTTP: Page was '%s', it exists! (%s)", get_status_string(data), page) return true end else @@ -1394,7 +1403,7 @@ function page_exists(data, result_404, known_404, page, displayall) if(data.status ~= 404 and data.status ~= result_404) then -- If this check succeeded, then the page isn't a standard 404 -- it could be a redirect, authentication request, etc. Unless the user -- asks for everything (with a script argument), only display 401 Authentication Required here. - stdnse.print_debug(1, "http-enum.nse: Page didn't match the 404 response (%s) (%s)", get_status_string(data), page) + stdnse.print_debug(1, "HTTP: Page didn't match the 404 response (%s) (%s)", get_status_string(data), page) if(data.status == 401) then -- "Authentication Required" return true @@ -1409,7 +1418,7 @@ function page_exists(data, result_404, known_404, page, displayall) end end else - stdnse.print_debug(1, "http-enum.nse: HTTP request failed (is the host still up?)") + stdnse.print_debug(1, "HTTP: HTTP request failed (is the host still up?)") return false end end diff --git a/scripts/http-headers.nse b/scripts/http-headers.nse index de6ea9167..ca7eab38a 100644 --- a/scripts/http-headers.nse +++ b/scripts/http-headers.nse @@ -17,7 +17,8 @@ Does a GET request for the root folder ("/"), and displays the HTTP headers retu -- | last-modified: Mon, 19 May 2008 04:49:49 GMT -- |_ server: Apache/2.2.2 (Fedora) -- --- +--@arg path The path to request, such as '/index.php'. Default: '/'. +--@arg useget Set to force GET requests instead of HEAD. author = "Ron Bowes " @@ -45,25 +46,45 @@ portrule = function(host, port) end action = function(host, port) - local result = http.get(host, port, "/") + local path = nmap.registry.args.path + local request_type = "HEAD" + if(path == nil) then + path = '/' + end + + local status = false + local result + + -- Check if the user didn't want HEAD to be used + if(nmap.registry.args.useget == nil) then + -- Try using HEAD first + status, result = http.can_use_head(host, port, path) + end + + -- If head failed, try using GET + if(status == false) then + stdnse.print_debug(1, "http-headers.nse: HEAD request failed, falling back to GET") + result = http.get(host, port, path) + request_type = "GET" + end if(result == nil) then if(nmap.debugging() > 0) then - return "ERROR: GET request failed" + return "ERROR: Header request failed" else return nil end end - if(result.header == nil) then + if(result.rawheader == nil) then if(nmap.debugging() > 0) then - return "ERROR: GET request didn't return a proper header" + return "ERROR: Header request didn't return a proper header" else return nil end end - local response = " \n" + local response = "(" .. request_type .. " used)\n" for _, header in pairs(result.rawheader) do response = response .. header .. "\n" end