Nmap Development mailing list archives
Re: [NSE] NSE HTTP library
From: Sven Klemm <sven () c3d2 de>
Date: Fri, 18 Jan 2008 22:54:55 +0100
Sven Klemm wrote:
Ah I found the bug. I forgot to declare the socket variable as local. Now everything works fine. I've attached the fixed version.
In the last version the newline fix got lost so this version adds it back in. I've also attached a patch for HTTPAuth.nse, showHTMLTitle.nse and robots.nse to make use of the library. Cheers, Sven -- Sven Klemm http://cthulhu.c3d2.de/~sven/
-- See nmaps COPYING for licence module(...,package.seeall) require 'stdnse' require 'url' -- -- http.get( host, port, path ) -- http.request( host, port, request ) -- http.get_url( url ) -- -- host may either be a string or table -- port may either be a number or a table -- -- the format of the return value is a table with the following structure: -- {status = 200, header = {}, body ="<html>...</html>"} -- the header table has an entry for each received header with the header name being the key -- the table also has an entry named "status" which contains the http status code of the request -- in case of an error status is nil -- fetch relative URL with get request get = function( host, port, path ) local hostname = host if type(host) == 'table' then hostname = ( host.name ~= '' and host.name ) or host.ip end local data data = "GET "..path.." HTTP/1.1\r\n" data = data .. "Host: "..hostname.."\r\n" data = data .. "User-Agent: Nmap NSE\r\n" data = data .. "Connection: close\r\n\r\n" return request( host, port, data ) end -- fetch URL with get request get_url = function( u ) local parsed = url.parse( u ) local port = {} port.service = parsed.scheme port.number = parsed.port if not port.number then if parsed.scheme == 'https' then port.number = 443 else port.number = 80 end end local path = parsed.path or "/" if parsed.query then path = path .. "?" .. parsed.query end return get( parsed.host, port, path ) end -- send http request and return the result as table -- host may be a table or the hostname -- port may be a table or the portnumber request = function( host, port, data ) if type(host) == 'table' then host = ( host.name ~= '' and host.name ) or host.ip end local protocol = 'tcp' if type(port) == 'table' then if nmap.have_ssl() and ( port.service == 'https' or ( port.version and port.version.service_tunnel == 'ssl' ) ) then protocol = 'ssl' end port = port.number end local socket = nmap.new_socket() socket:connect( host, port, protocol ) socket:send( data ) local buffer = stdnse.make_buffer( socket, "\r?\n" ) local status, line, result, _ local header, body = {}, {} -- header loop while true do status, line = buffer() if (not status or line == "") then break end table.insert(header,line) end result = {status=nil,header={},body=""} -- build nicer table for header for key, value in pairs( header ) do if key == 1 then local code _, _, code = string.find( value, "HTTP/%d\.%d (%d+)") result.status = tonumber(code) else _, _, key, value = string.find( value, "(.+): (.*)" ) if key and value then value = value:gsub( '[\r\n]+', '' ) if result.header[key] then result.header[key] = result.header[key] .. ',' .. value else result.header[key] = value end end end end -- body loop while true do status, line = buffer() if (not status) then break end table.insert(body,line) end socket:close() result.body = table.concat( body, "\n" ) return result end
Index: scripts/HTTPAuth.nse =================================================================== --- scripts/HTTPAuth.nse (revision 6740) +++ scripts/HTTPAuth.nse (working copy) @@ -14,74 +14,26 @@ 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 try = nmap.new_try(catch) - - local get_http_headers = function(dst, dst_port, query_string) - socket = nmap.new_socket() - - try(socket:connect(dst, dst_port)) - try(socket:send(query_string)) - - local response = "" - local lines - local status - - while true do - status, lines = socket:receive_lines(1) - - if not status then - break - end - - response = response .. lines - end - - try(socket:close()) - - local tags = {"(.-)<![Dd][Oo][Cc][Tt][Yy][Pp][Ee]", "(.-)<[Hh][Tt][Mm][Ll]", "(.-)<[Hh][Ee][Aa][Dd]", "(.-)<[Bb][Oo][Dd][Yy]"} - local hdrs - - for I = 1, #tags do - hdrs = string.match(response, tags[I]) - if hdrs ~= nil and hdrs ~= response and hdrs ~= "" then - return hdrs - end - end - - return response - end - - local auth - local value local realm local scheme local result local basic = false + local query - local query = "GET / HTTP/1.1\r\n" - query = query .. "Accept: */*\r\n" - query = query .. "Accept-Language: en\r\n" - query = query .. "User-Agent: Nmap NSE\r\n" - query = query .. "Connection: close\r\n" - query = query .. "Host: " .. host.ip .. ":" .. port.number .. "\r\n\r\n" + local answer = http.get( host, port, "/" ) - local headers = get_http_headers(host.ip, port.number, query) - --- check for 401 response code - auth = string.match(headers, "HTTP/1.- 401") - if auth ~= nil then + if answer.status == 401 then result = "HTTP Service requires authentication\n" -- loop through any WWW-Authenticate: headers to determine valid authentication schemes - for value in string.gmatch(headers, "[Aa]uthenticate:(.-)\n") do + local value = answer.header['WWW-Authenticate'] + if value then result = result .. " Auth type: " scheme, realm = string.match(value, "(%a+).-[Rr]ealm=\"(.-)\"") if scheme == "Basic" then @@ -104,11 +56,8 @@ query = query .. "Connection: close\r\n" query = query .. "Host: " .. host.ip .. ":" .. port.number .. "\r\n\r\n" - auth = "" - headers = get_http_headers(host.ip, port.number, query) - - auth = string.match(headers, "HTTP/1.- 40[013]") - if auth == nil then + answer = http.request(host, port, query) + 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 @@ -120,11 +69,8 @@ query = query .. "Connection: close\r\n" query = query .. "Host: " .. host.ip .. ":" .. port.number .. "\r\n\r\n" - auth = "" - headers = get_http_headers(host.ip, port.number, query) - - auth = string.match(headers, "HTTP/1.- 40[013]") - if auth == nil then + answer = http.request(host, port, query) + 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 Index: scripts/showHTMLTitle.nse =================================================================== --- scripts/showHTMLTitle.nse (revision 6740) +++ scripts/showHTMLTitle.nse (working copy) @@ -11,7 +11,7 @@ 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 @@ 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, "</?(%a+)>", function(c) return "<" .. string.lower(c) .. ">" end) - - title = string.match(result, "<title>(.+)<title>") + result = string.gsub(result, "<(/?%a+)>", function(c) return "<" .. string.lower(c) .. ">" end) + title = string.match(result, "<title>(.+)</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 Index: scripts/robots.nse =================================================================== --- scripts/robots.nse (revision 6740) +++ scripts/robots.nse (working copy) @@ -1,6 +1,7 @@ require('shortport') require('strbuf') require('listop') +require('http') id = "robots.txt" author = "Eddie Bell <ejlbell () gmail com>" @@ -9,7 +10,7 @@ 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 @@ 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
Attachment:
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Sent through the nmap-dev mailing list http://cgi.insecure.org/mailman/listinfo/nmap-dev Archived at http://SecLists.Org
Current thread:
- [NSE] NSE HTTP library Sven Klemm (Jan 16)
- Re: [NSE] NSE HTTP library Kris Katterjohn (Jan 16)
- Re: [NSE] NSE HTTP library Sven Klemm (Jan 17)
- Re: [NSE] NSE HTTP library Sven Klemm (Jan 18)
- Re: [NSE] NSE HTTP library Sven Klemm (Jan 18)
- RE: [NSE] NSE HTTP library Thomas Buchanan (Jan 18)
- Re: [NSE] NSE HTTP library Sven Klemm (Jan 18)
- Re: [NSE] NSE HTTP library Thomas Buchanan (Jan 19)
- RE: [NSE] NSE HTTP library Thomas Buchanan (Jan 19)
- Re: [NSE] NSE HTTP library Sven Klemm (Jan 20)
- Re: [NSE] NSE HTTP library Fyodor (Jan 31)
- Re: [NSE] NSE HTTP library Sven Klemm (Jan 31)
- RE: [NSE] NSE HTTP library Thomas Buchanan (Jan 31)
- Re: [NSE] NSE HTTP library Kris Katterjohn (Jan 31)
- Re: [NSE] NSE HTTP library Sven Klemm (Jan 31)
- Re: [NSE] NSE HTTP library Sven Klemm (Jan 17)
- Re: [NSE] NSE HTTP library Kris Katterjohn (Jan 16)