metasploit-framework/modules/auxiliary/gather/f5_bigip_cookie_disclosure.rb

160 lines
5.5 KiB
Ruby
Raw Normal View History

2014-04-14 23:11:17 +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{
This module identifies F5 BigIP load balancers and leaks backend
2015-02-20 19:10:31 +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
},
'Author' =>
[
'Thanat0s <thanspam[at]trollprod.org>',
'Oleg Broslavsky <ovbroslavsky[at]gmail.com>',
'Nikita Oleksov <neoleksov[at]gmail.com>',
'Denis Kolegov <dnkolegov[at]gmail.com>'
],
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(
[
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', '/']),
OptInt.new('REQUESTS', [true, 'The number of requests to send', 10])
2014-04-14 23:11:17 +00:00
], self.class)
end
def change_endianness(value, size = 4)
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)
backend = {}
case
when cookie_value =~ /(\d{8,10})\.(\d{1,5})\./
host = Regexp.last_match(1).to_i
port = Regexp.last_match(2).to_i
2014-04-30 14:31:07 +00:00
host = change_endianness(host)
host = Rex::Socket.addr_itoa(host)
port = change_endianness(port, 2)
when cookie_value.downcase =~ /rd\d+o0{20}f{4}([a-f0-9]{8})o(\d{1,5})/
host = Regexp.last_match(1).to_i(16)
port = Regexp.last_match(2).to_i
host = Rex::Socket.addr_itoa(host)
when cookie_value.downcase =~ /vi([a-f0-9]{32})\.(\d{1,5})/
host = Regexp.last_match(1).to_i(16)
port = Regexp.last_match(2).to_i
host = Rex::Socket.addr_itoa(host, true)
port = change_endianness(port, 2)
when cookie_value.downcase =~ /rd\d+o([a-f0-9]{32})o(\d{1,5})/
host = Regexp.last_match(1).to_i(16)
port = Regexp.last_match(2).to_i
host = Rex::Socket.addr_itoa(host, true)
else
host = nil
port = nil
2014-04-28 21:33:01 +00:00
end
2015-05-08 19:04:44 +00:00
backend[:host] = host.nil? ? nil : host
backend[:port] = port.nil? ? nil : port
backend
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 = {}
res = send_request_raw({ 'method' => 'GET', 'uri' => @uri })
2014-04-30 14:31:07 +00:00
unless res.nil?
# 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"
regexp = /
([~_\.\-\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})))
(?:$|,|;|\s)
/x
m = res.get_cookies.match(regexp)
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
requests = datastore['REQUESTS']
backends = []
2014-04-30 14:31:07 +00:00
@uri = normalize_uri(target_uri.path.to_s)
print_status("#{peer} - Starting request #{@uri}")
(1..requests).each do |i|
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?
print_error("#{peer} - F5 BigIP load balancing cookie not found")
return
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 == 1
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
2014-04-28 21:26:29 +00:00
end
2014-04-30 14:31:07 +00:00
backend = cookie_decode(cookie[:value])
unless backend[:host].nil? || backends.include?(backend)
print_status("#{peer} - Backend #{backend[:host]}:#{backend[:port]} found")
backends.push(backend)
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
unless backends.empty?
2015-05-08 19:04:44 +00:00
report_note(host: rhost, type: 'f5_load_balancer_backends', data: backends)
2014-04-30 14:31:07 +00:00
end
2015-05-08 19:04:44 +00:00
rescue ::Rex::ConnectionRefused
print_error("#{peer} - Network connection error")
2015-03-23 08:31:08 +00:00
rescue ::Rex::ConnectionError
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