{
	"id": "409530a3-8fd8-4968-be30-f4b6e3cde964",
	"created_at": "2026-04-06T00:07:56.339577Z",
	"updated_at": "2026-04-10T13:12:23.752549Z",
	"deleted_at": null,
	"sha1_hash": "5082f1c394a02ec32816e5d08a0ebbf1f46f13c4",
	"title": "Defeating Malicious Launch Persistence",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1472079,
	"plain_text": "Defeating Malicious Launch Persistence\r\nBy 4n7m4n\r\nPublished: 2021-11-23 · Archived: 2026-04-05 13:31:10 UTC\r\nPress enter or click to view image in full size\r\nA New Mitigation Strategy for the most used macOS Persistence Technique\r\nWhy Launch Persistence Research?\r\nThis research started with me looking for suggestions for my organization on mitigating launch persistence\r\ntechniques for macOS. As a red team operator, sharing knowledge and recommendations with our security\r\norganizations is crucial to making our customers safer.\r\nI took a look at the macOS MITRE ATT\u0026CK matrix for the persistence tactic (TA003), its launch persistence\r\ntechnique which MITRE calls \"Create or Modify System Process\" (T1543), and more specifically, the “Launch\r\nAgent” (T1543.001) and “Launch Daemon” (T1543.004) sub techniques. Collectively I am calling these two sub-techniques “launch persistence.” Patrick Wardle calls launch persistence “launch items.” Still, I want to ensure no\r\nconfusion between these and “login items” or “startup items,” which are entirely different persistence\r\nmechanisms.\r\nMITRE suggested setting group policies for mitigation to block launch persistence behavior. The problem was that\r\nI hadn't heard of any organization doing this, and I could not find any reference for this from MITRE or otherwise.\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 1 of 12\n\nI started asking around, and no one could tell me how any organization does this. So, I decided to figure out how\r\nan enterprise could mitigate launch persistence at scale.\r\nUpon discovering this new mitigation method, I reached out to my friend Jaron Bradley at Jamf Protect to see if\r\nhe knew if my approach was possible using an MDM solution like Jamf Pro. He connected me with\r\n, also from Jamf, who I teamed up with on this blog. Matt describes how to prepare this method using Jamf Pro in\r\nhis section below.\r\nI also reached out to\r\n, macOS lead for MITRE ATT\u0026CK, for some leads in the macOS community. She was a great help in tracking\r\ndown some individuals with experience in macOS administration who I was able to bounce my mitigation idea off\r\nof for a sanity check. Thank you, Cat!\r\nThe first section of this blog will cover persistence, launch agents, and why it is important to mitigate the\r\ntechniques. So, if you already understand this or don’t want/need a refresher, you can skip to the next section.\r\nPersistence\r\nPersistence is a vital tactic in the adversarial kill chain. It is typically the next step after initial access and\r\npayload/malware execution. \"Persistence is the means by which malware ensures that it will be automatically\r\n(re)executed by the operating system on system startup or user (re)login\" (Wardle, 2020, p. 2). Most malware\r\nattempts to gain persistence. Otherwise, a system reboot would kill access to the infected system.\r\nLaunch Persistence and launchd\r\nLaunch persistence includes launch agents and launch daemons. “Launch items are the Apple recommended way\r\nto persist non-application binaries (e.g., software updates, background processes, etc)” (Wardle, 2020, p. 5).\r\nHow does this work? After system boot and during system initialization, launchd searches the\r\n/System/Library/LaunchDaemons/ and the root /Library/LaunchDaemons/ directories for property list files\r\n(plists) and launches the daemon that the plist requests to be running at all times. These are launch daemons.\r\nWhen a user logs in, macOS starts a per-user launchd. This launchd launches the agent requested to be launched\r\non-demand from the property list files found in /System/Library/LaunchAgents , the root\r\n/Library/LaunchAgents , and the user’s ~/Library/LaunchAgents directory. These are launch agents.\r\nProperty Lists (plists)\r\nProperty lists are very important to understand when discussing launch persistence because they describe the\r\nlaunch persistence to launchd. A plist is typically an XML document that contains key/value pairs. A plist can also\r\nbe in JSON format, and in more rare cases a binary. We will just focus on the XML plists for this article.\r\nTo view the contents of a plist in human-readable form use the following commands:\r\nplutil -p \u003cpath to plist\u003e\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 2 of 12\n\ndefaults read \u003cpath to plist\u003e\r\ncat \u003cpath to plist\u003e\r\nExample Property List\r\nIn terms of persistence, the most pertinent key/value pairs include:\r\n1. Key: Program or Program Arguments. Value: Path to launch persistence’s script or binary to execute.\r\n2. Key: RunAtLoad. Value: Contains a boolean that, if set to true, instructs launchd to automatically launch\r\nthe launch persistence. The malware will be persistently restarted by macOS at system reboot at user re-login.\r\n3. Key: Label. Value: The job name. “This key is required for every job definition. It identifies the job and\r\nhas to be unique for the launchd instance. Theoretically, it is possible for an agent to have the same label as\r\na daemon, as daemons are loaded by the root launchd whereas agents are loaded by a user launchd, but it is\r\nnot recommended by convention job names are written in reverse domain notation” (Soma-Zone, What is\r\nLaunchd?). For example com.redteam.evil. For private agents, the domain local is a good choice:\r\nlocal.cleanup.\r\nThese three key/value pairs are all that is needed to create launch persistence.\r\nLaunch Persistence and Malware\r\nTo persist as a launch agent, malware can create a plist in the LaunchAgents directories, and for a launch daemon,\r\nit can do the same in the LaunchDaemons directory. Malware can create its persistence in the same manner as\r\nApple recommends developers create persistence for their legitimate applications.\r\nApple’s own Launch Agents live in the /System/Library/LaunchAgents directory while Launch Daemons live in\r\n/System/Library/LaunchDaemons . “Since the introduction of System Integrity Protection (SIP) in OS X 10.11 (El\r\nCapitan) these OS directories are now protected, therefore malware cannot modify them (i.e., they cannot create a\r\n“system” Launch Persistence). As such, malware is now constrained to creating launch persistence in the /Library\r\nor ~/Library directories” (Wardle, 2020, p. 6).\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 3 of 12\n\nMost macOS malware persists using launch persistence. In 2020, 80% of persistent malware persisted via launch\r\npersistence. According to “The Mac Malware of 2019” report, all persistent malware persisted via launch\r\npersistence. Needless to say, this is a critical technique for organizations to mitigate.\r\nMitigation\r\nTo mitigate the adversarial launch persistence techniques on our home computers, we can use Patrick Wardle of\r\nObjective-See’s Free and Open Sourced tool, BlockBlock. BlockBlock monitors common persistence locations\r\nand alerts whenever a persistent component is added. BlockBlock is a fantastic tool and is excellent for use on\r\nyour personal mac, but it isn’t scalable to the enterprise. We need something that will block an adversary from\r\ncreating a plist in the launch persistence directories.\r\nEnterprise Mitigation\r\nWhat I have found in my research is that we can lock the root-level launch persistence directories\r\n( /Library/Launch(Agents || Daemons) ) using a macOS mobile device management (MDM) solution. Assuming\r\nthat the user of this managed machine does not have access to the local administrative (root) user account on this\r\nmachine, the MDM admin(s) can lock these launch persistence directories using the root account. Now, only the\r\nowner of these directories (root) can unlock them. As it stands, malware, even running as root, would need to\r\nunlock the directory, and then create its launch persistence plist in the unlocked directory. I have not seen macOS\r\nmalware with this bit of sophistication in the wild.\r\nWhat do you mean by “locked?”\r\nFiles and directories on macOS have a flag called the uchg , or user immutable flag. If one sets this flag, then the\r\nfile or directory is “locked.” If set, this immutable bit means that the associated file or folder cannot be changed. If\r\nwe set this user immutable bit on the root launch persistence directories, nothing can add to or remove from the\r\ndirectories. Malware cannot add a plist, which means, without additional actions, no additional launch persistence\r\nfiles can be added.\r\nWe can check if a file/directory has the uchg flag set by listing the file using the ls command with the -O\r\noption:\r\nls -O \u003cpath/to/file/dir\u003e\r\nLauchAgents directory is immutable\r\nWe can set or unset this immutable bit using the default macOS tool, chflags .\r\nTo add the immutable bit:\r\n$ chflags uchg /Library/LauchAgents\r\nTo remove it:\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 4 of 12\n\n$ chflags nouchg /Library/LauchAgents\r\nGet 4n7m4n’s stories in your inbox\r\nJoin Medium for free to get updates from this writer.\r\nRemember me for faster sign in\r\nYou can also change the bit using the file information window, which you can access by pressing command + i .\r\nYou can then check or uncheck the “Locked” check box. This method is not helpful in using an MDM for\r\nscripting the locking at scale but works if you’d like to do it on your personal macOS machine.\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 5 of 12\n\nUnlocked LaunchAgents directory via File Information\r\nWill this break application functionality?\r\nWhen I told my colleagues about this mitigation method, they expressed concerns for legitimate applications that\r\nedit the plists they have created in the launch persistence directories. If an application loses its ability to write to\r\nits plist, this mitigation method might break application functionality. I agreed and did some testing.\r\nIt turns out that with the immutable bit set, I was still able to edit and completely overwrite a plist inside these\r\nlocked directories. So, even though nothing can add or remove a file in the locked launch persistence directories,\r\none can still change the files inside. While this is excellent news for my mitigation method, this seems like\r\nimmutable does not equal immutable. I reported this as a bug to Apple on 11/8/2021 and am awaiting a response.\r\nNote:\r\ninformed me that one could use chflags -R uchg /Library/LauchAgents ( -R option) and this will recursively\r\nlock the directory and all of its contents. However, because many services frequently update their plists this option\r\nis not ideal in that it may break service/application functionality.\r\nWhat about the USER LaunchAgents directory?\r\nSo, the local user account doesn’t own the root launch persistence directories. Therefore it cannot unlock the\r\nfolder. Still, since the user owns the /Users/\u003cusername\u003e/Library/LaunchAgents directory, they can just unlock it,\r\nnegating the mitigation in userland. This user LaunchAgent directory might not be necessary for an enterprise\r\nenvironment. Most approved software that I’ve seen lately in an enterprise environment doesn’t need to install\r\npersistence here, so we need a way to lock it down permanently.\r\nOur MDM solution can create an unprivileged user account on each managed macOS system. Using this\r\nunprivileged administrative account, the MDM administrator can create the LaunchAgent directory for our real\r\nuser and change ownership of the directory to the unprivileged administrative user account. The MDM\r\nadministrator can then lock the directory. Now the actual user cannot unlock the directory because they, again, do\r\nnot own it.\r\nThis new unprivileged administrative user can be considered a honey-user account. Defense should monitor it, and\r\nany attempts to log in to the account should create an alert and probably be regarded as malicious.\r\nHow do we do this at scale?\r\nApplications only need to add/create a plist to the root launch persistence directories at install time. If your IT\r\nadministrators manage installations, they can install approved software using an MDM solution with scripting\r\ncapabilities.\r\nTo explain how an administrator can configure this in an enterprise environment, I have teamed up with\r\nfrom Jamf.\r\nProtecting Launch Directories with Jamf Pro\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 6 of 12\n\nThe following is an example of how an admin might use Jamf Pro to lock the LaunchDaemon and LaunchAgent\r\ndirectories across a macOS fleet. This workflow highlights key Jamf Pro features, including scripting via the Jamf\r\nPro binary, reporting and monitoring via custom inventory attributes, and scoping actions with a dynamic Smart\r\nComputer Group.\r\nReporting on uchg Status\r\nThe first step of the Jamf Pro workflow is to be able to determine whether the launch persistence directories have\r\nbeen uchg locked. We do this by creating a Jamf Pro Extension Attribute that is updated during the routine Jamf\r\nPro inventory update. This custom inventory attribute checks the uchg status of the launch persistence\r\ndirectories and updates each computer’s inventory record.\r\nAs Anthony demonstrated, you can see the uchg status by listing the file or directory using the ls command with\r\nthe -lO option: ls -lO \u003cpath/to/file/dir\u003e .\r\nIn this example, we have created a custom Extension Attribute called “Launch Persistence Directories”. The\r\nExtension Attributes work by running a snippet of bash script that checks the uchg status of each launch\r\npersistence directory location and returns the result.\r\nPress enter or click to view image in full size\r\nWe can check the flags of a file or directory by running ls with the -Ol flag. We can then grab the uchg flag\r\nstatus by using awk to capture the fifth column, put that output into variables, and then test the variable values\r\nfor the substring uchg .\r\nPress enter or click to view image in full size\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 7 of 12\n\nThe image below is how the result of this Extension Attribute appears in the computer inventory record:\r\nPress enter or click to view image in full size\r\nOnce we can report on the uchg status of the Launch directories, we can then use that attribute as the basis for a\r\nSmart Group to scope our policy. First, we give the Smart Group an identifying name. Then we define the criteria\r\nfor this group as any computer that contains the word unlocked in the Launch Persistence Directories inventory\r\nfield.\r\nPress enter or click to view image in full size\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 8 of 12\n\nWriting the Script\r\nNext, we need to write the script that locks the launch persistence directories. Since the\r\n~/Library/LaunchAgents/ directory does not exist by default, in this script, we check for its existence and create\r\nit if not present. If the user is a standard user, Antonio recommends creating an unprivileged user account on each\r\nmachine and changing ownership of the ~/Library/LaunchAgents to this stand-in account. This avoids having\r\nthe directory owned by root and prevents the local user from simply unlocking their directory as its owner.\r\nLastly, we use chflags uchg to lock each directory.\r\nPress enter or click to view image in full size\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 9 of 12\n\nCrafting the Policy\r\nFinally, we create a policy that ties all of these elements together. After giving the policy an appropriate name, we\r\nset the policy to be triggered at the recurring check-in (every 15 minutes by default) on any computer that is in\r\nscope.\r\nPress enter or click to view image in full size\r\nAnd for the scope, we set the policy to run against any computer that falls into the “Launch Directories Unlocked”\r\nSmart Group that we previously created.\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 10 of 12\n\nPress enter or click to view image in full size\r\nAs for the Policy payloads, we will configure the Scripts payload and add our Lock Launch Directories script.\r\nPress enter or click to view image in full size\r\nWe also add a secondary Maintenance payload to force an inventory update, to immediately reflect this change in\r\nthe computer inventory record and remove the computer from the “Launch Directories Unlocked” Smart\r\nComputer Group.\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 11 of 12\n\nPress enter or click to view image in full size\r\nWhen the need arises to install a legitimate Launch Daemon or Launch Agent, an admin should run the chflags\r\nnouchg in a preinstall script to remove the lock and then re-enable the flag in a post-install script.\r\nTo Summarize\r\nWe can lock the root-level launch persistence directories using an MDM solution with scripting capabilities, like\r\nJamf Pro. An administrator would only unlock it when installing approved software. They would then relock it\r\nafter install.\r\nThe administrator can create a honey-user account for each managed macOS machine and change the owner of the\r\nuser-level LaunchAgents directory to this honey-user. The admin can then lock the user-level directory.\r\nI hope this mitigation strategy works for your enterprise environment. I’d love to hear from anyone who gives it a\r\ntry. Contact me via my antman1p Twitter.\r\nAs always, thanks for reading, and stand by for more sauce!\r\nSource: https://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nhttps://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67\r\nPage 12 of 12",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://antman1p-30185.medium.com/defeating-malicious-launch-persistence-156e2b40fc67"
	],
	"report_names": [
		"defeating-malicious-launch-persistence-156e2b40fc67"
	],
	"threat_actors": [],
	"ts_created_at": 1775434076,
	"ts_updated_at": 1775826743,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/5082f1c394a02ec32816e5d08a0ebbf1f46f13c4.pdf",
		"text": "https://archive.orkl.eu/5082f1c394a02ec32816e5d08a0ebbf1f46f13c4.txt",
		"img": "https://archive.orkl.eu/5082f1c394a02ec32816e5d08a0ebbf1f46f13c4.jpg"
	}
}