Skip to content

Commit

Permalink
Land #12217, f5_bigip_cookie_disclosure module now stores the cookie …
Browse files Browse the repository at this point in the history
…in noteswq
  • Loading branch information
h00die committed Jan 8, 2022
2 parents 1d3a6d5 + 4df91dd commit 7b11429
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## Vulnerable Application

This module identifies F5 BIG-IP load balancers and leaks backend information (pool name, routed domain,
and backend servers' IP addresses and ports) through cookies inserted by the BIG-IP systems.

## Verification Steps

1. Start `msfconsole`
1. Do: `use auxiliary/gather/f5_bigip_cookie_disclosure`
1. Do: `set RHOSTS www.example.com`
1. Do: `run`

## Options

### REQUESTS

The number of requests to send. Default value is `10`.

## Scenarios

### F5 BIP-IP load balancing cookie not found

```
msf5 > use auxiliary/gather/f5_bigip_cookie_disclosure
msf5 auxiliary(gather/f5_bigip_cookie_disclosure) > set RHOSTS www.example.com
RHOSTS => www.example.com
msf5 auxiliary(gather/f5_bigip_cookie_disclosure) > run
[*] Running module against 93.184.216.34
[*] Starting request /
[-] F5 BIG-IP load balancing cookie not found
[*] Auxiliary module execution completed
msf5 auxiliary(gather/f5_bigip_cookie_disclosure) >
```

### F5 BIP-IP load balancing cookie found

```
msf5 > use auxiliary/gather/f5_bigip_cookie_disclosure
msf5 auxiliary(gather/f5_bigip_cookie_disclosure) > set RHOSTS vulnerable-target.com
RHOSTS => vulnerable-target.com
msf5 auxiliary(gather/f5_bigip_cookie_disclosure) > run
[*] Running module against 1.1.1.1
[*] Starting request /
[+] F5 BIG-IP load balancing cookie "BIGipServer~DMZ~EXAMPLE~vulnarable-target-443_pool = 1214841098.47873.0000" found
[+] Load balancing pool name "~DMZ~EXAMPLE~vulnarable-target-443_pool" found
[+] Backend 10.1.105.72:443 found
[*] Auxiliary module execution completed
msf5 auxiliary(gather/f5_bigip_cookie_disclosure) > notes
Notes
=====
Time Host Service Port Protocol Type Data
---- ---- ------- ---- -------- ---- ----
2019-08-20 21:21:02 UTC 1.1.1.1 f5_load_balancer_cookie_name "BIGipServer~DMZ~EXAMPLE~vulnarable-target-443_pool"
2019-08-20 21:21:02 UTC 1.1.1.1 f5_load_balancer_pool_name "~DMZ~EXAMPLE~vulnarable-target-443_pool"
2019-08-20 21:21:02 UTC 1.1.1.1 f5_load_balancer_backends [{:host=>"10.1.105.72", :port=>443}]
msf5 auxiliary(gather/f5_bigip_cookie_disclosure) >
```
116 changes: 70 additions & 46 deletions modules/auxiliary/gather/f5_bigip_cookie_disclosure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,69 +8,77 @@ class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient

def initialize(info = {})
super(update_info(info,
'Name' => 'F5 BigIP Backend Cookie Disclosure',
'Description' => %q{
This module identifies F5 BigIP load balancers and leaks backend
information (pool name, backend's IP address and port, routed domain)
through cookies inserted by the BigIP system.
},
'Author' =>
[
super(
update_info(
info,
'Name' => 'F5 BIG-IP Backend Cookie Disclosure',
'Description' => %q{
This module identifies F5 BIG-IP load balancers and leaks backend information
(pool name, routed domain, and backend servers' IP addresses and ports) through
cookies inserted by the BIG-IP systems.
},
'Author' => [
'Thanat0s <thanspam[at]trollprod.org>',
'Oleg Broslavsky <ovbroslavsky[at]gmail.com>',
'Nikita Oleksov <neoleksov[at]gmail.com>',
'Denis Kolegov <dnkolegov[at]gmail.com>'
'Denis Kolegov <dnkolegov[at]gmail.com>',
'Paul-Emmanuel Raoul <skyper@skyplabs.net>'
],
'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']
'References' => [
['URL', 'https://support.f5.com/csp/article/K6917'],
['URL', 'https://support.f5.com/csp/article/K7784'],
['URL', 'https://support.f5.com/csp/article/K14784'],
['URL', 'https://support.f5.com/csp/article/K23254150']
],
'License' => MSF_LICENSE,
'DefaultOptions' =>
{
'SSL' => true
'License' => MSF_LICENSE,
'DefaultOptions' => {
'SSL' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => []
}
))
)
)

register_options(
[
OptInt.new('RPORT', [true, 'The BigIP service port to listen on', 443]),
OptInt.new('RPORT', [true, 'The BIG-IP service port', 443]),
OptString.new('TARGETURI', [true, 'The URI path to test', '/']),
OptInt.new('REQUESTS', [true, 'The number of requests to send', 10])
])
]
)
end

def change_endianness(value, size = 4)
conversion = nil
if size == 4
conversion = [value].pack("V").unpack("N").first
conversion = [value].pack('V').unpack('N').first
elsif size == 2
conversion = [value].pack("v").unpack("n").first
conversion = [value].pack('v').unpack('n').first
end
conversion
end

def cookie_decode(cookie_value)
backend = {}
case
when cookie_value =~ /(\d{8,10})\.(\d{1,5})\./
if cookie_value =~ /(\d{8,10})\.(\d{1,5})\./
host = Regexp.last_match(1).to_i
port = Regexp.last_match(2).to_i
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})/
elsif 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})/
elsif 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})/
elsif 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)
Expand All @@ -84,9 +92,10 @@ def cookie_decode(cookie_value)
backend
end

