1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 12:41:29 +00:00

Scripts now use fallback requests when valid codes are received but pattern was not found.

Scripts now use the lib proxy.lua to perform similar tasks
This commit is contained in:
joao
2009-07-30 02:08:55 +00:00
parent 928de03d3d
commit c24c728b5d
2 changed files with 293 additions and 223 deletions

View File

@@ -11,8 +11,8 @@ web page from www.google.com.
]] ]]
--- ---
-- @args openproxy.url Url that will be requested to the proxy -- @args proxy.url Url that will be requested to the proxy
-- @args openproxy.pattern Pattern that will be searched inside the request results -- @args proxy.pattern Pattern that will be searched inside the request results
-- @output -- @output
-- Interesting ports on scanme.nmap.org (64.13.134.52): -- Interesting ports on scanme.nmap.org (64.13.134.52):
-- PORT STATE SERVICE -- PORT STATE SERVICE
@@ -35,8 +35,7 @@ web page from www.google.com.
-- --
-- @usage -- @usage
-- nmap --script http-open-proxy.nse \ -- nmap --script http-open-proxy.nse \
-- --script-args 'openproxy={url=<url>,pattern=<pattern>}' -- --script-args proxy.url=<url>,proxy.pattern=<pattern>
author = "Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>" author = "Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html" license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
@@ -45,112 +44,179 @@ require "comm"
require "shortport" require "shortport"
require "stdnse" require "stdnse"
require "url" require "url"
require "proxy"
--- check function, makes checkings for all valid returned status --- Performs the custom test, with user's arguments
--- If any of the HTTP status below is found, the proxy is potentially open -- @param host The host table
--@param result connection result -- @param port The port table
--@return true if any of the status is found, otherwise false -- @param test_url The url te send the request
function check_code(result) -- @param pattern The pattern to check for valid result
if string.match(result:lower(),"^http/%d\.%d%s*200") then return true end -- @return status (if any request was succeded
if string.match(result:lower(),"^http/%d\.%d%s*30[12]") then return true end -- @return response String with supported methods
return false function custom_test(host, port, test_url, pattern)
end local lstatus = false
local response = ""
-- if pattern is not used, result for test is code check result.
-- otherwise it is pattern check result.
--- check pattern, searches a pattern inside a response with multiple lines -- strip hostname
--@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
--- 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
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"
-- 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 if not string.match(test_url, "^http://.*") then
test_url = "http://" .. test_url test_url = "http://" .. test_url
stdnse.print_debug("URL missing scheme. URL concatenated to http://") stdnse.print_debug("URL missing scheme. URL concatenated to http://")
end end
url_table = url.parse(test_url) local url_table = url.parse(test_url)
hostname = url_table.host local 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! local get_status = proxy.test_get(host, port, "http", test_url, hostname, pattern)
req = "GET " .. test_url .. " HTTP/1.0\r\nHost: " .. hostname .. "\r\n\r\n" local head_status = proxy.test_head(host, port, "http", test_url, hostname, pattern)
stdnse.print_debug("GET Request: " .. req) local conn_status = proxy.test_connect(host, port, "http", hostname)
local status, result = comm.exchange(host, port, req, {lines=1,proto=port.protocol, timeout=10000}) if get_status then
lstatus = true
if status then response = response .. " GET"
lstatus = check(result, pattern)
if lstatus then
supported_methods = supported_methods .. "GET "
fstatus = true
end 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
--- 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 _
-- 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 end
-- Trying HEAD method if fstatus then return fstatus, "Methods supported: " .. response end
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 status then -- same valid code checking as above
lstatus = check(result, pattern) if not (get_cstatus or head_cstatus or conn_status) then return false, nil end
if lstatus then stdnse.print_debug("Test 2 - Wikipedia.org\nReceived valid status codes, but pattern does not match")
supported_methods = supported_methods .. "HEAD "
fstatus = true test_url = "http://www.computerhistory.org"
end 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 end
-- Trying CONNECT method if fstatus then return fstatus, "Methods supported:" .. response end
req = "CONNECT " .. hostname .. ":80 HTTP/1.0\r\n\r\n" if not get_cstatus then
stdnse.print_debug("CONNECT Request: " .. req) stdnse.print_debug("Test 3 - Computer History\nReceived valid status codes, but pattern does not match")
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
-- 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 response
local i
local retval
local supported_methods = "\nMethods succesfully tested: "
local fstatus = false
local def_test = true
local test_url, pattern
local hostname
test_url, pattern = proxy.return_args()
if(test_url) then def_test = false end
if(pattern) then pattern = ".*" .. pattern .. ".*" end
if def_test
then fstatus, supported_methods = default_test(host, port)
else fstatus, supported_methods = custom_test(host, port, test_url, pattern);
end end
-- If any of the tests were OK, then the proxy is potentially open -- If any of the tests were OK, then the proxy is potentially open
if fstatus then if fstatus then
retval = "Potentially OPEN proxy.\n" .. supported_methods retval = "Potentially OPEN proxy.\n" .. supported_methods
return retval return retval
elseif not fstatus and supported_methods then
return supported_methods
end end
return return
end end

View File

@@ -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 different test host can be passed as openproxy.host (note the table
syntax in the example) argument, as described below. 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 --@output
-- Interesting ports on scanme.nmap.org (64.13.134.52): -- Interesting ports on scanme.nmap.org (64.13.134.52):
-- PORT STATE SERVICE -- PORT STATE SERVICE
@@ -20,144 +20,148 @@ syntax in the example) argument, as described below.
-- |_ Versions succesfully tested: Socks4 Socks5 -- |_ Versions succesfully tested: Socks4 Socks5
--@usage --@usage
-- nmap --script=socks-open-proxy \ -- nmap --script=socks-open-proxy \
-- --script-args openproxy={host=<host>} -- --script-args proxy.url=<host>,proxy.pattern=<pattern>
author = "Joao Correa <joao@livewire.com.br>" author = "Joao Correa <joao@livewire.com.br>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html" license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "external", "intrusive"} categories = {"default", "discovery", "external", "intrusive"}
require "shortport" require "shortport"
require "bin"
require "nmap"
require "stdnse" require "stdnse"
require "dns" require "url"
require "proxy"
--- Function that resolves IP address for hostname and --- Performs the custom test, with user's arguments
--- returns it as hex values -- @param host The host table
--@param hostname Hostname to resolve -- @param port The port table
--@return Ip address of hostname in hex -- @param test_url The url te send the request
function hex_resolve(hostname) -- @param pattern The pattern to check for valid result
local a, b, c, d; -- @return status (if any request was succeded
local dns_status, ip = dns.query(hostname) -- @return response String with supported methods
if not dns_status then local function custom_test(host, port, test_url, pattern)
return false 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 end
local t, err = ipOps.get_parts_as_number(ip) local url_table = url.parse(test_url)
if t and not err local hostname = url_table.host
then a, b, c, d = unpack(t)
else return false -- 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 end
local sip = string.format("%.2x ", a) .. string.format("%.2x ", b) .. string.format("%.2x ", c) .. string.format("%.2x ",d)
return true, sip -- Nothing works...
return false, nil
end end
portrule = shortport.port_or_service({1080},{"socks","socks4","socks5"}) portrule = shortport.port_or_service({1080},{"socks","socks4","socks5"})
action = function(host, port) action = function(host, port)
local response
local retval
local supported_versions = "\nVersions succesfully tested: " local supported_versions = "\nVersions succesfully tested: "
local fstatus = false local fstatus = false
local test_host = "www.google.com" 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 test_url, pattern = proxy.return_args()
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(test_url) then def_test = false end
if not status then if(pattern) then pattern = ".*" .. pattern .. ".*" end
stdnse.print_debug("Failed to resolve IP Address")
return if def_test
then fstatus, supported_versions = default_test(host, port)
else fstatus, supported_versions = custom_test(host, port, test_url, pattern)
end end
-- Attempting Socks 4 connection -- If any of the tests were OK, then the proxy is potentially open
-- 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)
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)
-- 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 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 if fstatus then
retval = "Potentially OPEN proxy." .. supported_versions retval = "Potentially OPEN proxy.\n" .. supported_versions
return retval return retval
elseif not fstatus and supported_versions then
return supported_versions
end end
return return
end end