{
	"id": "516428d4-bdd2-4b23-a9c2-914c979446a1",
	"created_at": "2026-04-06T00:15:28.362132Z",
	"updated_at": "2026-04-10T03:20:19.439381Z",
	"deleted_at": null,
	"sha1_hash": "0a069504b66092b3fb02e1014ddca5c74fa6324c",
	"title": "How to thwart application bundle manipulation on macOS",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 147983,
	"plain_text": "How to thwart application bundle manipulation on macOS\r\nBy susannah.matt@redcanary.com\r\nArchived: 2026-04-05 14:51:30 UTC\r\nIn the world of macOS, apps are, for all intents and purposes, packaged as application bundles. Traditionally,\r\napplication bundles have been very loosely defined—and one could manipulate them to subvert core macOS\r\nsecurity controls. In this context, a malicious actor could gain capabilities like privilege escalation and defense\r\nevasion. In this article, we’ll break down two forms of application bundle manipulation:\r\ntrojanizing legitimate application bundles for the purpose of subverting macOS security controls\r\ntampering with a legitimate application bundle on disk for the purpose of defense evasion and/or privilege\r\nescalation\r\nWhat kind of apps are being targeted?\r\nSubversion on this level has the potential to impact one of macOS’s first layers of defense: Gatekeeper. In fact,\r\nthis has become such a problem that developers have begun to point this out in Apple’s own developer forums. In\r\nthis thread, two developers discuss the inherent inability of macOS to protect their applications against tampering\r\n(as of Monterey). Unfortunately this has led to many developers’ apps being pirated or abused.\r\nHistorically, legitimate apps have been the target of adversaries and software pirates alike. Software pirates are\r\nindividuals who illicitly distribute applications like Microsoft Office, popular games (e.g., Fortnite), and\r\nproductivity software (e.g, Adobe Photoshop) free of charge and usually over peer-to-peer services. Additionally,\r\nif a user downloads a pirated application that has also been trojanized, they run the risk of infecting themselves.\r\nUnfortunately, this is not an uncommon scenario, as seen with the EvilQuest/ThiefQuest, Shlayer and Silver\r\nToucan/UpdateAgent/WizardUpdate malware families (just to name a few), which frequently leverage\r\nmasquerading tactics to hide their true nature.\r\nPirated and trojanized applications frequently abuse legitimate application bundles to subvert core macOS security\r\ncontrols. However, they do not explicitly exhibit the characteristics of adversarial on-disk application bundle\r\nmanipulation. For that, we’ll need to take a look at a slightly more advanced malware family called XCSSET. But\r\nfirst, let’s examine some of macOS’s native security controls.\r\nRelevant macOS security controls\r\nBriefly, some relevant core security controls to mention here are:\r\nSystem Integrity Protection (SIP) is designed to restrict modification of protected system files and\r\ndirectories from the root user. Only entitled macOS processes have the privilege to modify them. This is\r\nessentially Apple’s in-house version of Security Enhanced Linux (SELinux). This is the first of two\r\nruntime protection mechanisms enforced by macOS.\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 1 of 10\n\nHardened Runtime, the second runtime protection mechanism, helps protect apps against attacks like\r\ncode injection and memory space tampering. This is a capability applied by developers when building their\r\napps.\r\nCodesigning/Notarization determines if an application is who it says it is and has been checked by Apple\r\nto be free from malware. Note though that these are both independent procedures.\r\nGatekeeper validates the integrity of notarized application launches (through static analysis, codesigning,\r\netc), protects against tampering, and can launch applications from a randomized path (called Gatekeeper\r\nPath Randomization (GPR) / App Translocation) to prevent malicious plugins from loading. Additionally,\r\nin macOS 13, Gatekeeper will also help prevent unauthorized app tampering (see our Gatekeeper overview\r\nbelow).\r\nXProtect comprises YARA signature-based malware detection (and remediation with the advent of\r\nXProtect Remediator in macOS 13). XProtect runs a scan when:\r\nan app is opened for the first time\r\nan app been modified on disk\r\nXProtect signatures have been updated\r\nTransparency, Consent, and Control (TCC) are privacy protections controlling access to things like full\r\ndisk access, automation, microphone, camera, etc.\r\nWhat’s the problem?\r\nPrior to macOS 13 Ventura, the integrity of applications launched by the user was not always validated. In\r\nsummary, the following checks would occur when a user launched a notarized application for the first time:\r\nWas the app downloaded from the internet? If so, a Gatekeeper first launch prompt will be shown and\r\nthe user will be alerted that the application was downloaded from the internet. In practice, this means that\r\nmacOS has “quarantined” the app. Side note: This is an opt-in process for developers. In other words, a\r\nfile downloaded by any given app is only quarantined if the app’s developer has opted into Launch\r\nServices File Quarantine Enabled ( LSFileQuarantineEnabled ).\r\nIs the app validly signed? This check is done to validate the application is who it says it is and ensure it’s\r\nfree of tampering.\r\nIs the app safe/free from known malware? This check is performed by XProtect YARA scanning.\r\nIf so, Gatekeeper will allow the application to launch.\r\nOtherwise, Gatekeeper will block execution of the application only on first launch.\r\nThe crux of the problem here is that app integrity is only being validated on first launch and there are no\r\nadditional security controls to protect against tampering. This means that an adversary could potentially design\r\nmalware to abuse the gap in application integrity validation, as exhibited by the XCSSET malware family. Apple\r\nhas since addressed this issue by enabling Ventura to deny unauthorized app “updates” and tampering requests.\r\nThis feature was previewed and discussed in the WWDC22 session: What’s new in privacy (see our Gatekeeper\r\noverview below).\r\nWhich malware families manipulate application bundles?\r\nThe following malware families manipulate application bundles in some form. This is not an exhaustive list:\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 2 of 10\n\nXCSSET drops a malicious applet into a legitimate app’s (known as the donor) .../Contents/MacOS/\r\ndirectory. This is an explicit case of an adversary leveraging on-disk application bundle manipulation.\r\nShlayer* has been observed armed with the Gatekeeper bypass: CVE-2021–30657. The elegant logic flaw\r\nhere discovered by Cedric Owens enabled an adversary to bypass Gatekeeper simply by utilizing an\r\nexecutable script in the place of a Mach-O within an application bundle.\r\nEvilQuest / Silver Toucan* are examples of application bundles which have been cracked/modified and\r\ncan masquerade as popular macOS apps like: Ableton Live, Little Snitch, and MacSnipper. Silver Toucan\r\nhas been observed bypassing Gatekeeper.\r\n* Please note, the activity observed from these families does not explicitly tie them into our definition of\r\nadversarial on-disk application bundle manipulation. However, prior to landing on a Mac they come with a\r\nmodified installer, application bundle, or payload for the purpose of bypassing macOS security controls. This is\r\nsomewhat expected behavior of cracked/modified applications like EvilQuest and Silver Toucan.\r\nWhy is this a potentially attractive path for adversaries?\r\nBecause modern macOS supports a wide array of avenues for code execution, the current security controls in\r\nplace may have more work on their plates then they previously realized. Apps are just one way to get code to\r\nexecute on macOS; scripts and Java archives can also get the job done. Importantly, Apple does not currently\r\nnotarize all forms of executables. Supported deliverables are: apps, (non-app bundles: Frameworks and\r\nLoadables), UDIF Disk Images, and Flat installer packages. Unlike application bundles and Mach-O executables,\r\nsecurity controls such as entitlements, codesigning, and hardened runtime do not apply to shell scripts because\r\nthey do not make sense in this context. Entitlements are only able to be added to executables; this means that the\r\nshared libraries, frameworks, and in-process plug-ins inherit all the entitlements of the hosting executable.\r\nEvilQuest’s authors have been known to run scripts as part of an app install. The types of scripts you’re able to run\r\ndepends on the type of installer package: bundle or flat installer. This translates to preflight/install and\r\npostflight/install scripts.\r\nThe Gatekeeper logic flaw we mentioned earlier was enabled by Gatekeeper insufficiently validating the structure\r\nand content of the application bundle (appearing with a script executable instead of a more traditional Mach-O\r\nbinary). Tricking macOS into executing code within the context of a legitimate app is the name of the game for an\r\nadversary here. Why? It could enable some nice capabilities:\r\nDefense evasion: The ability of a malicious program to hide its activity. In this case, malware like\r\nXCSSET latches onto a legitimate donor app.\r\nPrivilege escalation: The malicious program gains elevated permissions to, for example, record or capture\r\nthe screen, steal information from targeted apps, modify software updates, etc. This capability was also\r\nfound in XCSSET.\r\nWhat’s the solution?\r\nmacOS needs a way to ensure that an application is who it says it is, all the time. Gatekeeper is one of the core\r\nsecurity controls associated with ensuring an application has not been compromised. Below we will take a brief\r\nlook at what Gatekeeper is and the upcoming changes in macOS 13 you should be aware of.\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 3 of 10\n\nGatekeeper\r\nA key layer of the macOS security model, Gatekeeper’s job is to only allow trusted application launches. It does\r\nthis on the first launch of notarized apps through validating the requesting app’s cryptographic signature and a\r\nnotarization ticket, if one exists. Notarization tickets can be “stapled” to the app and/or stored on Apple’s internet\r\naccessible servers. In System Settings (or via mobile device management with additional options), Gatekeeper’s\r\nsecurity policy “Allow apps downloaded from” can be modified to take on the following values:\r\n“App Store”\r\n“App Store and identified developers” (notarized applications)\r\nAdditionally, the /usr/sbin/spctl ”security policy control” command-line tool can be used to modify the\r\nbehavior of Gatekeeper, exposing an additional policy:\r\nsudo spctl --global-disable results in a Gatekeeper security policy allowing applications downloaded\r\nfrom: “Anywhere”\r\nIn practice, this means tagging applications downloaded from the internet with the com.apple.quarantine\r\nextended attribute (i.e., using a macOS application and not a command-line tool like curl or wget ). As\r\nmentioned above, developers opt in to the file quarantine policy by by adding the following key to the\r\nInfo.plist : LSFileQuarantineEnabled\r\nGatekeeper in macOS 13\r\nExcluding the App Store case, prior to macOS 13 Ventura, Gatekeeper only validated the integrity of newly\r\ndownloaded (i.e., quarantined) apps on first launch. However, starting with macOS 13, as explained during\r\nWWDC22 session: What’s new in privacy, Gatekeeper will validate the integrity of all notarized apps on first\r\nlaunch (ensuring code signatures stay valid) Previously, Gatekeeper only validated the integrity of notarized apps\r\nthat had been quarantined on first launch.\r\nAdditionally, Gatekeeper will attempt to block unauthorized tampering attempts alerting the user in the process.\r\nModifying an app’s executable, secondary resources, or the Info.plist are all common tasks that app updates\r\nmight perform if authorized. This is a rather elegant solution to the expensive signature validation process that\r\noccurs on app first launch. By providing the user with the option to allow or deny incoming tampering requests for\r\na given notarized app, the user can effectively prevent their notarized apps from being tampered with.\r\nDevelopers with the same team identifier can still update their own apps with no problem. However, if another\r\ndevelopment team needs to be able to update that app, they would have to add an NSUpdateSecurityPolicy to\r\nthe Info.plist and specify the AllowProcesses dictionary that maps a team identifier to a set of signing\r\nidentifiers.\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 4 of 10\n\nImage courtesy of developer.apple.com \r\nHow has on-disk application bundle manipulation been seen in the wild?\r\nXCSSET achieves defense evasion and privilege escalation by living off of a donor application like Discord or\r\nSkype. XCSSET accomplishes these goals with help from the Open Scripting Architecture (OSA) that’s baked\r\ninto macOS. AppleScript—and by extension OSA—has traditionally been referred to as the “PowerShell of\r\nmacOS.” The execution of a supported scripting language through OSA enables some unique capabilities such as:\r\naccess to core macOS APIs (e.g., Foundation, Cocoa)\r\nthe potential to evade detection with execute-only scripts\r\nestablishing persistence by writing launch agents / daemons\r\nhamstring macOS security controls (e.g., Gatekeeper and system updates)\r\nThese capabilities are enabled by the AppleScriptObjC/PyObjC bridge. What are the consequences of these\r\nbridges from a security perspective? Well, for one, Hardened Runtime (which helps protect against code injection\r\nand is required for notarization) would need to be disabled and, secondly, a JIT-compilation / “allow unsigned\r\nexecutable memory” entitlement would need to be added. Again, these requirements are only applicable to apps,\r\ndisk images, and installers—the deliverables that can be notarized, as noted earlier.\r\nAlthough these capabilities may seem a little scary on the surface, they also serve as a very real solution to an\r\norganizational management problem. For example, it’d be useful for Mac administrators to get or set management\r\nsettings with a script, and this is where the AppleScriptObjC/PyObjC bridges come into play.\r\nA note on endpoint security\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 5 of 10\n\nThe Endpoint Security Framework (ESF), in its current implementation, does not include any notify\r\nevents surrounding the Apple Event Manager (e.g., the sending / receiving of Apple Events). This means\r\nthat any endpoint, detection, and response (EDR) sensor that wants to provide visibility into the sending or\r\nreceiving of Apple Events would need to do so without the help of ESF, a much trickier job.\r\nIn Apple’s latest iteration of the ESF, we received a host of new events surrounding login items,\r\nauthentication, OpenSSH, screen sharing, and XProtect (malware detection and remediation). Many of\r\nthese notify events are useful in detecting the malware we’ve discussed so far.\r\nXCSSET: a case study\r\nWe will now walk through how XCSSET, a recent malware family, explicitly demonstrates the concept of on-disk\r\napplication bundle manipulation. In short, XCSSET drops a malicious applet in the donor application’s\r\n.../Contents/MacOS/ directory. Thus, complexities aside, the malicious applet inherits the privileges associated\r\nwith the donor application and runs as a seemingly legitimate application. This has the added benefit of helping\r\nevade defenses as well.\r\nBackground\r\nXCSSET, first reported on in August of 2020 by Trend Micro’s Mac Threat Response team, is an extremely\r\ncapable macOS malware family primarily targeting developers through malicious Xcode projects (an IDE\r\ndesigned explicitly for Apple platform development). Users are infected by XCSSET by downloading and\r\nbuilding a compromised Xcode project, which subsequently downloads malicious applets. XCSSET contains\r\nmultiple modules to do things like steal data and includes a replicator component to infect other Xcode projects.\r\nApplets\r\nApplets, for all intents and purposes, are apps. Their structure follows the same anatomy as generic application\r\nbundles and can be constructed in a few ways:\r\ngraphically, with Script Editor.app by selecting the export to application option\r\ngraphically, with Automator.app by selecting the application template when creating a new project.\r\nAutomator describes applets as: self-running workflows. For example, one can make a simple macOS\r\napplet which starts the screensaver.\r\nvia command line, with osacompile by specifying the [-o] with a .app file extension or [-s]\r\noption (for Stay-open applets).\r\nvia Foundation Library, using the NSAppleScript API\r\nEach of these methods will generate a valid application bundle with a thin Mach-O wrapper for OSA script\r\nexecution(s). However, the Automator option will include a specialty document.wflow file and will name the\r\nMach-O wrapper Automator Application Stub. By contrast, the other three options will create a PkgInfo file,\r\nkeep compiled versions of the scripts in the Resources directory, and will name the Mach-O wrapper applet by\r\ndefault. Like regular apps, applets can be launched with the Launch Services API (essentially Finder) and must\r\nadhere to all the same Transparency, Consent, and Control (TCC) restrictions.\r\nApplet observations\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 6 of 10\n\nBy default and if constructed with Automator.app , the Mach-O binary in the ../MacOS directory will be\r\nnamed Application Stub\r\nThe Automator Application Stub Mach-O template binary can be found in\r\n/System/Library/CoreServices/ as a universal binary\r\nThe Mach-O binary links to many of the same dylibs and frameworks as /usr/bin/osascript\r\nBy disassembling the binary, we can see that the ScriptMonitor.app is being called as a way to\r\nprovide the user with a visual indicator that a script is running. In other words, when an applet is\r\nlaunched so is ScriptMonitor.app. This enables another potential avenue for detection.\r\nIf built using Automator, the contents of the OSA script will be placed in the document.wflow file (XML\r\nbased) under the \u003ckey\u003eAction Parameters\u003c/key\u003e tag. Otherwise, there will be a ../Resources/Scripts\r\ndirectory, which will contain all the scripts for the applet\r\nDuring execution, an osascript command line will generally not be seen in this case, unless the\r\nosascript binary is explicitly invoked\r\nApplets can also contain shell scripts used to run OSA code. This would be the explicit case in\r\nwhich the osascript binary would be executed, and the osascript command line would be\r\nvisible, as would the ScriptMonitor process\r\nProcedures\r\nXCSSET has been observed utilizing the curl and /usr/bin/osacompile command-line tools to download and\r\ncompile an execute-only AppleScript file (into an applet) respectively. XCSSET will then drop the malicious\r\napplet at the …/Contents/MacOS/ directory of the donor application.\r\nLegitimate apps targeted by XCSSET as donors have included (shown below in the example from Jamf Threat\r\nLabs): Google Chrome, Telegram, Apple Notes, etc.\r\nIn summary, each Applet “downloaded” by XCSSET is a module containing functionalities such as:\r\nInformation stealing: Safari, Contacts, Opera, Evernote, Apple Notes, Skype, Telegram, QQ, WeChat, etc.\r\nT1119: Automated Collection (Collection)\r\nInjecting JavaScript backdoors into websites via a universal cross-site scripting (UXSS) attack\r\nT1562: Impair Defenses\r\nT1539: Steal Web Session Cookie\r\nStealing browser cookies protected by SIP over SCP (secure copy)\r\nT1048: Exfiltration Over Alternative Protocol (Exfiltration)\r\nT1539: Steal Web Session Cookie (Credential Access)\r\nDisabling Gatekeeper, macOS updates, etc.\r\nT1562: Impair Defenses\r\nCapturing screenshots, etc.\r\nT1113: Screen Capture (Collection)\r\nConsider the following example from Jamf Threat Labs featuring the createDonorApp() AppleScript function:\r\nFile: screen_sim.applescript\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 7 of 10\n\nFunction: createDonorApp()\r\nFirst, notice the in-memory loading of screen.applescript from some C2 domain (T1059.002: AppleScript).\r\nThe AppleScript code in memory is “execute-only” compiled line by line into an applet and then “dropped” to the\r\nlocation of a temporary application bundle (T1027.004: Compile After Delivery). Finally, the applet is copied to\r\nthe donor application’s …/Contents/MacOS/ directory (T1036: Masquerading and T1554: Compromise Client\r\nSoftware Binary).\r\nProcedural examples from Jamf Threat Labs and Trend Micro Research\r\nExample 1\r\nThis excerpt from Jamf Threat Labs was taken from the file screen_sim.applescript and the function\r\nverifyCapturePermissions() .\r\nHere we see AppleScipt code enumerating the different donor applications XCSSET supports via\r\n“plugins”/applets.\r\nExample 2\r\nThis excerpt from Jamf Threat Labs was taken from the file screen_sim.applescript and the function\r\ncreateDonorApp() .\r\nHere we see step one of the primary ties XCSSET has to the application bundle manipulation technique: Creating\r\na fake donor application bundle (and applet) with osacompile -x -e \u0026 payload \u0026-o \u0026 quoted form of tempApp\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 8 of 10\n\n(T1059.002: AppleScript, T1027.004: Compile After Delivery, and T1036: Masquerading).\r\nExample 3\r\nThis excerpt from Jamf Threat Labs was also taken from the file screen_sim.applescript and the function\r\ncreateDonorApp() .\r\nHere, XCSSET replaces the donor application bundle’s Mach-O with the malicious applet plugin (T1059.002:\r\nAppleScript,\r\nT1036: Masquerading, and T1554: Compromise Client Software Binary).\r\nExample 4\r\nAs seen this example from Trend Micro Research, XCSSET is also able to disable software updates and\r\nGatekeeper (T1059.002: AppleScript and T1562.001: Impair Defenses: Disable or Modify Tools), which is shown\r\nwith the following AppleScript:\r\ndo shell script \"spctl --master-disable\" with administrator privileges\r\nSample XCSSET detection analytics\r\nThe following pseudo-detectors rely on telemetry from ESF and should help security teams detect XCSSET.\r\nDetecting on the Xcode build process\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 9 of 10\n\nThis analytic uses ES_EVENT_TYPE_NOTIFY_EXEC and ES_EVENT_TYPE_NOTIFY_FORK events. It looks for the Xcode\r\nbuild process spawning shell scripts that, in turn, create a hex dump.\r\nparent_process == xcbbuildservice\r\n\u0026\u0026\r\nprocess == ( bash || zsh || or sh )\r\n\u0026\u0026\r\nchild_processes == /usr/bin/xxd\r\nDetecting behavioral process execution from within the GameKit cache directory\r\nThis analytics uses a single ES_EVENT_TYPE_NOTIFY_EXEC event, and looks explicitly for processes executing from\r\nthe .../library/caches/gamekit/ file directory.\r\nprocess == [anything]\r\n\u0026\u0026\r\nfile_path == .../library/caches/gamekit/\r\nDetecting strange applet / command-line OSA behavior\r\nThis analytics relies on a single ES_EVENT_TYPE_NOTIFY_EXEC event and looks for suspicious OSA commands.\r\nparent_process == ( applet || osascript )\r\n\u0026\u0026\r\ncommand_line_includes ( osacompile )\r\nDetecting the in-memory downloading and compiling of payload applets\r\nThis analytics uses a single ES_EVENT_TYPE_NOTIFY_EXEC event and looks for the the execution of curl , | , or\r\nosacompile commands.\r\ncommand_line_includes ( osacompile \u0026\u0026 | \u0026\u0026 curl )\r\nSource: https://redcanary.com/blog/mac-application-bundles/\r\nhttps://redcanary.com/blog/mac-application-bundles/\r\nPage 10 of 10",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://redcanary.com/blog/mac-application-bundles/"
	],
	"report_names": [
		"mac-application-bundles"
	],
	"threat_actors": [],
	"ts_created_at": 1775434528,
	"ts_updated_at": 1775791219,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/0a069504b66092b3fb02e1014ddca5c74fa6324c.pdf",
		"text": "https://archive.orkl.eu/0a069504b66092b3fb02e1014ddca5c74fa6324c.txt",
		"img": "https://archive.orkl.eu/0a069504b66092b3fb02e1014ddca5c74fa6324c.jpg"
	}
}