def get_cookie # request a page and extract a F5 looking cookie.
def fetch_cookie
# Request a page and extract a F5 looking cookie
cookie = {}
res = send_request_raw({ 'method' => 'GET', 'uri' => @uri })
res = send_request_raw('method' => 'GET', 'uri' => @uri)

unless res.nil?
# Get the SLB session IDs for all cases:
Expand All @@ -96,41 +105,47 @@ def get_cookie # request a page and extract a F5 looking cookie.
# 4. IPv6 pool members in non-default route domains - "BIGipServerWEB=rd3o20010112000000000000000000000030o80"

regexp = /
([~_\.\-\w\d]+)=(((?:\d+\.){2}\d+)|
([~.\-\w]+)=(((?:\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]
cookie[:id] = m.nil? ? nil : m[1]
cookie[:value] = m.nil? ? nil : m[2]
end
cookie
end

def run
requests = datastore['REQUESTS']
backends = []
cookie_name = ''
pool_name = ''
route_domain = ''
@uri = normalize_uri(target_uri.path.to_s)
print_status("Starting request #{@uri}")

(1..requests).each do |i|
cookie = get_cookie # Get the cookie
cookie = fetch_cookie # Get the cookie
# If the cookie is not found, stop process
if cookie.empty? || cookie[:id].nil?
print_error("F5 BigIP load balancing cookie not found")
return
print_error('F5 BIG-IP load balancing cookie not found')
return nil
end

# Print the cookie name on the first request
if i == 1
print_good("F5 BigIP load balancing cookie \"#{cookie[:id]} = #{cookie[:value]}\" found")
cookie_name = cookie[:id]
print_good("F5 BIG-IP load balancing cookie \"#{cookie_name} = #{cookie[:value]}\" found")
if cookie[:id].start_with?('BIGipServer')
print_good("Load balancing pool name \"#{cookie[:id].split('BIGipServer')[1]}\" found")
pool_name = cookie[:id].split('BIGipServer')[1]
print_good("Load balancing pool name \"#{pool_name}\" found")
end
if cookie[:value].start_with?('rd')
print_good("Route domain \"#{cookie[:value].split('rd')[1].split('o')[0]}\" found")
route_domain = cookie[:value].split('rd')[1].split('o')[0]
print_good("Route domain \"#{route_domain}\" found")
end
end

Expand All @@ -141,16 +156,25 @@ def run
end
end

# Reporting found cookie name in database
unless cookie_name.empty?
report_note(host: rhost, type: 'f5_load_balancer_cookie_name', data: cookie_name)
# Reporting found pool name in database
unless pool_name.empty?
report_note(host: rhost, type: 'f5_load_balancer_pool_name', data: pool_name)
end
# Reporting found route domain in database
unless route_domain.empty?
report_note(host: rhost, type: 'f5_load_balancer_route_domain', data: route_domain)
end
end
# Reporting found backends in database
unless backends.empty?
report_note(host: rhost, type: 'f5_load_balancer_backends', data: backends)
end

rescue ::Rex::ConnectionRefused
print_error("Network connection error")
rescue ::Rex::ConnectionError
print_error("Network connection error")
rescue ::OpenSSL::SSL::SSLError
print_error("SSL/TLS connection error")
rescue ::Rex::ConnectionRefused, ::Rex::ConnectionError
print_error('Network connection error')
rescue ::OpenSSL::SSL::SSLError
print_error('SSL/TLS connection error')
end
end

0 comments on commit 7b11429

Please sign in to comment.