Distributing unnotarized Mac apps in a text file

April 2 2021 by Jeff Johnson

This isn't a late April Fools joke. (My April Fools joke that I got hired by Apple as a Swift Evangelist backfired with a profusion of congratulations.) This blog post is a kind of follow-up to some previous blog posts. Last month I wondered what's the best way to distribute Mac apps without notarization, and I decided that the best way was downloading with curl directly to the Applications folder. Unlike web browsers, curl does not add the com.apple.quarantine extended attribute to downloaded files. Still, this method is not ideal, because the app developer has to send users to the scary place: the command line! So I've continued to wonder if there's a relatively simple way to do it with a graphical interface. (If you think you can "just right click", well no, that's not quite how it works.) And then it finally hit me like a brick of gold wrapped in a lemon: I already knew how to remove the quarantine with a GUI, because this was my Mac sandbox escape! If you recall, a year ago I showed how to escape the sandbox by opening a maliciously crafted executable in TextEdit and then telling TextEdit via AppleScript to save the executable file. This causes the quarantine to be removed from the executable, because TextEdit has the special com.apple.security.files.user-selected.executable entitlement. I never received a bug bounty from Apple, and as far as I can tell this sandbox escape still exists in Big Sur. Which is actually good news for us right now, because we can use it to distribute Mac apps! For your enjoyment, I've created an example, which I call Gatecrasher:


Gatecrasher is an empty Mac app that I created in Xcode in a few minutes. It has no code other than the standard NSApplicationMain and the default MainMenu.xib file with the main menu and window. Gatecrasher isn't signed with an Apple code signing certificate and isn't notarized; it has only an "ad hoc" (codesign -s -) code signature with no identity. I compressed Gatecrasher into a zip file, but as you'll see, that's not what you're downloading. Instead, I embedded the zipped app into a "rich text" document (.rtfd file) in TextEdit and then compressed it. That's what you're downloading. You can unzip the rtfd, double-click to open it in TextEdit, follow the simple instructions written inside, and you'll end up with an app that you can double-click to launch — all without any macOS Gatekeeper alert, and all without any Developer ID or notarization.

And there you have it, the easiest way yet to distribute unnotarized Mac apps! Perhaps too easy…

Here's a screenshot of the rtfd file opened in TextEdit:
In my testing, I only have to ⌘-S once on Mojave to unquarantine the zip, but for some reason I have to ⌘-S twice on Big Sur. I haven't figured out why yet, but I wanted to get this blog post out before bedtime. Indeed, I can see now that in my rush I made a typo: it should have said "unzip Gatecrasher app" rather than "unzip Gatekeeper app". Oh well. The typo does serve to highlight my pun!

Addendum April 3 2021

I decided to try the same technique with an unsigned installer package that I had lying around. In this case it was old version of Carthage, the open source dependency manager. And it worked! You can distribute an unsigned, unnotarized .pkg file inside an .rtfd file, and when you double-click the package (after saving to remove the quarantine), TextEdit will run the macOS installer. Here's a sample:


You don't need to install Carthage, you can just open the installer and then quit before installing, to verify that you can bypass Gatekeeper.

On Catalina and Mojave, you only need to save the rich text document once in TextEdit to remove the quarantine on the file attachment. On Big Sur, you need to save it twice. I watched the Console log while this was happening (like looking for a needle in a haystack nowadays), and I noticed that the first time I saved, the sandboxd process logged "checking kTCCServiceSystemPolicyDownloadsFolder for TextEdit". Then the kernel rejected the request from sandboxd with the reason "would require prompt by TCC for TextEdit". But the second time I saved, there were no such log messages. So this might be a bug in macOS. There are only two hard things in computer science: cache invalidation and running unnotarized things on the Mac.

By the way, I've been told that double-clicking an embedded .zip file doesn't work right if a third-party app such as The Unarchiver is the default handler for archives on your Mac rather than the built-in Archive Utility. However, after removing the quarantine on the .zip, you can still drag it out of TextEdit and drop it into Finder, and then unarchive it to bypass Gatekeeper.

Jeff Johnson (My apps, PayPal.Me)