hacktricks/macos-hardening/macos-auto-start-locations.md

14 KiB
Raw Blame History

macOS Auto Start Locations

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Here are locations on the system that could lead to the execution of a binary without user interaction.

Launchd

launchd is the first process executed by OX S kernel at startup and the last one to finish at shut down. It should always have the PID 1. This process will read and execute the configurations indicated in the ASEP plists in:

  • /Library/LaunchAgents: Per-user agents installed by the admin
  • /Library/LaunchDaemons: System-wide daemons installed by the admin
  • /System/Library/LaunchAgents: Per-user agents provided by Apple.
  • /System/Library/LaunchDaemons: System-wide daemons provided by Apple.

When a user logs in the plists located in /Users/$USER/Library/LaunchAgents and /Users/$USER/Library/LaunchDemons are started with the logged users permissions.

The main difference between agents and daemons is that agents are loaded when the user logs in and the daemons are loaded at system startup (as there are services like ssh that needs to be executed before any user access the system). Also agents may use GUI while daemons need to run in the background.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN">
<plist version="1.0">
<dict>
    <key>Label</key>
        <string>com.apple.someidentifier</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/username/malware</string>
    </array>
    <key>RunAtLoad</key><true/> <!--Execute at system startup-->
    <key>StartInterval</key>
    <integer>800</integer> <!--Execute each 800s-->
    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key></false> <!--Re-execute if exit unsuccessful-->
        <!--If previous is true, then re-execute in successful exit-->
    </dict>
</dict>
</plist>

There are cases where an agent needs to be executed before the user logins, these are called PreLoginAgents. For example, this is useful to provide assistive technology at login. They can be found also in /Library/LaunchAgents(see here an example).

{% hint style="info" %} New Daemons or Agents config files will be loaded after next reboot or using launchctl load <target.plist> It's also possible to load .plist files without that extension with launchctl -F <file> (however those plist files won't be automatically loaded after reboot).
It's also possible to unload with launchctl unload <target.plist> (the process pointed by it will be terminated),

To ensure that there isn't anything (like an override) preventing an Agent or Daemon from running run: sudo launchctl load -w /System/Library/LaunchDaemos/com.apple.smdb.plist {% endhint %}

List all the agents and daemons loaded by the current user:

launchctl list

Cron

List the cron jobs of the current user with:

crontab -l

You can also see all the cron jobs of the users in /usr/lib/cron/tabs/ and /var/at/tabs/ (needs root).

In MacOS several folders executing scripts with certain frequency can be found in:

ls -lR /usr/lib/cron/tabs/ /private/var/at/jobs /etc/periodic/

There you can find the regular cron jobs, the at jobs (not very used) and the periodic jobs (mainly used for cleaning temporary files). The daily periodic jobs can be executed for example with: periodic daily.

The periodic scripts (/etc/periodic) are executed because of the launch daemons configured in /System/Library/LaunchDaemons/com.apple.periodic*. Note that if a script is stored in /etc/periodic/ as a way to escalate privileges, it will be executed as the owner of the file.

ls -l /System/Library/LaunchDaemons/com.apple.periodic*
-rw-r--r--  1 root  wheel  887 May 13 00:29 /System/Library/LaunchDaemons/com.apple.periodic-daily.plist
-rw-r--r--  1 root  wheel  895 May 13 00:29 /System/Library/LaunchDaemons/com.apple.periodic-monthly.plist
-rw-r--r--  1 root  wheel  891 May 13 00:29 /System/Library/LaunchDaemons/com.apple.periodic-weekly.plist

kext

In order to install a KEXT as a startup item, it needs to be installed in one of the following locations:

  • /System/Library/Extensions
    • KEXT files built into the OS X operating system.
  • /Library/Extensions
    • KEXT files installed by 3rd party software

You can list currently loaded kext files with:

kextstat #List loaded kext
kextload /path/to/kext.kext #Load a new one based on path
kextload -b com.apple.driver.ExampleBundle #Load a new one based on path
kextunload /path/to/kext.kext
kextunload -b com.apple.driver.ExampleBundle

For more information about kernel extensions check this section.

Login Items

In System Preferences -> Users & Groups -> Login Items you can find items to be executed when the user logs in.
It it's possible to list them, add and remove from the command line:

#List all items:
osascript -e 'tell application "System Events" to get the name of every login item'

#Add an item:
osascript -e 'tell application "System Events" to make login item at end with properties {path:"/path/to/itemname", hidden:false}' 

#Remove an item:
osascript -e 'tell application "System Events" to delete login item "itemname"' 

These items are stored in the file /Users/<username>/Library/Application Support/com.apple.backgroundtaskmanagementagent

At

“At tasks” are used to schedule tasks at specific times.
These tasks differ from cron in that they are one time tasks that get removed after executing. However, they will survive a system restart so they cant be ruled out as a potential threat.

By default they are disabled but the root user can enable them with:

sudo launchctl load -F /System/Library/LaunchDaemons/com.apple.atrun.plist

This will create a file at 13:37:

echo hello > /tmp/hello | at 1337

