1local proxy = require "proxy" 2local shortport = require "shortport" 3local stdnse = require "stdnse" 4local string = require "string" 5local table = require "table" 6local url = require "url" 7 8description=[[ 9Checks if an HTTP proxy is open. 10 11The script attempts to connect to www.google.com through the proxy and 12checks for a valid HTTP response code. Valid HTTP response codes are 13200, 301, and 302. If the target is an open proxy, this script causes 14the target to retrieve a web page from www.google.com. 15]] 16 17--- 18-- @usage 19-- nmap --script http-open-proxy.nse \ 20-- --script-args proxy.url=<url>,proxy.pattern=<pattern> 21-- @output 22-- Interesting ports on scanme.nmap.org (64.13.134.52): 23-- PORT STATE SERVICE 24-- 8080/tcp open http-proxy 25-- | proxy-open-http: Potentially OPEN proxy. 26-- |_ Methods successfully tested: GET HEAD CONNECT 27 28-- Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar> / www.buanzo.com.ar / linux-consulting.buanzo.com.ar 29-- Changelog: Added explode() function. Header-only matching now works. 30-- * Fixed set_timeout 31-- * Fixed some \r\n's 32-- 2008-10-02 Vlatko Kosturjak <kost@linux.hr> 33-- * Match case-insensitively against "^Server: gws" rather than 34-- case-sensitively against "^Server: GWS/". 35-- 2009-05-14 Joao Correa <joao@livewire.com.br> 36-- * Included tests for HEAD and CONNECT methods 37-- * Included url and pattern arguments 38-- * Script now checks for http response status code, when url is used 39-- * If google is used, script checks for Server: gws 40 41author = "Arturo 'Buanzo' Busleiman" 42license = "Same as Nmap--See https://nmap.org/book/man-legal.html" 43categories = {"default", "discovery", "external", "safe"} 44 45--- Performs the custom test, with user's arguments 46-- @param host The host table 47-- @param port The port table 48-- @param test_url The url te send the request 49-- @param pattern The pattern to check for valid result 50-- @return status if any request succeeded 51-- @return response String with supported methods 52function custom_test(host, port, test_url, pattern) 53 local lstatus = false 54 local response = {} 55 -- if pattern is not used, result for test is code check result. 56 -- otherwise it is pattern check result. 57 58 -- strip hostname 59 if not string.match(test_url, "^http://.*") then 60 test_url = "http://" .. test_url 61 stdnse.debug1("URL missing scheme. URL concatenated to http://") 62 end 63 local url_table = url.parse(test_url) 64 local hostname = url_table.host 65 66 local get_status = proxy.test_get(host, port, "http", test_url, hostname, pattern) 67 local head_status = proxy.test_head(host, port, "http", test_url, hostname, pattern) 68 local conn_status = proxy.test_connect(host, port, "http", hostname) 69 if get_status then 70 lstatus = true 71 response[#response+1] = "GET" 72 end 73 if head_status then 74 lstatus = true 75 response[#response+1] = "HEAD" 76 end 77 if conn_status then 78 lstatus = true 79 response[#response+1] = "CONNECTION" 80 end 81 if lstatus then response = "Methods supported: " .. table.concat(response, " ") end 82 return lstatus, response 83end 84 85--- Performs the default test 86-- First: Default google request and checks for Server: gws 87-- Seconde: Request to wikipedia.org and checks for wikimedia pattern 88-- Third: Request to computerhistory.org and checks for museum pattern 89-- 90-- If any of the requests is successful, the proxy is considered open 91-- If all get requests return the same result, the user is alerted that 92-- the proxy might be redirecting his requests (very common on wi-fi 93-- connections at airports, cafes, etc.) 94-- 95-- @param host The host table 96-- @param port The port table 97-- @return status if any request succeeded 98-- @return response String with supported methods 99function default_test(host, port) 100 local fstatus = false 101 local cstatus = false 102 local get_status, head_status, conn_status 103 local get_r1, get_r2, get_r3 104 local get_cstatus, head_cstatus 105 106 -- Start test n1 -> google.com 107 -- making requests 108 local test_url = "http://www.google.com" 109 local hostname = "www.google.com" 110 local pattern = "^server: gws" 111 get_status, get_r1, get_cstatus = proxy.test_get(host, port, "http", test_url, hostname, pattern) 112 local _ 113 head_status, _, head_cstatus = proxy.test_head(host, port, "http", test_url, hostname, pattern) 114 conn_status = proxy.test_connect(host, port, "http", hostname) 115 116 -- checking results 117 -- conn_status use a different flag (cstatus) 118 -- because test_connection does not use patterns, so it is unable to detect 119 -- cases where you receive a valid code, but the response does not match the 120 -- pattern. 121 -- if it was using the same flag, program could return without testing GET/HEAD 122 -- once more before returning 123 local response = {} 124 if get_status then fstatus = true; response[#response+1] = "GET" end 125 if head_status then fstatus = true; response[#response+1] = "HEAD" end 126 if conn_status then cstatus = true; response[#response+1] = "CONNECTION" end 127 128 -- if proxy is open, return it! 129 if fstatus then return fstatus, "Methods supported: " .. table.concat(response, " ") end 130 131 -- if we receive a invalid response, but with a valid 132 -- response code, we should make a next attempt. 133 -- if we do not receive any valid status code, 134 -- there is no reason to keep testing... the proxy is probably not open 135 if not (get_cstatus or head_cstatus or conn_status) then return false, nil end 136 stdnse.debug1("Test 1 - Google Web Server\nReceived valid status codes, but pattern does not match") 137 138 test_url = "http://www.wikipedia.org" 139 hostname = "www.wikipedia.org" 140 pattern = "wikimedia" 141 get_status, get_r2, get_cstatus = proxy.test_get(host, port, "http", test_url, hostname, pattern) 142 head_status, _, head_cstatus = proxy.test_head(host, port, "http", test_url, hostname, pattern) 143 conn_status = proxy.test_connect(host, port, "http", hostname) 144 145 if get_status then fstatus = true; response[#response+1] = "GET" end 146 if head_status then fstatus = true; response[#response+1] = "HEAD" end 147 if conn_status then 148 if not cstatus then response[#response+1] = "CONNECTION" end 149 cstatus = true 150 end 151 152 if fstatus then return fstatus, "Methods supported: " .. table.concat(response, " ") end 153 154 -- same valid code checking as above 155 if not (get_cstatus or head_cstatus or conn_status) then return false, nil end 156 stdnse.debug1("Test 2 - Wikipedia.org\nReceived valid status codes, but pattern does not match") 157 158 test_url = "http://www.computerhistory.org" 159 hostname = "www.computerhistory.org" 160 pattern = "museum" 161 get_status, get_r3, get_cstatus = proxy.test_get(host, port, "http", test_url, hostname, pattern) 162 conn_status = proxy.test_connect(host, port, "http", hostname) 163 164 if get_status then fstatus = true; response[#response+1] = "GET" end 165 if conn_status then 166 if not cstatus then response[#response+1] = "CONNECTION" end 167 cstatus = true 168 end 169 170 if fstatus then return fstatus, "Methods supported:" .. table.concat(response, " ") end 171 if not get_cstatus then 172 stdnse.debug1("Test 3 - Computer History\nReceived valid status codes, but pattern does not match") 173 end 174 175 -- Check if GET is being redirected 176 if proxy.redirectCheck(get_r1, get_r2) and proxy.redirectCheck(get_r2, get_r3) then 177 return false, "Proxy might be redirecting requests" 178 end 179 180 -- Check if at least CONNECTION worked 181 if cstatus then return true, "Methods supported:" .. table.concat(response, " ") end 182 183 -- Nothing works... 184 return false, nil 185end 186 187portrule = shortport.port_or_service({8123,3128,8000,8080},{'polipo','squid-http','http-proxy'}) 188 189action = function(host, port) 190 local supported_methods = "\nMethods successfully tested: " 191 local fstatus = false 192 local def_test = true 193 local test_url, pattern 194 195 test_url, pattern = proxy.return_args() 196 197 if(test_url) then def_test = false end 198 if(pattern) then pattern = ".*" .. pattern .. ".*" end 199 200 if def_test 201 then fstatus, supported_methods = default_test(host, port) 202 else fstatus, supported_methods = custom_test(host, port, test_url, pattern); 203 end 204 205 -- If any of the tests were OK, then the proxy is potentially open 206 if fstatus then 207 return "Potentially OPEN proxy.\n" .. supported_methods 208 elseif not fstatus and supported_methods then 209 return supported_methods 210 end 211 return 212 213end 214