6
6
require 'msf/core'
7
7
8
8
class Metasploit3 < Msf ::Auxiliary
9
-
10
9
include Msf ::Auxiliary ::Report
11
10
include Msf ::Exploit ::Remote ::HttpClient
12
11
@@ -17,11 +16,13 @@ def initialize(info = {})
17
16
This module identifies F5 BigIP load balancers and leaks backend
18
17
information (pool name, backend's IP address and port, routed domain) through cookies inserted by the BigIP system.
19
18
} ,
20
- 'Author' => [ 'Thanat0s <thanspam[at]trollprod.org>' ,
21
- 'Oleg Broslavsky <ovbroslavsky[at]gmail.com>' ,
22
- 'Nikita Oleksov <neoleksov[at]gmail.com>' ,
23
- 'Denis Kolegov <dnkolegov[at]gmail.com>'
24
- ] ,
19
+ 'Author' =>
20
+ [
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
+ ] ,
25
26
'References' =>
26
27
[
27
28
[ 'URL' , 'http://support.f5.com/kb/en-us/solutions/public/6000/900/sol6917.html' ] ,
@@ -43,121 +44,114 @@ def initialize(info = {})
43
44
] , self . class )
44
45
end
45
46
46
- def change_endianness ( value , size = 4 )
47
+ def change_endianness ( value , size = 4 )
47
48
conversion = nil
48
-
49
49
if size == 4
50
50
conversion = [ value ] . pack ( "V" ) . unpack ( "N" ) . first
51
51
elsif size == 2
52
52
conversion = [ value ] . pack ( "v" ) . unpack ( "n" ) . first
53
53
end
54
-
55
54
conversion
56
55
end
57
56
58
57
def cookie_decode ( cookie_value )
59
- if cookie_value =~ /(\d {8,10})\. (\d {1,5})\. /
60
- host = $1. to_i
61
- port = $2. to_i
58
+ backend = { }
59
+ case
60
+ when cookie_value =~ /(\d {8,10})\. (\d {1,5})\. /
61
+ host = Regexp . last_match ( 1 ) . to_i
62
+ port = Regexp . last_match ( 2 ) . to_i
62
63
host = change_endianness ( host )
63
64
host = Rex ::Socket . addr_itoa ( host )
64
65
port = change_endianness ( port , 2 )
65
- elsif cookie_value . downcase =~ /rd\d +o0{20}f{4}([a-f0-9]{8})o(\d {1,5})/
66
- host = $1 . to_i ( 16 )
67
- port = $2 . to_i
66
+ when cookie_value . downcase =~ /rd\d +o0{20}f{4}([a-f0-9]{8})o(\d {1,5})/
67
+ host = Regexp . last_match ( 1 ) . to_i ( 16 )
68
+ port = Regexp . last_match ( 2 ) . to_i
68
69
host = Rex ::Socket . addr_itoa ( host )
69
- elsif cookie_value . downcase =~ /vi([a-f0-9]{32})\. (\d {1,5})/
70
- host = $1 . to_i ( 16 )
71
- port = $2 . to_i
72
- host = Rex ::Socket . addr_itoa ( host , v6 = true )
70
+ when cookie_value . downcase =~ /vi([a-f0-9]{32})\. (\d {1,5})/
71
+ host = Regexp . last_match ( 1 ) . to_i ( 16 )
72
+ port = Regexp . last_match ( 2 ) . to_i
73
+ host = Rex ::Socket . addr_itoa ( host , true )
73
74
port = change_endianness ( port , 2 )
74
- elsif cookie_value . downcase =~ /rd\d +o([a-f0-9]{32})o(\d {1,5})/
75
- host = $1 . to_i ( 16 )
76
- port = $2 . to_i
77
- host = Rex ::Socket . addr_itoa ( host , v6 = true )
78
- elsif cookie_value =~ /!(.){104}/
75
+ when cookie_value . downcase =~ /rd\d +o([a-f0-9]{32})o(\d {1,5})/
76
+ host = Regexp . last_match ( 1 ) . to_i ( 16 )
77
+ port = Regexp . last_match ( 2 ) . to_i
78
+ host = Rex ::Socket . addr_itoa ( host , true )
79
+ else
79
80
host = nil
80
81
port = nil
81
82
end
82
- back_end = ( host . nil? ) ? nil : "#{ host } :#{ port } "
83
+
84
+ backend [ :host ] = ( host . nil? ) ? nil : host
85
+ backend [ :port ] = ( port . nil? ) ? nil : port
86
+ backend
83
87
end
84
88
85
89
def get_cookie # request a page and extract a F5 looking cookie.
86
90
cookie = { }
87
- res = send_request_raw ( {
88
- 'method' => 'GET' ,
89
- 'uri' => @uri
90
- } )
91
+ res = send_request_raw ( { 'method' => 'GET' , 'uri' => @uri } )
91
92
92
93
unless res . nil?
93
94
# Get the SLB session IDs for all cases:
94
95
# 1. IPv4 pool members - "BIGipServerWEB=2263487148.3013.0000",
95
96
# 2. IPv4 pool members in non-default routed domains - "BIGipServerWEB=rd5o00000000000000000000ffffc0000201o80",
96
97
# 3. IPv6 pool members - "BIGipServerWEB=vi20010112000000000000000000000030.20480",
97
- # 4. IPv6 pool members in non-default route domains - "BIGipServerWEB=rd3o20010112000000000000000000000030o80",
98
- # 5. Encrypted cookies - "BIGipServerWEB=!dcdlUciYEFlt1QzXtD7QKx22XJx7Uuj2I0dYdFTwJASsJyJySME9/GACjztr7WYJIvHxTSNreeve7foossGzKS3vT9ECJscSg1LAc3rc"
98
+ # 4. IPv6 pool members in non-default route domains - "BIGipServerWEB=rd3o20010112000000000000000000000030o80"
99
+
100
+ regexp = /
101
+ ([~_\. \- \w \d ]+)=(((?:\d +\. ){2}\d +)|
102
+ (rd\d +o0{20}f{4}\w +o\d {1,5})|
103
+ (vi([a-f0-9]{32})\. (\d {1,5}))|
104
+ (rd\d +o([a-f0-9]{32})o(\d {1,5})))
105
+ (?:$|,|;|\s )
106
+ /x
99
107
100
- 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 )/ )
108
+ m = res . get_cookies . match ( regexp )
101
109
cookie [ :id ] = ( m . nil? ) ? nil : m [ 1 ]
102
110
cookie [ :value ] = ( m . nil? ) ? nil : m [ 2 ]
103
-
104
- end
105
-
111
+ end
106
112
cookie
107
113
end
108
114
109
115
def run
110
- unless datastore [ 'REQUESTS' ] > 0
111
- print_error ( "Please, configure more than 0 REQUESTS" )
112
- return
113
- end
114
-
115
- back_ends = [ ]
116
+ requests = datastore [ 'REQUESTS' ]
117
+ backends = [ ]
116
118
@uri = normalize_uri ( target_uri . path . to_s )
117
119
print_status ( "#{ peer } - Starting request #{ @uri } " )
118
120
119
- for i in 0 ... datastore [ 'REQUESTS' ]
120
- cookie = get_cookie ( ) # Get the cookie
121
+ ( 1 .. requests ) . each do | i |
122
+ cookie = get_cookie # Get the cookie
121
123
# If the cookie is not found, stop process
122
124
if cookie . empty? || cookie [ :id ] . nil?
123
125
print_error ( "#{ peer } - F5 BigIP load balancing cookie not found" )
124
- break
126
+ return
125
127
end
126
128
127
129
# Print the cookie name on the first request
128
- if i == 0
130
+ if i == 1
129
131
print_status ( "#{ peer } - F5 BigIP load balancing cookie \" #{ cookie [ :id ] } = #{ cookie [ :value ] } \" found" )
130
132
if cookie [ :id ] . start_with? ( 'BIGipServer' )
131
133
print_status ( "#{ peer } - Load balancing pool name \" #{ cookie [ :id ] . split ( 'BIGipServer' ) [ 1 ] } \" found" )
132
134
end
133
135
if cookie [ :value ] . start_with? ( 'rd' )
134
136
print_status ( "#{ peer } - Route domain \" #{ cookie [ :value ] . split ( 'rd' ) [ 1 ] . split ( 'o' ) [ 0 ] } \" found" )
135
137
end
136
- if cookie [ :value ] . start_with? ( '!' )
137
- print_status ( "#{ peer } - F5 BigIP cookie is probably encrypted" )
138
- end
139
138
end
140
139
141
- back_end = cookie_decode ( cookie [ :value ] )
142
- unless back_end . nil? || back_ends . include? ( back_end )
143
- print_status ( "#{ peer } - Backend #{ back_end } found" )
144
- back_ends . push ( back_end )
140
+ backend = cookie_decode ( cookie [ :value ] )
141
+ unless backend [ :host ] . nil? || backends . include? ( backend )
142
+ print_status ( "#{ peer } - Backend #{ backend [ :host ] } : #{ backend [ :port ] } found" )
143
+ backends . push ( backend )
145
144
end
146
145
end
147
146
148
147
# Reporting found backends in database
149
- unless back_ends . empty?
150
- report_note (
151
- :host => rhost ,
152
- :type => "f5_load_balancer_backends" ,
153
- :data => back_ends
154
- )
148
+ unless backends . empty?
149
+ report_note ( host : rhost , type : "f5_load_balancer_backends" , data : backends )
155
150
end
156
151
157
152
rescue ::Rex ::ConnectionError
158
153
print_error ( "#{ peer } - Network connection error" )
159
154
rescue ::OpenSSL ::SSL ::SSLError
160
- print_error ( "#{ peer } - SSL/TLS connection error" )
161
-
155
+ print_error ( "#{ peer } - SSL/TLS connection error" )
162
156
end
163
157
end
0 commit comments