If AT tasks aren't enabled the created tasks won't be executed.

Login/Logout Hooks

They are deprecated but can be used to execute commands when a user logs in.

cat > $HOME/hook.sh << EOF
#!/bin/bash
echo 'My is: \`id\`' > /tmp/login_id.txt
EOF
chmod +x $HOME/hook.sh
defaults write com.apple.loginwindow LoginHook /Users/$USER/hook.sh

This setting is stored in /Users/$USER/Library/Preferences/com.apple.loginwindow.plist

defaults read /Users/$USER/Library/Preferences/com.apple.loginwindow.plist
{
    LoginHook = "/Users/username/hook.sh";
    MiniBuddyLaunch = 0;
    TALLogoutReason = "Shut Down";
    TALLogoutSavesState = 0;
    oneTimeSSMigrationComplete = 1;
}

To delete it:

defaults delete com.apple.loginwindow LoginHook

In the previous example we have created and deleted a LoginHook, it's also possible to create a LogoutHook.

The root user one is stored in /private/var/root/Library/Preferences/com.apple.loginwindow.plist

Emond

Apple introduced a logging mechanism called emond. It appears it was never fully developed, and development may have been abandoned by Apple for other mechanisms, but it remains available.

This little-known service may not be much use to a Mac admin, but to a threat actor one very good reason would be to use it as a persistence mechanism that most macOS admins probably wouldn't know to look for. Detecting malicious use of emond shouldn't be difficult, as the System LaunchDaemon for the service looks for scripts to run in only one place:

ls -l /private/var/db/emondClients

{% hint style="danger" %} As this isn't used much, anything in that folder should be suspicious {% endhint %}

Startup Items

{% hint style="danger" %} This is deprecated, so nothing should be found in the following directories. {% endhint %}

A StartupItem is a directory that gets placed in one of these two folders. /Library/StartupItems/ or /System/Library/StartupItems/

After placing a new directory in one of these two locations, two more items need to be placed inside that directory. These two items are a rc script and a plist that holds a few settings. This plist must be called “StartupParameters.plist”.

{% tabs %} {% tab title="StartupParameters.plist" %}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Description</key>
        <string>This is a description of this service</string>
    <key>OrderPreference</key>
        <string>None</string> <!--Other req services to execute before this -->
    <key>Provides</key>
    <array>
        <string>superservicename</string> <!--Name of the services provided by this file -->
    </array>
</dict>
</plist>

{% endtab %}

{% tab title="superservicename" %}

#!/bin/sh
. /etc/rc.common

StartService(){
    touch /tmp/superservicestarted
}

StopService(){
    rm /tmp/superservicestarted
}

RestartService(){
    echo "Restarting"
}

RunService "$1"

{% endtab %} {% endtabs %}

/etc/rc.common

{% hint style="danger" %} This isn't working in modern MacOS versions {% endhint %}

It's also possible to place here commands that will be executed at startup. Example os regular rc.common script:

#
# Common setup for startup scripts.
#
# Copyright 1998-2002 Apple Computer, Inc.
#

######################
# Configure the shell #
######################

#
# Be strict
#
#set -e
set -u

#
# Set command search path
#
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/libexec:/System/Library/CoreServices; export PATH

#
# Set the terminal mode
#
#if [ -x /usr/bin/tset ] && [ -f /usr/share/misc/termcap ]; then
#    TERM=$(tset - -Q); export TERM
#fi

###################
# Useful functions #
###################

#
# Determine if the network is up by looking for any non-loopback
# internet network interfaces.
#
CheckForNetwork()
{
    local test

    if [ -z "${NETWORKUP:=}" ]; then
	test=$(ifconfig -a inet 2>/dev/null | sed -n -e '/127.0.0.1/d' -e '/0.0.0.0/d' -e '/inet/p' | wc -l)
	if [ "${test}" -gt 0 ]; then
	    NETWORKUP="-YES-"
	else
	    NETWORKUP="-NO-"
	fi
    fi
}

alias ConsoleMessage=echo

#
# Process management
#
GetPID ()
{
    local program="$1"
    local pidfile="${PIDFILE:=/var/run/${program}.pid}"
    local     pid=""

    if [ -f "${pidfile}" ]; then
	pid=$(head -1 "${pidfile}")
	if ! kill -0 "${pid}" 2> /dev/null; then
	    echo "Bad pid file $pidfile; deleting."
	    pid=""
	    rm -f "${pidfile}"
	fi
    fi

    if [ -n "${pid}" ]; then
	echo "${pid}"
	return 0
    else
	return 1
    fi
}

#
# Generic action handler
#
RunService ()
{
    case $1 in
      start  ) StartService   ;;
      stop   ) StopService    ;;
      restart) RestartService ;;
      *      ) echo "$0: unknown argument: $1";;
    esac
}

Profiles

Configuration profiles can force a user to use certain browser settings, DNS proxy settings, or VPN settings. Many other payloads are possible which make them ripe for abuse.

You can enumerate them running:

ls -Rl /Library/Managed\ Preferences/

Other persistence techniques and tools

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