diff --git a/scripts/http-open-proxy.nse b/scripts/http-open-proxy.nse index 204fd251d..092be95e9 100644 --- a/scripts/http-open-proxy.nse +++ b/scripts/http-open-proxy.nse @@ -11,8 +11,8 @@ web page from www.google.com. ]] --- --- @args openproxy.url Url that will be requested to the proxy --- @args openproxy.pattern Pattern that will be searched inside the request results +-- @args proxy.url Url that will be requested to the proxy +-- @args proxy.pattern Pattern that will be searched inside the request results -- @output -- Interesting ports on scanme.nmap.org (64.13.134.52): -- PORT STATE SERVICE @@ -35,8 +35,7 @@ web page from www.google.com. -- -- @usage -- nmap --script http-open-proxy.nse \ --- --script-args 'openproxy={url=,pattern=}' - +-- --script-args proxy.url=,proxy.pattern= author = "Arturo 'Buanzo' Busleiman " license = "Same as Nmap--See http://nmap.org/book/man-legal.html" @@ -45,112 +44,179 @@ require "comm" require "shortport" require "stdnse" require "url" +require "proxy" ---- check function, makes checkings for all valid returned status ---- If any of the HTTP status below is found, the proxy is potentially open ---@param result connection result ---@return true if any of the status is found, otherwise false -function check_code(result) - if string.match(result:lower(),"^http/%d\.%d%s*200") then return true end - if string.match(result:lower(),"^http/%d\.%d%s*30[12]") then return true end - return false +--- Performs the custom test, with user's arguments +-- @param host The host table +-- @param port The port table +-- @param test_url The url te send the request +-- @param pattern The pattern to check for valid result +-- @return status (if any request was succeded +-- @return response String with supported methods +function custom_test(host, port, test_url, pattern) + local lstatus = false + local response = "" + -- if pattern is not used, result for test is code check result. + -- otherwise it is pattern check result. + + -- strip hostname + if not string.match(test_url, "^http://.*") then + test_url = "http://" .. test_url + stdnse.print_debug("URL missing scheme. URL concatenated to http://") + end + local url_table = url.parse(test_url) + local hostname = url_table.host + + local get_status = proxy.test_get(host, port, "http", test_url, hostname, pattern) + local head_status = proxy.test_head(host, port, "http", test_url, hostname, pattern) + local conn_status = proxy.test_connect(host, port, "http", hostname) + if get_status then + lstatus = true + response = response .. " GET" + end + if head_status then + lstatus = true + response = response .. " HEAD" + end + if conn_status then + lstatus = true + response = response .. " CONNECTION" + end + if lstatus then response = "Methods supported: " .. response end + return lstatus, response end ---- check pattern, searches a pattern inside a response with multiple lines ---@param result Connection result ---@param pattern The pattern to be searched ---@return true if pattern is found, otherwise false -function check_pattern(result, pattern) - local lines = stdnse.strsplit("\n", result) - local i = 1 - local n = table.getn(lines) - while true do - if i > n then return false end - if string.match(lines[i]:lower(),pattern) then return true end - i = i + 1 - end -end +--- Performs the default test +-- First: Default google request and checks for Server: gws +-- Seconde: Request to wikipedia.org and checks for wikimedia pattern +-- Third: Request to computerhistory.org and checks for museum pattern +-- +-- If any of the requests is succesful, the proxy is considered open +-- If all get requests return the same result, the user is alerted that +-- the proxy might be redirecting his requests (very common on wi-fi +-- connections at airports, cafes, etc.) +-- +-- @param host The host table +-- @param port The port table +-- @return status (if any request was succeded +-- @return response String with supported methods +function default_test(host, port) + local fstatus = false + local response = "" + local get_status, head_status, conn_status + local get_r1, get_r2, get_r3 + local get_cstatus, head_cstatus + local _ ---- check, decides what kind of check should be done on the response, ---- depending if a specific pattern is being used ---@param result Connection result ---@param pattern The pattern that should be checked (must be false, in case of ---code check) ---@return true, if the performed check returns true, otherwise false -function check(result, pattern) - if pattern - then return check_pattern(result, pattern) - else return check_code(result) - end -end + -- Start test n1 -> google.com + -- making requests + local test_url = "http://www.google.com" + local hostname = "www.google.com" + local pattern = "^server: gws" + get_status, get_r1, get_cstatus = proxy.test_get(host, port, "http", test_url, hostname, pattern) + head_status, _, head_cstatus = proxy.test_head(host, port, "http", test_url, hostname, pattern) + conn_status = proxy.test_connect(host, port, "http", hostname) + -- checking results + -- conn_status use a different flag (cstatus) + -- because test_connection does not use patterns, so it is unable to detect + -- cases where you receive a valid code, but the response does not match the + -- pattern. + -- if it was using the same flag, program could return without testing GET/HEAD + -- once more before returning + + if get_status then fstatus = true; response = response .. " GET" end + if head_status then fstatus = true; response = response .. " HEAD" end + if conn_status then cstatus = true; response = response .. " CONNECTION" end + + -- if proxy is open, return it! + if fstatus then return fstatus, "Methods supported: " .. response end + + -- if we receive a invalid response, but with a valid + -- response code, we should make a next attempt. + -- if we do not receive any valid status code, + -- there is no reason to keep testing... the proxy is probably not open + if not (get_cstatus or head_cstatus or conn_status) then return false, nil end + stdnse.print_debug("Test 1 - Google Web Server\nReceived valid status codes, but pattern does not match") + + test_url = "http://www.wikipedia.org" + hostname = "www.wikipedia.org" + pattern = "wikimedia" + get_status, get_r2, get_cstatus = proxy.test_get(host, port, "http", test_url, hostname, pattern) + head_status, _, head_cstatus = proxy.test_head(host, port, "http", test_url, hostname, pattern) + conn_status = proxy.test_connect(host, port, "http", hostname) + + if get_status then fstatus = true; response = response .. " GET" end + if head_status then fstatus = true; response = response .. " HEAD" end + if conn_status then + if not cstatus then response = response .. " CONNECTION" end + cstatus = true + end + + if fstatus then return fstatus, "Methods supported: " .. response end + + -- same valid code checking as above + if not (get_cstatus or head_cstatus or conn_status) then return false, nil end + stdnse.print_debug("Test 2 - Wikipedia.org\nReceived valid status codes, but pattern does not match") + + test_url = "http://www.computerhistory.org" + hostname = "www.computerhistory.org" + pattern = "museum" + get_status, get_r3, get_cstatus = proxy.test_get(host, port, "http", test_url, hostname, pattern) + conn_status = proxy.test_connect(host, port, "http", hostname) + + if get_status then fstatus = true; response = response .. " GET" end + if conn_status then + if not cstatus then response = response .. " CONNECTION" end + cstatus = true + end + + if fstatus then return fstatus, "Methods supported:" .. response end + if not get_cstatus then + stdnse.print_debug("Test 3 - Computer History\nReceived valid status codes, but pattern does not match") + end + + -- Check if GET is being redirected + if proxy.redirectCheck(get_r1, get_r2) and proxy.redirectCheck(get_r2, get_r3) then + return false, "Proxy might be redirecting requests" + end + + -- Check if at least CONNECTION worked + if cstatus then return true, "Methods supported:" .. response end + + -- Nothing works... + return false, nil +end portrule = shortport.port_or_service({8123,3128,8000,8080},{'polipo','squid-http','http-proxy'}) action = function(host, port) - local retval - local supported_methods = "\nMethods successfully tested: " - local fstatus = false - local test_url = "http://www.google.com" - local hostname = "www.google.com" - local pattern = "^server: gws" + local response + local i + local retval + local supported_methods = "\nMethods succesfully tested: " + local fstatus = false + local def_test = true + local test_url, pattern + local hostname - -- If arg url exists, use it as test url - if(nmap.registry.args.openproxy and nmap.registry.args.openproxy.url) then - test_url = nmap.registry.args.openproxy.url - pattern = false - if not string.match(test_url, "^http://.*") then - test_url = "http://" .. test_url - stdnse.print_debug("URL missing scheme. URL concatenated to http://") - end - url_table = url.parse(test_url) - hostname = url_table.host - end - if(nmap.registry.args.openproxy and nmap.registry.args.openproxy.pattern) then pattern = ".*" .. nmap.registry.args.openproxy.pattern .. ".*" end - - -- Trying GET method! - req = "GET " .. test_url .. " HTTP/1.0\r\nHost: " .. hostname .. "\r\n\r\n" - stdnse.print_debug("GET Request: " .. req) - local status, result = comm.exchange(host, port, req, {lines=1,proto=port.protocol, timeout=10000}) + test_url, pattern = proxy.return_args() + + if(test_url) then def_test = false end + if(pattern) then pattern = ".*" .. pattern .. ".*" end - if status then - lstatus = check(result, pattern) - if lstatus then - supported_methods = supported_methods .. "GET " - fstatus = true - end - end + if def_test + then fstatus, supported_methods = default_test(host, port) + else fstatus, supported_methods = custom_test(host, port, test_url, pattern); + end - -- Trying HEAD method - req = "HEAD " .. test_url .. " HTTP/1.0\r\nHost: " .. hostname .. "\r\n\r\n" - stdnse.print_debug("HEAD Request: " .. req) - local status, result = comm.exchange(host, port, req, {lines=1,proto=port.protocol, timeout=10000}) + -- If any of the tests were OK, then the proxy is potentially open + if fstatus then + retval = "Potentially OPEN proxy.\n" .. supported_methods + return retval + elseif not fstatus and supported_methods then + return supported_methods + end + return - if status then - lstatus = check(result, pattern) - if lstatus then - supported_methods = supported_methods .. "HEAD " - fstatus = true - end - end - - -- Trying CONNECT method - req = "CONNECT " .. hostname .. ":80 HTTP/1.0\r\n\r\n" - stdnse.print_debug("CONNECT Request: " .. req) - local status, result = comm.exchange(host, port, req, {lines=1,proto=port.protocol, timeout=10000}) - - if status then - lstatus = check(result, false); - if lstatus then - supported_methods = supported_methods .. "CONNECT" - fstatus = true - end - end - - -- If any of the tests were OK, then the proxy is potentially open - if fstatus then - retval = "Potentially OPEN proxy.\n" .. supported_methods - return retval - end - return end diff --git a/scripts/socks-open-proxy.nse b/scripts/socks-open-proxy.nse index 47d050c20..6795878aa 100644 --- a/scripts/socks-open-proxy.nse +++ b/scripts/socks-open-proxy.nse @@ -9,9 +9,9 @@ The payloads try to open a connection to www.google.com port 80. A different test host can be passed as openproxy.host (note the table syntax in the example) argument, as described below. ]] - --- ---@args openproxy.host Host that will be requested to the proxy +--@args proxy.url Url that will be requested to the proxy +--@args proxy.pattern Pattern that will be searched inside the request results --@output -- Interesting ports on scanme.nmap.org (64.13.134.52): -- PORT STATE SERVICE @@ -20,144 +20,148 @@ syntax in the example) argument, as described below. -- |_ Versions succesfully tested: Socks4 Socks5 --@usage -- nmap --script=socks-open-proxy \ --- --script-args openproxy={host=} +-- --script-args proxy.url=,proxy.pattern= author = "Joao Correa " license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default", "discovery", "external", "intrusive"} require "shortport" -require "bin" -require "nmap" require "stdnse" -require "dns" +require "url" +require "proxy" + +--- Performs the custom test, with user's arguments +-- @param host The host table +-- @param port The port table +-- @param test_url The url te send the request +-- @param pattern The pattern to check for valid result +-- @return status (if any request was succeded +-- @return response String with supported methods +local function custom_test(host, port, test_url, pattern) + local status4, status5, fstatus + local get_r4, get_r5 + local methods + local response = "Versions succesfully tested:" + + -- strip hostname + if not string.match(test_url, "^http://.*") then + test_url = "http://" .. test_url + stdnse.print_debug("URL missing scheme. URL concatenated to http://") + end + local url_table = url.parse(test_url) + local hostname = url_table.host + + -- make requests + status4, get_r4 = proxy.test_get(host, port, "socks4", test_url, hostname, pattern) + status5, get_r5 = proxy.test_get(host, port, "socks5", test_url, hostname, pattern) + + if(status4) then fstatus = true; response = response .. " Socks4" end + if(status5) then fstatus = true; response = response .. " Socks5" end + if(fstatus) then return fstatus, response end +end + +--- Performs the default test +-- First: Default google request and checks for Server: gws +-- Seconde: Request to wikipedia.org and checks for wikimedia pattern +-- Third: Request to computerhistory.org and checks for museum pattern +-- +-- If any of the requests is succesful, the proxy is considered open +-- If all requests return the same result, the user is alerted that +-- the proxy might be redirecting his requests (very common on wi-fi +-- connections at airports, cafes, etc.) +-- +-- @param host The host table +-- @param port The port table +-- @return status (if any request was succeded +-- @return response String with supported methods +local function default_test(host, port) + local status4, status5, fstatus + local cstatus4, cstatus5 + local get_r4, get_r5 + local methods + local response = "Versions succesfully tested:" + + local test_url = "http://www.google.com" + local hostname = "www.google.com" + local pattern = "^server: gws" + status4, get_r4, cstatus4 = proxy.test_get(host, port, "socks4", test_url, hostname, pattern) + status5, get_r5, cstatus5 = proxy.test_get(host, port, "socks5", test_url, hostname, pattern) + + if(status4) then fstatus = true; response = response .. " Socks4" end + if(status5) then fstatus = true; response = response .. " Socks5" end + if(fstatus) then return fstatus, response end + + -- if we receive a invalid response, but with a valid + -- response code, we should make a next attempt. + -- if we do not receive any valid status code, + -- there is no reason to keep testing... the proxy is probably not open + if not (cstatus4 or cstatus5) then return false, nil end + stdnse.print_debug("Test 1 - Google Web Server: Received valid status codes, but pattern does not match") + + test_url = "http://www.wikipedia.org" + hostname = "www.wikipedia.org" + pattern = "wikimedia" + status4, get_r4, cstatus4 = proxy.test_get(host, port, "socks4", test_url, hostname, pattern) + status5, get_r5, cstatus5 = proxy.test_get(host, port, "socks5", test_url, hostname, pattern) + + if(status4) then fstatus = true; response = response .. " Socks4" end + if(status5) then fstatus = true; response = response .. " Socks5" end + if(fstatus) then return fstatus, response end + + if not (cstatus4 or cstatus5) then return false, nil end + stdnse.print_debug("Test 2 - Wikipedia.org: Received valid status codes, but pattern does not match") + + test_url = "http://www.computerhistory.org" + hostname = "www.computerhistory.org" + pattern = "museum" + status4, get_r4, cstatus4 = proxy.test_get(host, port, "socks4", test_url, hostname, pattern) + status5, get_r5, cstatus5 = proxy.test_get(host, port, "socks5", test_url, hostname, pattern) + + if(status4) then fstatus = true; response = response .. " Socks4" end + if(status5) then fstatus = true; response = response .. " Socks5" end + if(fstatus) then return fstatus, response end + + if not (cstatus4 or cstatus5) then return false, nil end + stdnse.print_debug("Test 3 - Computer History: Received valid status codes, but pattern does not match") + + -- Check if GET is being redirected + if proxy.redirectCheck(get_r4, get_r5) then + return false, "Proxy might be redirecting requests" + end + + -- Nothing works... + return false, nil ---- Function that resolves IP address for hostname and ---- returns it as hex values ---@param hostname Hostname to resolve ---@return Ip address of hostname in hex -function hex_resolve(hostname) - local a, b, c, d; - local dns_status, ip = dns.query(hostname) - if not dns_status then - return false - end - local t, err = ipOps.get_parts_as_number(ip) - if t and not err - then a, b, c, d = unpack(t) - else return false - end - local sip = string.format("%.2x ", a) .. string.format("%.2x ", b) .. string.format("%.2x ", c) .. string.format("%.2x ",d) - return true, sip end portrule = shortport.port_or_service({1080},{"socks","socks4","socks5"}) action = function(host, port) - local response - local retval - local supported_versions = "\nVersions succesfully tested: " - local fstatus = false - local test_host = "www.google.com" + local supported_versions = "\nVersions succesfully tested: " + local fstatus = false + local pattern, test_url + local def_test = true + local hostname + local retval - -- If arg open-proxy.host exists, query dns for IP number and convert it to hex - if (nmap.registry.args.openproxy and nmap.registry.args.openproxy.host) then test_host = nmap.registry.args.openproxy.host end - local status, sip = hex_resolve(test_host) - if not status then - stdnse.print_debug("Failed to resolve IP Address") - return - end + test_url, pattern = proxy.return_args() - -- Attempting Socks 4 connection - -- Socks 4 payload: Version, Command, Null, Port, Ip Address, User (nmap), Null - -- Default port is always 80, different ports means different services, with different results - paystring = '04 01 00 50 ' .. sip .. ' 6e 6d 61 70 00' - payload = bin.pack("H",paystring) + if(test_url) then def_test = false end + if(pattern) then pattern = ".*" .. pattern .. ".*" end - local socket = nmap.new_socket() - socket:set_timeout(10000) - try = nmap.new_try(function() socket:close() end) - try(socket:connect(host.ip, port.number)) - try(socket:send(payload)) - response = try(socket:receive()) - request_status = string.byte(response, 2) + if def_test + then fstatus, supported_versions = default_test(host, port) + else fstatus, supported_versions = custom_test(host, port, test_url, pattern) + end - -- Send Socks4 payload to estabilish connection - -- If did not receive Request Granted byte from server, skip next test - if(request_status == 0x5b) then - stdnse.print_debug("Socks4: Received \"Request rejected or failed\" from proxy server") - elseif (request_status == 0x5c) then - stdnse.print_debug("Socks4: Received \"request failed because client is not running identd\" from proxy server") - elseif (request_status == 0x5d) then - stdnse.print_debug("Socks4: Received \"request failed because client's identd could not confirm the user ID string in the request\n from proxy server") + -- If any of the tests were OK, then the proxy is potentially open + if fstatus then + retval = "Potentially OPEN proxy.\n" .. supported_versions + return retval + elseif not fstatus and supported_versions then + return supported_versions + end + return - -- If received Request Granted byte from server, proxy is considered open - elseif (request_status == 0x5a) then - stdnse.print_debug("Socks4: Received \"Request Granted\" from proxy server") - supported_versions = supported_versions .. "Socks4 " - fstatus = true - end - socket:close() - - -- Attempting Socks 5 connection - -- Socks5 payload: Version, Auths Length, Auths methods required - payload = bin.pack("H",'05 01 00') - - -- Send first Socks5 payload to estabilish connection without authentication - local socket2 = nmap.new_socket() - socket2:set_timeout(10000) - try = nmap.new_try(function() socket2:close() end) - try(socket2:connect(host.ip, port.number)) - try(socket2:send(payload)) - auth = try(socket2:receive()) - r2 = string.byte(auth,2) - - -- If Auth is required, proxy is closed, skip next test - if(r2 ~= 0x00) then - stdnse.print_debug("Socks5: Authentication required") - - -- If no Auth is required, try to estabilish connection - else - stdnse.print_debug("Socks5: No authentication required") - - -- Socks5 second payload: Version, Command, Null, Address type, Ip-Address, Port number - paystring = '05 01 00 01 ' .. sip .. '00 50' - payload = bin.pack("H",paystring) - try(socket2:send(payload)) - z = try(socket2:receive()) - request_status = string.byte(z, 2) - - -- If did not received Request Granted byte from server, skip next test - if(request_status == 0x01) then - stdnse.print_debug("Socks5: Received \"General failure\" from proxy server") - elseif (request_status == 0x02) then - stdnse.print_debug("Socks5: Received \"Connection not allowed by ruleset\" from proxy server") - elseif (request_status == 0x03) then - stdnse.print_debug("Socks5: Received \"Network unreachable\" from proxy server") - elseif (request_status == 0x04) then - stdnse.print_debug("Socks5: Received \"Host unreachable\" from proxy server") - elseif (request_status == 0x05) then - stdnse.print_debug("Socks5: Received \"Connection refused by destination host\" from proxy server") - elseif (request_status == 0x06) then - stdnse.print_debug("Socks5: Received \"TTL Expired\" from proxy server") - elseif (request_status == 0x07) then - stdnse.print_debug("Socks5: Received \"command not supported / protocol error\" from proxy server") - elseif (request_status == 0x08) then - stdnse.print_debug("Socks5: Received \"Address type not supported\" from proxy server") - - -- If received request granted byte from server, the proxy is considered open - elseif (request_status == 0x00) then - stdnse.print_debug("Socks5: Received \"Request granted\" from proxy server") - supported_versions = supported_versions .. "Socks5" - fstatus = true - end - end - socket2:close() - - -- show results - if fstatus then - retval = "Potentially OPEN proxy." .. supported_versions - return retval - end - return end