{
	"id": "d0569493-89ad-4f9c-a303-99c699445478",
	"created_at": "2026-04-06T01:32:18.515961Z",
	"updated_at": "2026-04-10T03:21:43.775401Z",
	"deleted_at": null,
	"sha1_hash": "3e29ad9d834698fdc8c3e4d9e373de13264cc612",
	"title": "macOS Gatekeeper Bypass (2021 Edition)",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 680639,
	"plain_text": "macOS Gatekeeper Bypass (2021 Edition)\r\nBy Cedric Owens\r\nPublished: 2021-05-04 · Archived: 2026-04-06 00:26:41 UTC\r\nAbusing the bug in syspolicyd\r\nPreparation: I first ensured that Gatekeeper was enabled and even set it to the most restrictive level on the target macOS\r\nhost. Next I stood up a new host with Mythic C2 (https://github.com/its-a-feature/Mythic), created a new unsigned Mythic\r\nPoseidon golang macho payload, and hosted the payload at http://192.168.1.191:8000/bad-unsigned-macho. Later I will\r\npoint my shell script downloader to pull this unsigned macho payload down.\r\nNormally, macOS apps tend to have a directory structure as follows:\r\n[Name.app]/Contents/MacOS/Name (this is usually a macho with the same name as the app that is executed when the app\r\nis run; since it is a macho, gatekeeper checks to ensure that it has been signed and notarized if it contains the quarantine\r\nattribute — “com.apple.quarantine”). And macOS will append the quarantine attribute to any files downloaded from\r\nbrowsers (macOS also appends this attribute to files downloaded via other methods such AirDrop, but for the sake of this\r\narticle I will not go into those here).\r\nSo this begs the question: what if a script is placed in the Contents/MacOS/ directory instead of a macho, since scripts\r\nare not checked by Gatekeeper? That is essentially what this technique does. Steps are below:\r\nBuild A Simple Shell Script Downloader:\r\nBuild a simple shell script that uses curl to download a payload (ex: unsigned macho binary, jxa payload, python, etc.) to\r\n/tmp , sets the executable bit, and executes that payload. I chose curl because it is known that files pulled down by curl do\r\nnot have the quarantine attribute appended and I chose the /tmp directory because /tmp is not protected by TCC. An example\r\nshell script downloader (down.sh) is below:\r\nThe example shell script above pulls a malicious unsigned macho binary down to tmp, sets the executable bit, and runs it\r\nbackgrounded. Then it displays a message to the user thanking them for installing the fake “IT macOS Provisioner”. Again,\r\nsince curl is used the unsigned macho that is pulled down will not get the quarantine attribute appended (i.e., won’t be\r\nchecked by gatekeeper) and since /tmp is not protected by TCC we can drop payloads there. So now we have our shell script\r\ndownloader payload ready to use.\r\nTurn This Shell Script Into A Double-Clickable “App”:\r\nNext, we will need to put this shell script into the proper format. I did some digging and found that mac admins have\r\nactually been running shell scripts for years inside of app directories to make them “double clickable” so they would not\r\nneed to remember long command line strings when they needed to do a simple task (ex: run a headless browser). In my\r\nsearching, I found a script named appify had been written 11 years ago that basically turned shell scripts into “apps” that\r\ncould be double clicked: https://gist.github.com/mathiasbynens/674099. I use the word “apps” loosely since as I note later\r\nbelow this directory structure is missing some key components of what a real app directory structure contains. So I\r\ndownloaded appify, pointed it at my malicious shell script above and it worked as expected and put my shell script into the\r\nproper macOS app directory structure so it could be double-clicked and run like an app. The example below is me running\r\nappify (renamed as masquerade.sh) to put my malicious shell script (down.sh) into an app named “FakeApp”:\r\nPress enter or click to view image in full size\r\nExample of putting the script into the macOS directory structure, changing it to a clickable “app”\r\nAlternatively, I could have done the same thing manually from Terminal by running:\r\nmkdir -p “FakeApp.app/Contents/MacOS”\r\ncp down.sh FakeApp.app/Contents/MacOS/FakeApp\r\nSo now we have our malicious shell script disguised as an app:\r\nFakeApp.app/Contents/MacOS/FakeApp (note: this is the shell script downloader down.sh)\r\nSomething of note: Apple stated that Info.plist is a required Application Bundle component in their Bundle Structure\r\narticle here:\r\nhttps://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_r\r\nhttps://cedowens.medium.com/macos-gatekeeper-bypass-2021-edition-5256a2955508\r\nPage 1 of 4\n\nCH101-SW1. However, as you can see in our fake app above, Info.plist is not required for macOS to recognize an app\r\n(just the [Name.app]/Contents/MacOS/[Name] app directory structure where Name can be a macho OR a script).\r\nmacOS recognizes this as a valid app:\r\nhttps://cedowens.medium.com/macos-gatekeeper-bypass-2021-edition-5256a2955508\r\nPage 2 of 4\n\nNext, you can pick the app icon from a target app in the /Applications directory. Once you have identified the icon you want\r\nto copy, you Right Click -\u003e Get Info -\u003e Click the icon image -\u003e Edit -\u003e Copy. This will copy the target icon to the\r\nclipboard. Then you can Right Click on your new FakeApp -\u003e Get Info -\u003e Click the icon -\u003e Edit -\u003e Paste. This will\r\npaste the icon onto your fake app to make it look even more appealing:\r\nPress enter or click to view image in full size\r\nNow that your app is ready to go, you can simply put the app inside of a .dmg (or zip), host it, and share the link. To put\r\ninside of a dmg:\r\n1. create a new directory and cp -r FakeApp.app into that new directory\r\n2. Disk Utility -\u003e File -\u003e New Image -\u003e Image From Folder -\u003e select the folder you created in #1 above. This will create\r\na new .dmg that contains your fake app.\r\nNow you can host the .dmg and share the link. At this point, you can test what would happen on a target victim machine by\r\nplacing the .dmg you just created into a folder, cd into that folder, and run “python -m SimpleHTTPServer 80”. In a browser\r\nyou can then navigate to http://127.0.0.1 and download the .dmg (we want to simulate how a targeted user may download\r\nthis payload).\r\nPress enter or click to view image in full size\r\nhttps://cedowens.medium.com/macos-gatekeeper-bypass-2021-edition-5256a2955508\r\nPage 3 of 4\n\nexample of hosted payload inside .dmg\r\nOnce downloaded, the .dmg (and everything in it) will have the com.apple.quarantine attribute set since it was downloaded\r\nvia a browser. You can verify this by running “xattr ~/Downloads/RealApp.dmg”.\r\nshowing that the quarantine attribute is set\r\nNow you can see what would happen on the victim machine:\r\nGet Cedric Owens’s stories in your inbox\r\nJoin Medium for free to get updates from this writer.\r\nRemember me for faster sign in\r\n- double click the downloaded .dmg\r\n- double click the fake app inside of the .dmg\r\n- the shell script payload will run, you will receive a callback on your Mythic C2 server, and you’ll see the fake message to\r\nthe user thanking them for installing the “IT Provisioner Script”. Notice that gatekeeper did not prompt or block this\r\n- you will notice that you have un-sandboxed access at this stage (i.e., you will have access to non-TCC protected folders\r\nsuch as some folders inside of the user’s home directory and /tmp plus any directories that the user has given Terminal\r\naccess to). So even worst case scenario where the user has not granted Terminal full disk access or access to any\r\nfolders, this payload would still be capable of accessing sensitive files in the user’s home directory (ex: ~/.ssh, ~/.aws,\r\netc.).\r\nTo recap, this payload bypassed the following macOS protections:\r\nGatekeeper: I view this payload as a hybrid app and script. It’s an app in the sense that you can double click it and\r\nmacOS views it as an app when you right click -\u003e Get Info on the payload. Yet it’s also shell script in that shell\r\nscripts are not checked by Gatekeeper even if the quarantine attribute is present. The bug in syspolicyd allowed the\r\nunique combination of properties present in this payload to completely bypass Gatekeeper.\r\nApp Transport Security (ATS): ATS usually requires certain Info.plist entries to allow macOS apps to connect to\r\nservers. However, this payload does not even have an Info.plist and ATS is not invoked, even though macOS treats\r\nthe payload as an app.\r\nSource: https://cedowens.medium.com/macos-gatekeeper-bypass-2021-edition-5256a2955508\r\nhttps://cedowens.medium.com/macos-gatekeeper-bypass-2021-edition-5256a2955508\r\nPage 4 of 4\n\n https://cedowens.medium.com/macos-gatekeeper-bypass-2021-edition-5256a2955508    \nCH101-SW1. However, as you can see in our fake app above, Info.plist is not required for macOS to recognize an app\n(just the [Name.app]/Contents/MacOS/[Name]  app directory structure where Name can be a macho OR a script).\nmacOS recognizes this as a valid app:    \n  Page 2 of 4",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://cedowens.medium.com/macos-gatekeeper-bypass-2021-edition-5256a2955508"
	],
	"report_names": [
		"macos-gatekeeper-bypass-2021-edition-5256a2955508"
	],
	"threat_actors": [],
	"ts_created_at": 1775439138,
	"ts_updated_at": 1775791303,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/3e29ad9d834698fdc8c3e4d9e373de13264cc612.pdf",
		"text": "https://archive.orkl.eu/3e29ad9d834698fdc8c3e4d9e373de13264cc612.txt",
		"img": "https://archive.orkl.eu/3e29ad9d834698fdc8c3e4d9e373de13264cc612.jpg"
	}
}