diff --git a/modules/auxiliary/scanner/http/open_proxy.rb b/modules/auxiliary/scanner/http/open_proxy.rb
index 6ec76f75bb..9c1240d5fa 100644
--- a/modules/auxiliary/scanner/http/open_proxy.rb
+++ b/modules/auxiliary/scanner/http/open_proxy.rb
@@ -7,7 +7,7 @@ require 'msf/core'
class MetasploitModule < Msf::Auxiliary
- include Msf::Exploit::Remote::Tcp
+ include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::WmapScanServer
include Msf::Auxiliary::Report
@@ -16,8 +16,10 @@ class MetasploitModule < Msf::Auxiliary
super(update_info(info,
'Name' => 'HTTP Open Proxy Detection',
'Description' => %q{
- Checks if an HTTP proxy is open. False positive are avoided
- verifing the HTTP return code and matching a pattern.
+ Checks if an HTTP proxy is open. False positive are avoided
+ verifying the HTTP return code and matching a pattern.
+ The CONNECT method is verified only the return code.
+ HTTP headers are shown regarding the use of proxy or load balancer.
},
'References' =>
[
@@ -31,224 +33,126 @@ class MetasploitModule < Msf::Auxiliary
register_options(
[
Opt::RPORT(8080),
- OptBool.new('MULTIPORTS', [ false, 'Multiple ports will be used : 80, 1080, 3128, 8080, 8123', false ]),
- OptBool.new('RANDOMIZE_PORTS', [ false, 'Randomize the order the ports are probed', false ]),
- OptBool.new('VERIFY_CONNECT', [ false, 'Enable test for CONNECT method', false ]),
- OptBool.new('VERIFY_HEAD', [ false, 'Enable test for HEAD method', false ]),
- OptBool.new('LOOKUP_PUBLIC_ADDRESS', [ false, 'Enable test for retrieve public IP address via RIPE.net', false ]),
- OptString.new('SITE', [ true, 'The web site to test via alleged web proxy (default is www.google.com)', 'www.google.com' ]),
- OptString.new('ValidCode', [ false, "Valid HTTP code for a successfully request", '200,302' ]),
- OptString.new('ValidPattern', [ false, "Valid HTTP server header for a successfully request", 'server: gws' ]),
- OptString.new('UserAgent', [ true, 'The HTTP User-Agent sent in the request', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)' ]),
- ], self.class)
-
- register_advanced_options(
- [
- OptString.new('RIPE_ADDRESS', [ true, 'www.ripe.net IP address', '193.0.6.139' ]),
+ OptBool.new('MULTIPORTS', [ false, 'Multiple ports will be used: 80, 443, 1080, 3128, 8000, 8080, 8123', false ]),
+ OptBool.new('VERIFYCONNECT', [ false, 'Enable CONNECT HTTP method check', false ]),
+ OptString.new('CHECKURL', [ true, 'The web site to test via alleged web proxy', 'http://www.google.com' ]),
+ OptString.new('VALIDCODES', [ true, "Valid HTTP code for a successfully request", '200,302' ]),
+ OptString.new('VALIDPATTERN', [ true, "Valid pattern match (case-sensitive into the headers and HTML body) for a successfully request", '
302 Moved' ]),
], self.class)
register_wmap_options({
- 'OrderID' => 1,
- 'Require' => {},
- })
+ 'OrderID' => 1,
+ 'Require' => {},
+ })
end
def run_host(target_host)
+ check_url = datastore['CHECKURL']
+
+ if datastore['VERIFYCONNECT']
+ target_method = 'CONNECT'
+ # CONNECT doesn't need but need port
+ check_url = check_url.gsub(/[http:\/\/|https:\/\/]/, '')
+ if check_url !~ /:443$/
+ check_url = check_url + ":443"
+ end
+ else
+ target_method = 'GET'
+ # GET only http request
+ check_url = check_url.gsub(/https:\/\//, '')
+ if check_url !~ /^http:\/\//i
+ check_url = 'http://' + check_url
+ end
+ end
+
target_ports = []
if datastore['MULTIPORTS']
- target_ports = [ 80, 1080, 3128, 8080, 8123 ]
+ target_ports = [ 80, 443, 1080, 3128, 8000, 8080, 8123 ]
+ else
+ target_ports.push(datastore['RPORT'].to_i)
end
- target_ports.push(datastore['RPORT'].to_i)
-
- if datastore['RANDOMIZE_PORTS']
- target_ports = target_ports.sort_by { rand }
- end
-
- target_ports = target_ports.uniq
-
- site = datastore['SITE']
- user_agent = datastore['UserAgent']
+ target_proxy_headers = [ 'Forwarded', 'Front-End-Https', 'Max-Forwards', 'Via', 'X-Cache', 'X-Cache-Lookup', 'X-Client-IP', 'X-Forwarded-For', 'X-Forwarded-Host' ]
target_ports.each do |target_port|
- datastore['RPORT'] = target_port
- if target_host == site
- print_error("Target is the same as proxy site.")
- else
- check_host(target_host,target_port,site,user_agent)
- end
+ verify_target(target_host,target_port,target_method,check_url,target_proxy_headers)
end
end
- def check_pattern(res,pattern)
+ def verify_target(target_host,target_port,target_method,check_url,target_proxy_headers)
- if (res =~ /#{pattern}/i)
- return 1
- else
- return 0
- end
+ vprint_status("#{peer} - Sending a web request... [#{target_method}][#{check_url}]")
- end
-
- def write_request(method,site,user_agent)
-
- request = method + " http://" + site + "/ HTTP/1.1" + "\r\n" +
- "Host: " + site + "\r\n" +
- "Connection: close" + "\r\n" +
- "User-Agent: #{user_agent}" + "\r\n" +
- "Accept-Encoding: *" + "\r\n" +
- "Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7" + "\r\n" +
- "Cache-Control: no" + "\r\n" +
- "Accept-Language: de,en;q=0.7,en-us;q=0.3" + "\r\n" +
- "\r\n"
-
- return request
-
- end
-
- def send_request(site,user_agent)
+ datastore['RPORT'] = target_port
begin
- connect
-
- request = write_request('GET',site,user_agent)
- sock.put(request)
- res = sock.get_once(-1, 10)
-
- disconnect
-
- validcodes = datastore['ValidCode'].split(/,/)
-
- is_valid = 0
- retcode = 0
- retvia = 'n/a'
- retsrv = 'n/a'
-
- if (res and res.match(/^HTTP\/1\.[01]\s+([^\s]+)\s+(.*)/))
-
- retcode = $1
-
- if (res.match(/Server: (.*)/))
- retsrv = $1.chomp
- end
-
- if (res.match(/Via: (.*)\((.*)\)/))
- retvia = $2
- end
-
- validcodes.each do |validcode|
- if (retcode.to_i == validcode.to_i)
- is_valid += 1
- end
- end
-
- if (check_pattern(res,datastore['ValidPattern']) == 1)
- is_valid += 1
- end
- end
-
- retres = [ is_valid, retcode, retvia, retsrv ]
-
- return retres
-
- rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
- rescue ::Timeout::Error, ::Errno::EPIPE
- end
- end
-
- def send_request_ripe(user_agent)
-
- ripe_address = datastore['RIPE_ADDRESS']
-
- begin
- connect
-
- request = write_request('GET',ripe_address,user_agent)
- sock.put(request)
- res = sock.get_once(-1, 10)
-
- disconnect
-
- retres = 0
-
- if (res and res.match(/^HTTP\/1\.[01]\s+([^\s]+)\s+(.*)/))
-
- retcode = $1
-
- if (retcode.to_i == 200)
- res.match(/Your IP Address is: (\s+)([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\s+)<\/strong>/m)
- retres = "#{$2}.#{$3}.#{$4}.#{$5}"
- end
- end
-
- return retres
-
- rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
- rescue ::Timeout::Error, ::Errno::EPIPE
- end
- end
-
- def check_host(target_host,target_port,site,user_agent)
- vprint_status("Checking #{target_host}:#{target_port} [#{site}]")
-
- is_valid,retcode,retvia,retsrv = send_request(site,user_agent)
-
- if (is_valid == 2)
-
- print_status("#{target_host}:#{target_port} is a potentially OPEN proxy [#{retcode}] (#{retvia})")
-
- report_note(
- :host => target_host,
- :port => target_port,
- :method => 'GET',
- :proto => 'tcp',
- :sname => (ssl ? 'https' : 'http'),
- :type => 'OPEN PROXY',
- :data => 'Open proxy'
+ res = send_request_cgi(
+ 'uri' => check_url,
+ 'method' => target_method,
+ 'version' => '1.1'
)
- if (datastore['VERIFY_CONNECT'])
+ return if not res
- permit_connect,retcode,retvia,retsrv = send_request(site,user_agent)
+ vprint_status("#{peer} - Returns with '#{res.code}' status code [#{target_method}][#{check_url}]")
- if (permit_connect == 2)
- print_status("#{target_host}:#{target_port} CONNECT method successfully tested")
+ valid_codes = datastore['VALIDCODES'].split(/,/)
+
+ target_proxy_headers_results = []
+ target_proxy_headers.each do |proxy_header|
+ if (res.headers.to_s.match(/#{proxy_header}: (.*)/))
+ proxy_header_value = $1
+ # Ok...I don't like it but works...
+ target_proxy_headers_results.push("\n |_ #{proxy_header}: #{proxy_header_value}")
+ end
+ end
+
+ if target_proxy_headers_results.any?
+ proxy_headers = target_proxy_headers_results.join()
+ end
+
+ if datastore['VERIFYCONNECT']
+ # Verifiying CONNECT we check only the return code
+ if valid_codes.include?(res.code.to_s)
+
+ print_good("#{peer} - Potentially open proxy [#{res.code}][#{target_method}]#{proxy_headers}")
report_note(
:host => target_host,
:port => target_port,
- :method => 'CONNECT'
+ :method => target_method,
+ :proto => 'tcp',
+ :sname => (ssl ? 'https' : 'http'),
+ :type => 'OPEN HTTP PROXY',
+ :data => 'Open http proxy (CONNECT)'
)
+
end
- end
+ else
+ # Verify return code && (headers.pattern or body.pattern)
+ if valid_codes.include?(res.code.to_s) && (res.headers.include?(datastore['VALIDPATTERN']) || res.body.include?(datastore['VALIDPATTERN']))
- if (datastore['VERIFY_HEAD'])
-
- permit_connect,retcode,retvia,retsrv = send_request(site,user_agent)
-
- if (permit_connect == 2)
- print_status("#{target_host}:#{target_port} HEAD method successfully tested")
+ print_good("#{peer} - Potentially open proxy [#{res.code}][#{target_method}]#{proxy_headers}")
report_note(
:host => target_host,
:port => target_port,
- :method => 'HEAD'
+ :method => target_method,
+ :proto => 'tcp',
+ :sname => (ssl ? 'https' : 'http'),
+ :type => 'OPEN HTTP PROXY',
+ :data => 'Open http proxy (GET)'
)
+
end
end
- if (datastore['LOOKUP_PUBLIC_ADDRESS'])
-
- retres = send_request_ripe(user_agent)
-
- if (retres != 0)
- print_status("#{target_host}:#{target_port} using #{retres} public IP address")
- end
- end
+ rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE => e
+ vprint_error("#{peer} - The port '#{target_port}' is unreachable!")
+ return nil
end
-
end
end