diff --git a/nselib/shortport.lua b/nselib/shortport.lua index d3d48e1df..109de01cf 100644 --- a/nselib/shortport.lua +++ b/nselib/shortport.lua @@ -49,11 +49,25 @@ service = function(service, _proto, _state) end end -port_or_service = function(port, _service, proto, state) - local port_checker = portnumber(port, proto, state) - local service_checker = service(_service, proto, state) +port_or_service = function(port, _service, proto, _state) + local state = _state or {"open"} + local state_table + + if(type(state) == "string") then + state_table = {state} + elseif(type(state) == "table") then + state_table = state + end return function(host, port) - return port_checker(host, port) or service_checker(host, port) + for _, state in pairs(state_table) do + local port_checker = portnumber(port, proto, state) + local service_checker = service(_service, proto, state) + if (port_checker(host, port) or service_checker(host, port)) then + return true + end + end + + return false end end diff --git a/scripts/HTTP_open_proxy.nse b/scripts/HTTP_open_proxy.nse new file mode 100644 index 000000000..876237191 --- /dev/null +++ b/scripts/HTTP_open_proxy.nse @@ -0,0 +1,80 @@ +-- Arturo 'Buanzo' Busleiman / www.buanzo.com.ar / linux-consulting.buanzo.com.ar +-- See Nmap'ss COPYING file for licence details +-- This is version 20070413 aka "13th Friday" :) +-- Changelog: Added explode() function. Header-only matching now works. +-- * Fixed set_timeout +-- * Fixed some \r\n's + +id="Open Proxy Test" +description="Test if a discovered proxy is open to us by connecting to www.google.com and checking for the 'Server: GWS/' header response." +tags = {"intrusive"} + +-- I found a nice explode() function in lua-users' wiki. I had to fix it, though. +-- http://lua-users.org/wiki/LuaRecipes +function explode(d,p) + local t,ll,l + t={} + ll=0 + while true do + l=string.find(p,d,ll+1,true) -- find the next d in the string + if l~=nil then -- if "not not" found then.. + table.insert(t, string.sub(p,ll,l-1)) -- Save it in our array. + ll=l+1 -- save just after where we found it for searching next time. + else + table.insert(t, string.sub(p,ll)) -- Save what's left in our array. + break -- Break at end, as it should be, according to the lua manual. + end + end + return t +end + +portrule = function(host, port) + if (port.number == 3128 or port.number == 8080 or port.service == "http-proxy" or port.service == "squid-proxy" or port.service == "squid-proxy?") + and port.protocol == "tcp" + then + return true + else + return false + end +end + +action = function(host, port) + local socket = nmap.new_socket() + local result + local status = true + local response + local i +-- We will return this if we don't find "^Server: GWS" in response headers + local retval + + socket:set_timeout(10000); + socket:connect(host.ip, port.number, port.protocol) + +-- Ask proxy to open www.google.com + socket:send("GET http://www.google.com HTTP/1.0\r\nHost: www.google.com\r\n\r\n") + +-- read the response, if any + status, result = socket:receive_lines(1) + +-- Explode result into the response table + if (status == false) or (result == "TIMEOUT") then + else + response = explode("\n",result) + end + +-- Now, search for Server: GWS until headers (or table) end. + i = 0 + while true do + i = i+1 + if i > table.getn(response) then break end + if response[i]=="\r" then break end + if string.match(response[i],"^Server: GWS/") then + retval = "Potentially OPEN proxy. Check for Google\'s \"Server: GWS/\" header FOUND." + break + end + end + +-- close the socket and exit, returning the retval string. + socket:close() + return retval +end diff --git a/scripts/robots.nse b/scripts/robots.nse new file mode 100644 index 000000000..33ffc076f --- /dev/null +++ b/scripts/robots.nse @@ -0,0 +1,76 @@ +require('shortport') +require('strbuf') +require('listop') + +id = "robots.txt" +author = "Eddie Bell " +description = "Download a http servers robots.txt file and display all disallowed entries" +license = "See nmaps COPYING for licence" +categories = {"safe"} +runlevel = 1.0 + +portrule = shortport.port_or_service(80, "http") +local last_len = 0 + +-- split the output in 40 character lines +local function buildOutput(output, w) + local len = string.len(w) + + for i,v in ipairs(output) do + if w == v then return nil end + end + + if last_len == 0 or last_len + len <= 40 then + last_len = last_len + len + else + output = output .. '\n' + last_len = 0 + end + + output = output .. w + output = output .. ' ' +end + +action = function(host, port) + local soc, lines, status + + 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 .. '\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 + return nil + end + + -- parse all disallowed entries + local output = strbuf.new() + for w in string.gmatch(strbuf.dump(response, '\n'), "Disallow:%s*([^\n]*)\n") do + buildOutput(output, w) + end + + if not listop.is_empty(output) then + return strbuf.dump(output) + end + + return nil +end