Beyond good ol? LaunchAgent - part 0

Published: 2018-10-21
Last Updated: 2018-10-21 15:21:02 UTC
by Pasquale Stirparo (Version: 1)
0 comment(s)

First and foremost, let me start with a disclaimer. As probably most of you may have noticed the similarity, the title of this post, which I hope to be the first of a series, is inspired by the great and amazing work from Adam (@Hexacorn) [1] “Beyond good ol’ Run Key” (which, as of today, reached episode 93!!!), where he writes about all Windows persistence mechanisms he comes across/discovers in his research, which are, as the title suggest, much more than just the Run registry key we all love. In the rare case you have not read it yet, you should asap. Really.

Having said that, my motivation behind this is the intention to focus more on macOS analysis/research and macOS internals [2][3] (J. Levin’s book is The bible, if you have to pick one place where to start from on anything macOS related, there you go), and also because I’m a documentation maniac, I like to write things down, ideally in one location, to easily retrieve them when needed. This is a way to document and share my findings about macOS persistence mechanisms. And I could not but start with the most common mechanisms (hence part 0): LaunchAgents and LaunchDaemons.

In the macOS boot/startup process, immediately after the kernel initialization phase there is launchd. Launchd is “The process”. It is started directly by the kernel and is the first process “appearing” in user mode. It is responsible, among other things, for initializing and scheduling all system services and processes, and so for launching Agents and Daemons.

Both Agents and Daemons are described in their respective property list (.plist) files, containing the instructions of how and when they have to be launched. Daemons are system services, and are started in the boot process before any user logs in. Daemons may be created with administrator privileges, but are executed under root privileges, so an adversary may also use a service to escalate privileges from administrator to root. Daemons plist files are located at the following paths [4]:

  • /System/Library/LaunchDaemons/
    This is the location for Apple specific Daemon. This is a restricted location and is mounted as read-only.
  • /Library/LaunchDaemons/
    This for all third-party daemons, therefore not restricted as the previous one (but requires root permission).

Agents, instead, are user’s services/processes and are started only after user logs in. When a user logs in, a per-user launchd process is started which loads the parameters for each launch-on-demand user agent from the property list (plist) files located at the following paths [4]:

  • /System/Library/LaunchAgents/
    As for the Daemons, this location is for Apple specific Agents and its access is restricted.
  • /Library/LaunchAgents/
    Again, as for Daemons, this folder is for third party Agents.
  • ~/Library/LaunchAgents/
    This folder contains the user installed Agents and are loaded by the user level launchd process. Because those are executed as soon as that specific user logs in and do not require administrator privileges, you can easily imagine why this is the (most) favourite malware persistence location.

Agents and Daemons plist files are just like any other property list file in the Apple universe, therefore you can easily review them with plutil -u from command line or use Xcode if you prefer GUI based. The following are some of the keywords of interest you may want to look for when analyzing a suspicious one.

Label: This key is required as it identifies the agent/daemon and has to be unique for the launchd instance (i.e. two agents or two daemons cannot have the same label, but an agent and a daemon can, since daemons are loaded by the root launchd whereas agents are loaded by a user launchd). This is typically the file name.

Program: This key defines actually what has to be run, the path to the specific binary/script.
ProgramArguments: This is also quite self explicative, and is defined as an array of arguments to be passed to the “Program” when launched

As you may have noticed, the name of the first argument is again the program itself. This is important to keep in mind as the first item will not be the first argument, much like argv[0]. If both keys are used, the value of Program is the executable to be launched and the first string in ProgramArguments will be ignored by launchd.

Additional Keys of interest, which will define when the agent/daemon is to be run, are:
RunAtLoad: This key specifies that it has to be run right after it has been loaded, i.e. at boot time for daemons, and after user logs in for agents.

WatchPaths: launchd will start the program if the provided path is modified. If path points to a folder, modifying the folder or any of its content will trigger, as well as any modification to a file if path points to a specific one.

StartOnMount: The program is started whenever a file system is successfully mounted, i.e. a CD/DVD, USB drive, SD Card, etc. (haven’t checked with network drive to be honest, but I expect so).

Start[Calendar]Interval: StartInterval will tell launchd to start your program every n seconds, while StartCalendarInterval will tell to schedule the execution every day at a specific hour for example. This is pretty much like the cronjob in classic *NIX systems. The following example will tell launchd to run the program every monday morning at 7am:


KeepAlive: this is an important “control” key, which tells launchd to keep the job alive under certain specific conditions, specified by several sub-keys. If no subkey is provided, the message is basically “always”, so launchd will bring the job back running in any case if it would crash (or be terminated). Some of the interesting sub-keys are:

  • NetworkState: if set to True, it will run the program as soon as a network connection becomes available.
  • SuccessfulExit: This key will take into account the exit code of the program in case of termination. If set to True, the program will be restarted until it fails. If set to False, it will restart the program every time it will terminate in a non “successful” way, i.e. exit code different from zero.
  • Crashed: If set to True, the program will be restarted after it crashed. If set to false, it will restart the program unless it has crashed.

Disabled/Enabled: Last but not least, you can still have a plist file in the Agents/Daemons folder, but that may not be executed. The meaning of these two keys is quite self explanatory.

If you are interested to know more about all possible keys available for launchd related plist files, “Launchd Info” website is quite a great resource for this [5]. As simple and basic as it can be, specifically setting a LaunchAgents, this mechanism is quite popular also among those malware that are used by allegedly sophisticated attackers. Some recent and interesting examples are Komplex Trojan, allegedly attributed to Sofacy [6]. It sets its persistency as the following LaunchAgents, $HOME/Library/LaunchAgents/, running as soon as the user logged in, no matter what, as specified in the following configuration:




Other known example where LaunchAgents have been used as persistence are Careto [7] (~/Library/LaunchAgents/ and the infamous RCS suite implant from HackingTeam [8] (~/Library/LaunchAgents/

I plan to add all my findings from this journey into macOS persistency as well into the mac4n6 artifacts collection I maintain [4]. Please let me know if you have encountered any particular use of LaunchAgents/Daemons in your analysis, and of course if you see any incorection on this post :).

Finally, if you are interested in macOS security/forensics/research, I’m leaving you with a list of people (and their blogs/books) in the reference section (I love references, hope you don’t mind) that are my main source of information when it come to macOS, you may want to check them out and follow those people [2][3][9][10][11][12].

Happy Hunting,


[1] - Adam (@Hexacorn), “Beyond good ol’ Run Key”,
[2] - Jonathan Levin (@Technologeeks), “*OS Internals”, 
[3] - Jonathan Levin (@Technologeeks), “Mac OS X and iOS Internals - To the Apple’s Core”,
[4] - Pasquale Stirparo (@pstirparo), “mac4n6 Artifacts Project”,
[5] - “Launchd Info”,
[6] - Unit42, “Sofacy’s ‘Komplex’ OS X Trojan”,
[7] - Kaspersky, “Unveiling Careto - The Masked APT”,
[8] - Partick Warlde (@patrickwardle), “HackingTeam Reborn; A Brief Analysis of an RCS Implant Installer”,
[9] - Patrick Warlde (@patrickwardle), “Objective See”, 
[10] - Sarah Edwards (@iamevltwin), “Mac4n6 Blog”,
[11] - Pedro Vilaça (@osxreverser), “Reverse Engineering Mac OS X”,
[12] - MITRE ATT&CK (@MITREattack), “MacOS Techniques”,


Pasquale Stirparo, Ph.D.


0 comment(s)


Diary Archives