2014-04-14 23:11:17 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2014-04-14 23:11:17 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
|
|
|
|
include Msf::Auxiliary::Report
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
2014-04-30 14:31:07 +00:00
|
|
|
'Name' => 'F5 BigIP Backend Cookie Disclosure',
|
2014-04-14 23:11:17 +00:00
|
|
|
'Description' => %q{
|
2014-05-05 18:38:53 +00:00
|
|
|
This module identifies F5 BigIP load balancers and leaks backend
|
2015-02-13 08:29:23 +00:00
|
|
|
information (pool name, backend's IP address and port, routed domain) through cookies inserted by the BigIP system.
|
2014-04-14 23:11:17 +00:00
|
|
|
},
|
2015-02-19 10:06:00 +00:00
|
|
|
'Author' => [ 'Thanat0s <thanspam[at]trollprod.org>',
|
2015-02-13 08:29:23 +00:00
|
|
|
'Oleg Broslavsky <ovbroslavsky[at]gmail.com>',
|
|
|
|
'Nikita Oleksov <neoleksov[at]gmail.com>',
|
2015-02-19 10:02:10 +00:00
|
|
|
'Denis Kolegov <dnkolegov[at]gmail.com>'
|
2015-02-13 08:29:23 +00:00
|
|
|
],
|
2014-04-15 00:48:55 +00:00
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
['URL', 'http://support.f5.com/kb/en-us/solutions/public/6000/900/sol6917.html'],
|
|
|
|
['URL', 'http://support.f5.com/kb/en-us/solutions/public/7000/700/sol7784.html?sr=14607726']
|
|
|
|
],
|
2015-03-23 08:31:08 +00:00
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'DefaultOptions' =>
|
|
|
|
{
|
|
|
|
'SSLVersion' => 'TLS1'
|
|
|
|
}
|
2014-04-14 23:11:17 +00:00
|
|
|
))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
2015-03-17 06:23:13 +00:00
|
|
|
OptInt.new('RPORT', [true, 'The BigIP service port to listen on', 443]),
|
2015-03-23 08:31:08 +00:00
|
|
|
OptBool.new('SSL', [true, "Negotiate SSL for outgoing connections", true]),
|
2014-04-14 23:11:17 +00:00
|
|
|
OptString.new('TARGETURI', [true, 'The URI path to test', '/']),
|
2015-03-17 06:23:13 +00:00
|
|
|
OptInt.new('REQUESTS', [true, 'The number of requests to send', 10])
|
2014-04-14 23:11:17 +00:00
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
2014-04-30 14:31:07 +00:00
|
|
|
def change_endianness(value, size=4)
|
2015-02-13 08:29:23 +00:00
|
|
|
conversion = nil
|
2014-04-30 14:31:07 +00:00
|
|
|
|
|
|
|
if size == 4
|
|
|
|
conversion = [value].pack("V").unpack("N").first
|
|
|
|
elsif size == 2
|
|
|
|
conversion = [value].pack("v").unpack("n").first
|
|
|
|
end
|
|
|
|
|
|
|
|
conversion
|
|
|
|
end
|
|
|
|
|
2014-04-14 23:11:17 +00:00
|
|
|
def cookie_decode(cookie_value)
|
2015-02-13 08:29:23 +00:00
|
|
|
if cookie_value =~ /(\d{8,10})\.(\d{1,5})\./
|
2014-04-30 14:31:07 +00:00
|
|
|
host = $1.to_i
|
|
|
|
port = $2.to_i
|
|
|
|
host = change_endianness(host)
|
|
|
|
host = Rex::Socket.addr_itoa(host)
|
|
|
|
port = change_endianness(port, 2)
|
2015-02-13 08:29:23 +00:00
|
|
|
elsif cookie_value.downcase =~ /rd\d+o0{20}f{4}([a-f0-9]{8})o(\d{1,5})/
|
|
|
|
host = $1.to_i(16)
|
|
|
|
port = $2.to_i
|
|
|
|
host = Rex::Socket.addr_itoa(host)
|
|
|
|
elsif cookie_value.downcase =~ /vi([a-f0-9]{32})\.(\d{1,5})/
|
|
|
|
host = $1.to_i(16)
|
|
|
|
port = $2.to_i
|
|
|
|
host = Rex::Socket.addr_itoa(host, v6=true)
|
|
|
|
port = change_endianness(port, 2)
|
|
|
|
elsif cookie_value.downcase =~ /rd\d+o([a-f0-9]{32})o(\d{1,5})/
|
|
|
|
host = $1.to_i(16)
|
|
|
|
port = $2.to_i
|
|
|
|
host = Rex::Socket.addr_itoa(host, v6=true)
|
|
|
|
elsif cookie_value =~ /!(.){104}/
|
|
|
|
host = nil
|
|
|
|
port = nil
|
2014-04-28 21:33:01 +00:00
|
|
|
end
|
2015-02-19 10:06:00 +00:00
|
|
|
back_end = (host.nil?) ? nil : "#{host}:#{port}"
|
2014-04-14 23:11:17 +00:00
|
|
|
end
|
|
|
|
|
2014-04-28 09:32:46 +00:00
|
|
|
def get_cookie # request a page and extract a F5 looking cookie.
|
2014-04-30 14:31:07 +00:00
|
|
|
cookie = {}
|
2014-04-14 23:11:17 +00:00
|
|
|
res = send_request_raw({
|
|
|
|
'method' => 'GET',
|
|
|
|
'uri' => @uri
|
|
|
|
})
|
2014-04-30 14:31:07 +00:00
|
|
|
|
|
|
|
unless res.nil?
|
2015-02-16 06:31:52 +00:00
|
|
|
# Get the SLB session IDs for all cases:
|
|
|
|
# 1. IPv4 pool members - "BIGipServerWEB=2263487148.3013.0000",
|
|
|
|
# 2. IPv4 pool members in non-default routed domains - "BIGipServerWEB=rd5o00000000000000000000ffffc0000201o80",
|
|
|
|
# 3. IPv6 pool members - "BIGipServerWEB=vi20010112000000000000000000000030.20480",
|
|
|
|
# 4. IPv6 pool members in non-default route domains - "BIGipServerWEB=rd3o20010112000000000000000000000030o80",
|
|
|
|
# 5. Encrypted cookies - "BIGipServerWEB=!dcdlUciYEFlt1QzXtD7QKx22XJx7Uuj2I0dYdFTwJASsJyJySME9/GACjztr7WYJIvHxTSNreeve7foossGzKS3vT9ECJscSg1LAc3rc"
|
|
|
|
|
2015-02-16 16:53:32 +00:00
|
|
|
m = res.get_cookies.match(/([~_\.\-\w\d]+)=(((?:\d+\.){2}\d+)|(rd\d+o0{20}f{4}\w+o\d{1,5})|(vi([a-f0-9]{32})\.(\d{1,5}))|(rd\d+o([a-f0-9]{32})o(\d{1,5}))|(!(.){104}))(?:$|,|;|\s)/)
|
2015-02-13 08:29:23 +00:00
|
|
|
cookie[:id] = (m.nil?) ? nil : m[1]
|
|
|
|
cookie[:value] = (m.nil?) ? nil : m[2]
|
|
|
|
|
|
|
|
end
|
2014-04-30 14:31:07 +00:00
|
|
|
|
|
|
|
cookie
|
2014-04-14 23:11:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
2014-04-30 14:31:07 +00:00
|
|
|
unless datastore['REQUESTS'] > 0
|
|
|
|
print_error("Please, configure more than 0 REQUESTS")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
back_ends = []
|
|
|
|
@uri = normalize_uri(target_uri.path.to_s)
|
|
|
|
print_status("#{peer} - Starting request #{@uri}")
|
|
|
|
|
|
|
|
for i in 0...datastore['REQUESTS']
|
|
|
|
cookie = get_cookie() # Get the cookie
|
2014-04-28 21:26:29 +00:00
|
|
|
# If the cookie is not found, stop process
|
2014-04-30 14:31:07 +00:00
|
|
|
if cookie.empty? || cookie[:id].nil?
|
2015-02-16 06:31:52 +00:00
|
|
|
print_error("#{peer} - F5 BigIP load balancing cookie not found")
|
2015-02-13 08:29:23 +00:00
|
|
|
break
|
2014-04-28 21:26:29 +00:00
|
|
|
end
|
2014-04-30 14:31:07 +00:00
|
|
|
|
2014-04-28 21:26:29 +00:00
|
|
|
# Print the cookie name on the first request
|
|
|
|
if i == 0
|
2015-02-19 05:43:57 +00:00
|
|
|
print_status("#{peer} - F5 BigIP load balancing cookie \"#{cookie[:id]} = #{cookie[:value]}\" found")
|
2015-02-19 10:02:10 +00:00
|
|
|
if cookie[:id].start_with?('BIGipServer')
|
|
|
|
print_status("#{peer} - Load balancing pool name \"#{cookie[:id].split('BIGipServer')[1]}\" found")
|
|
|
|
end
|
|
|
|
if cookie[:value].start_with?('rd')
|
|
|
|
print_status("#{peer} - Route domain \"#{cookie[:value].split('rd')[1].split('o')[0]}\" found")
|
|
|
|
end
|
2015-02-16 06:31:52 +00:00
|
|
|
if cookie[:value].start_with?('!')
|
2015-02-19 10:06:00 +00:00
|
|
|
print_status("#{peer} - F5 BigIP cookie is probably encrypted")
|
2015-02-19 10:02:10 +00:00
|
|
|
end
|
2014-04-28 21:26:29 +00:00
|
|
|
end
|
2014-04-30 14:31:07 +00:00
|
|
|
|
|
|
|
back_end = cookie_decode(cookie[:value])
|
2015-02-13 08:29:23 +00:00
|
|
|
unless back_end.nil? || back_ends.include?(back_end)
|
2014-04-30 14:31:07 +00:00
|
|
|
print_status("#{peer} - Backend #{back_end} found")
|
|
|
|
back_ends.push(back_end)
|
2014-04-14 23:11:17 +00:00
|
|
|
end
|
|
|
|
end
|
2014-04-30 14:31:07 +00:00
|
|
|
|
2014-04-28 21:26:29 +00:00
|
|
|
# Reporting found backends in database
|
2014-04-30 14:31:07 +00:00
|
|
|
unless back_ends.empty?
|
|
|
|
report_note(
|
|
|
|
:host => rhost,
|
|
|
|
:type => "f5_load_balancer_backends",
|
|
|
|
:data => back_ends
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2015-03-23 08:31:08 +00:00
|
|
|
rescue ::Rex::ConnectionError
|
2015-03-17 06:23:13 +00:00
|
|
|
print_error("#{peer} - Network connection error")
|
|
|
|
rescue ::OpenSSL::SSL::SSLError
|
|
|
|
print_error("#{peer} - SSL/TLS connection error")
|
|
|
|
|
2014-04-14 23:11:17 +00:00
|
|
|
end
|
2015-02-19 10:02:10 +00:00
|
|
|
end
|