From 9cf7f14afef8bc21913c876bb56b8fcec1fa16a6 Mon Sep 17 00:00:00 2001 From: fyodor Date: Fri, 1 Feb 2008 02:47:09 +0000 Subject: [PATCH] o Added NSE HTTP library which allows scripts to easily fetch URLs with http.get_url() or create more complex requests with http.request(). There is also an http.get() function which takes components (hostname, port, and path) rather than a URL. The HTTPAuth, robots, and showHTMLTitle NSE scripts have been updated to use this library. Sven Klemm wrote all of this code. --- CHANGELOG | 7 ++ scripts/HTTPAuth.nse | 143 ++++++++++---------------------------- scripts/robots.nse | 34 ++------- scripts/showHTMLTitle.nse | 37 +++------- 4 files changed, 57 insertions(+), 164 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e5facd5c9..8776ca2c7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,12 @@ # Nmap Changelog ($Id$); -*-text-*- +o Added NSE HTTP library which allows scripts to easily fetch URLs + with http.get_url() or create more complex requests with + http.request(). There is also an http.get() function which takes + components (hostname, port, and path) rather than a URL. The + HTTPAuth, robots, and showHTMLTitle NSE scripts have been updated to + use this library. Sven Klemm wrote all of this code. + o Nmap URL has changed from http://insecure.org/nmap/ to http://nmap.org to save everyone some typing. All the files from the former location are now available at the latter (e.g. download page diff --git a/scripts/HTTPAuth.nse b/scripts/HTTPAuth.nse index b6adfb5b1..ebd8f9ca6 100644 --- a/scripts/HTTPAuth.nse +++ b/scripts/HTTPAuth.nse @@ -14,121 +14,52 @@ license = "See nmaps COPYING for licence" categories = {"intrusive"} require "shortport" +require "http" -portrule = shortport.port_or_service({80, 8080}, "http") +portrule = shortport.port_or_service({80, 443, 8080}, {"http","https"}) action = function(host, port) - local socket - local catch = function() - socket:close() - end + local realm,scheme,result + local basic = false - local try = nmap.new_try(catch) + local answer = http.get( host, port, "/" ) - local get_http_headers = function(dst, dst_port, query_string) - socket = nmap.new_socket() + --- check for 401 response code + if answer.status == 401 then + result = "HTTP Service requires authentication\n" - try(socket:connect(dst, dst_port)) - try(socket:send(query_string)) + -- split www-authenticate header + local auth_headers = {} + local pcre = pcre.new('\\w+( (\\w+=("[^"]+"|\\w+), *)*(\\w+=("[^"]+"|\\w+)))?',0,"C") + local match = function( match ) table.insert(auth_headers, match) end + pcre:gmatch( answer.header['www-authenticate'], match ) - local response = "" - local lines - local status + for _, value in pairs( auth_headers ) do + result = result .. " Auth type: " + scheme, realm = string.match(value, "(%a+).-[Rr]ealm=\"(.-)\"") + if scheme == "Basic" then + basic = true + end + if realm ~= nil then + result = result .. scheme .. ", realm = " .. realm .. "\n" + else + result = result .. string.match(value, "(%a+)") .. "\n" + end + end + end - while true do - status, lines = socket:receive_lines(1) + if basic then + answer = http.get(host, port, '/', {header={Authorization="Basic YWRtaW46C"}}) + if answer.status ~= 401 and answer.status ~= 403 then + result = result .. " HTTP server may accept user=\"admin\" with blank password for Basic authentication\n" + end - if not status then - break - end + answer = http.get(host, port, '/', {header={Authorization="Basic YWRtaW46YWRtaW4"}}) + if answer.status ~= 401 and answer.status ~= 403 then + result = result .. " HTTP server may accept user=\"admin\" with password=\"admin\" for Basic authentication\n" + end + end - response = response .. lines - end - - try(socket:close()) - - local tags = {"(.-)" @@ -9,7 +10,7 @@ license = "See nmaps COPYING for licence" categories = {"safe"} runlevel = 1.0 -portrule = shortport.port_or_service(80, "http") +portrule = shortport.port_or_service({80,443}, {"http","https"}) local last_len = 0 -- split the output in 40 character lines @@ -32,40 +33,15 @@ local function buildOutput(output, w) end action = function(host, port) - local soc, lines, status + local answer = http.get( host, port, "/robots.txt" ) - local catch = function() soc:close() end - local try = nmap.new_try(catch) - - -- connect to webserver - soc = nmap.new_socket() - soc:set_timeout(4000) - try(soc:connect(host.ip, port.number)) - - local query = strbuf.new() - query = query .. "GET /robots.txt HTTP/1.1" - query = query .. "Accept: */*" - query = query .. "Accept-Language: en" - query = query .. "User-Agent: Nmap NSE" - query = query .. "Host: " .. host.ip .. ":" .. port.number - query = query .. "Connection: close" - query = query .. '\r\n\r\n'; - try(soc:send(strbuf.dump(query, '\r\n'))) - - local response = strbuf.new() - while true do - status, lines = soc:receive_lines(1) - if not status then break end - response = response .. lines - end - - if not string.find(strbuf.dump(response), "HTTP/1.1 200 OK") then + if answer.status ~= 200 then return nil end -- parse all disallowed entries and remove comments local output = strbuf.new() - for w in string.gmatch(strbuf.dump(response, '\n'), "Disallow:%s*([^\n]*)\n") do + for w in string.gmatch(answer.body, "Disallow:%s*([^\n]*)\n") do w = w:gsub("%s*#.*", "") buildOutput(output, w) end diff --git a/scripts/showHTMLTitle.nse b/scripts/showHTMLTitle.nse index b2ea51860..f37b028ed 100644 --- a/scripts/showHTMLTitle.nse +++ b/scripts/showHTMLTitle.nse @@ -11,7 +11,7 @@ license = "See nmaps COPYING for licence" categories = {"demo", "safe"} -require "stdnse" +require 'http' portrule = function(host, port) if not (port.service == 'http' or port.service == 'https') then @@ -26,41 +26,20 @@ portrule = function(host, port) end action = function(host, port) - local socket, request, result, status, s, title, protocol + local data, result, title, protocol - socket = nmap.new_socket() + data = http.get( host, port, '/' ) + result = data.body - if port.service == 'https' or port.version.service_tunnel == 'ssl' then - protocol = "ssl" - else - protocol = "tcp" - end - - socket:connect(host.ip, port.number, protocol ) - request = "GET / HTTP/1.0\r\n\r\n" - socket:send(request) - - result = "" - while true do - status, s = socket:receive_lines(1) - if not status then - break - end - - result = result .. s - end - socket:close() - -- watch out, this doesn't really work for all html tags - -- also string.lower consumes the / - result = string.gsub(result, "", function(c) return "<" .. string.lower(c) .. ">" end) - - title = string.match(result, "(.+)<title>") + result = string.gsub(result, "<(/?%a+)>", function(c) return "<" .. string.lower(c) .. ">" end) + + title = string.match(result, "<title>(.+)") if title ~= nil then result = string.gsub(title , "[\n\r\t]", "") if string.len(title) > 50 then - stdnse.print_debug("showHTMLTitle.nse: Title got truncated!"); + stdnse.print_debug("showHTMLTitle.nse: Title got truncated!"); result = string.sub(result, 1, 62) .. "..." end else