macOS Auto Start Locations

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


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">
<key>RunAtLoad</key><true/> <!--Execute at system startup-->
<integer>800</integer> <!--Execute each 800s-->
<key>SuccessfulExit</key></false> <!--Re-execute if exit unsuccessful-->
<!--If previous is true, then re-execute in successful exit-->
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/ {% endhint %}
List all the agents and daemons loaded by the current user:
launchctl list


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/*. 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/*
-rw-r--r-- 1 root wheel 887 May 13 00:29 /System/Library/LaunchDaemons/
-rw-r--r-- 1 root wheel 895 May 13 00:29 /System/Library/LaunchDaemons/
-rw-r--r-- 1 root wheel 891 May 13 00:29 /System/Library/LaunchDaemons/


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 #Load a new one based on path
kextunload /path/to/kext.kext
kextunload -b
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/


“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 can’t 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/
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/ << EOF
echo 'My is: \`id\`' > /tmp/login_id.txt
chmod +x $HOME/
defaults write LoginHook /Users/$USER/
This setting is stored in /Users/$USER/Library/Preferences/
defaults read /Users/$USER/Library/Preferences/
LoginHook = "/Users/username/";
MiniBuddyLaunch = 0;
TALLogoutReason = "Shut Down";
TALLogoutSavesState = 0;
oneTimeSSMigrationComplete = 1;
To delete it:
defaults delete 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/


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
As this isn't used much, anything in that folder should be suspicious

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”.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "">
<plist version="1.0">
<string>This is a description of this service</string>
<string>None</string> <!--Other req services to execute before this -->
<string>superservicename</string> <!--Name of the services provided by this file -->
. /etc/rc.common
touch /tmp/superservicestarted
rm /tmp/superservicestarted
echo "Restarting"
RunService "$1"


This isn't working in modern MacOS versions
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
# Useful functions #
# Determine if the network is up by looking for any non-loopback
# internet network interfaces.
local test
if [ -z "${NETWORKUP:=}" ]; then
test=$(ifconfig -a inet 2>/dev/null | sed -n -e '/' -e '/' -e '/inet/p' | wc -l)
if [ "${test}" -gt 0 ]; then
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."
rm -f "${pidfile}"
if [ -n "${pid}" ]; then
echo "${pid}"
return 0
return 1
# Generic action handler
RunService ()
case $1 in
start ) StartService ;;
stop ) StopService ;;
restart) RestartService ;;
* ) echo "$0: unknown argument: $1";;


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