How to slip ‘unsigned’ apps past Gatekeeper in Big Sur

Consistency in security, particularly in the GUI, is very important. This article is a demonstration of features in Big Sur which you might have thought would protect you, but because of their inconsistent behaviour could catch you out. This shows how you can download, install and run executable code, such as an app, which isn’t signed with a Developer ID, only an ad-hoc signature, without macOS warning you that the code is potentially dangerous.

What happens

Installer packages are one of the two means of installing third-party software which Apple officially recommends (the other being disk images). In Big Sur, developers are required to sign them, and notarize the apps they distribute within them. Normally, if you try to open a package which isn’t signed, and has been downloaded from the internet so has been put in quarantine, macOS informs you that the package is from an unidentified developer, so can’t be opened.

package01

That behaviour applies when you double-click the package. Use the Finder’s Open command and, much like when trying to open a new app which hasn’t been notarized, you’re warned but given the choice of “overriding system security” and the risks that exposes you to.

package02

If you first open the Installer app and use its open file dialog to choose an unsigned package, there are no warnings given at all. Instead, you’re taken straight into the first of the sequence of windows which lead you through the installation process.

Of course, the information is there if you look for it: when the package has been correctly signed, a small padlock icon is shown at the top right of the window. That’s not true of Apple’s installer packages, though, and it requires you to notice something by its absence, which invites human error.

package03

package04

My example installer package has another surprise for you. When you open it, if you check what it installs using the Show Files command in the File menu, it admits only to installing a command tool, cintch, which is put in the main Applications folder. As with many packages, it actually does a bit more, but even browsing its log you won’t know that it has also downloaded another app (in fact, a properly signed and notarized command tool, blowhole) from a remote website. This is performed using curl in its pre-install script, but as Installer doesn’t let you browse the scripts in a package, the only way you’ll know is by discovering that Zip archive in your Applications folder too.

If you have a software firewall active, you will at least be warned that the package is trying to perform “preinstall via curl” from the target site, but Little Snitch doesn’t worry about the fact that the package isn’t signed, of course.

Neither the command tool installed by this package nor the Zip archive downloaded by its pre-install script are put into quarantine, therefore neither will be subjected to Gatekeeper’s first run checks when you do try to open them. So by using the Open command in Installer, Gatekeeper hasn’t taken a proper look at either, and nothing has warned you of that fact.

With no more than a couple of ad-hoc signatures, and using the GUI alone, this package has managed to slip two payloads past Big Sur’s Gatekeeper checks. To the user, the only indication that this has happened is the absence of a padlock in Installer’s window.

Why does this happen?

It’s easy to assume that opening any quarantined file will somehow magically invoke Gatekeeper, but that isn’t the case. This mechanism relies on the open request being handled by LaunchServices. When you double-click on an app or document, or use the Finder’s Open command, that’s exactly what happens, and triggers one of the warnings shown above.

Opening a document such as an Installer package from within the app doesn’t pass through LaunchServices, though, so doesn’t trigger the quarantine and Gatekeeper check. macOS leaves it to the app to perform any security checks which it deems necessary, and in the case of the Installer app that appears to be little if any.

In ordinary use, Apple expects users not to open Installer direct, in which case this problem wouldn’t arise. Indeed, when you’ve just used Installer, it isn’t left in the Dock for further access. However, Apple hasn’t considered that some users add apps from CoreServices to the Dock to make them easier to run, nor the fact that the Installer app still ends up in the Finder’s Recent Items menu, from where it’s easy to run and trigger this problem.

Pre-install scripts are a greater concern. According to Apple’s documentation, they’re primarily intended to support checks that everything’s good to install the package, with post-install scripts to perform any cleaning up that’s needed. In practice, though, many commercial developers supply users with a stub Installer package which then downloads the bulk of an app using curl, which evades quarantine and Gatekeeper completely. Unfortunately this is a technique used to deploy malicious software too. Any suggestion of limiting the power or scope of these scripts is thus likely to be strongly opposed by all parties, however much it might improve security.

Demonstration

If you want to test this out yourself, I’m providing the demonstration for download from here: installTest1

It behaves as described above. The package itself installs a copy of my command tool cintch, which in this case isn’t signed with a Developer ID or notarized, but a special build which is just ad-hoc signed. To make it easy to remove, cintch is installed into the main Applications folder.

The pre-install script downloads a Zip archive of my command tool blowhole, which is both signed and notarized, and the same as that available on its product page. This also ends up in your Applications folder.

The package itself is unsigned, and when downloaded from here using a browser should have a quarantine flag added, to make it easy for you to reproduce my tests.

Bonus

When you’re trying to check quarantined unsigned packages using my utilities such as xattred (which shows its extended attributes), ArchiChect and Taccy, you’ll encounter the same problems with LaunchServices: if the package has a quarantine flag set, dragging and dropping it onto one of those apps elicits the same refusal as double-clicking the package. This is because LaunchServices engages Gatekeeper, as described above. If you want to check the package, use the app’s Open command instead, and you won’t be blocked by Gatekeeper.

Lessons

There’s an important difference in how packages and apps are treated when you double-click or open them in the Finder, and when you open them from within the app. If you’ve got a download which you want Gatekeeper to check, Finder double-click is best.

Subtle indicators, such as the padlock in the top right of Installer’s window, can be very important. There’s scope for Installer’s design to be improved to help the user more given the modern threat landscape.

Don’t be surprised when an Installer package installs more than you expect, and don’t expect such downloads to pass through Gatekeeper’s checks. If you’re unsure about the safety of any download, don’t install it until you’re confident that it’s safe.

Acknowledgements

This investigation and article were inspired by what seemed to be a simple question from Nick, and subsequent discussion to which Thomas Reed and others contributed. I don’t think that I’ve answered anything useful, but hopefully have made us all more aware and cautious about Installer packages. I’m very grateful to Milo (see comment below) who pointed out my scripting error in the original version of the demo: this has now been corrected, and the demo should now work as described above.