How your Mac can download an old ‘security’ update by accident

Last weekend, I reported the installation of two ‘updates’ to security data bundles which are protected by SIP, without any information being given to the user, and without any record in Sierra’s Install History. Thanks to some help from Al Varnell, I can now explain what happened, and warn you that the same can happen to your Mac too.

The reason is that daemons in macOS can decide that they want to update their components and, without warning the user, can download and replace files protected by SIP. Such installations can bypass the Install History completely, so you are unlikely ever to be aware that they happened, unless you look deeper into your Mac’s records.

The crucial clues come from one of the few traditional logs which are still useful in Sierra and High Sierra: /var/log/install.log. Although all general log entries are now made in the new unified log, and buried in the chatter of thousands of other entries, Sierra and High Sierra still make clear and useful entries in the traditional install log.

In my case, that install log showed that softwareupdate had performed a routine check for updates at 2200 on 4 January 2018, as it does every 8 hours or so. Then, less than half an hour later, the StorageKit daemon was started:
22:26:44.51 storagekitd[7773]: Starting SKDaemon...
StorageKit is the private framework that supports most of the features in Disk Utility, and may have kicked in because I opened that app, or another of Apple’s apps which accesses information about disks mounted, etc.

This in turn connected with the System Migration daemon:
22:26:58 systemmigrationd[7772]: systemmigrationd: Transitioning scanner request from Nothing to Local Volumes.
which in turn told the System Install daemon that it needed to download and install old security data packages:
22:27:00 system_installd[415]: PackageKit: Adding client PKInstallDaemonClient pid=7772, uid=0 (/System/Library/PrivateFrameworks/SystemMigration.framework/Versions/A/Resources/systemmigrationd)

The System Install daemon then proceeded to download and install the package:
22:27:00 system_installd[415]: PackageKit: ----- Begin install -----
22:27:00 system_installd[415]: PackageKit: request=PKInstallRequest >1 packages, destination=/<
22:27:00 system_installd[415]: PackageKit: packages=("PKLeopardPackage >file:///var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/SMIncompatibleAppUpdate/CFNetworkDownload_sL6CyW.tmp<")

In the course of doing that, it discovered that one component, part of the SIP configuration files, already existed in a more recent version, so decided to skip that:
22:27:08 system_installd[415]: PackageKit: Skipping component "com.apple.rootless.compatibility" (12.0.0-12.0.0-12000000000000-*) because the version 14.0.0-14.0.0-14000000000000-187 is already installed at /System/Library/Sandbox/Compatibility.bundle.

However, for com.apple.pkg.OSX1012IncompatibleAppList it decided to rely on receipts rather than checking versions:
22:27:08 system_installd[415]: PackageKit: Will do receipt-based obsoleting for package identifier com.apple.pkg.OSX1012IncompatibleAppList (prefix path=/)

Once that installer package was downloaded, it wrote the receipt:
22:27:55 system_installd[415]: PackageKit: Writing system content receipt for com.apple.pkg.OSX1012IncompatibleAppList to /

Because this installation was going to overwrite files and folders protected by SIP, the Authorisation daemon had to grant that right to the System Migration daemon. Log entries reflecting that were:
22:27:00.77 authd[155]: Succeeded authorizing right 'system.install.software' by client '/System/Library/PrivateFrameworks/SystemMigration.framework/Versions/A/Resources/systemmigrationd' [7772] for authorization created by '/System/Library/PrivateFrameworks/SystemMigration.framework/Versions/A/Resources/systemmigrationd' [7772] (2,0)
22:27:00.80 authd[155]: Succeeded authorizing right 'system.install.apple-software' by client '/System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/system_installd' [415] for authorization created by '/System/Library/PrivateFrameworks/SystemMigration.framework/Versions/A/Resources/systemmigrationd' [7772] (4,0)
22:27:00.80 authd[155]: Succeeded authorizing right 'system.install.software' by client '/System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/system_installd' [415] for authorization created by '/System/Library/PrivateFrameworks/SystemMigration.framework/Versions/A/Resources/systemmigrationd' [7772] (4,0)
22:27:00.80 authd[155]: Succeeded authorizing right 'system.install.software.iap' by client '/System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/system_installd' [415] for authorization created by '/System/Library/PrivateFrameworks/SystemMigration.framework/Versions/A/Resources/systemmigrationd' [7772] (4,0)

With that right granted, and the installer package ready, the System Installer daemon then performed the ‘update’, which in fact re-installed the same files. First, it prevented system sleep and suspended Time Machine backups:
22:27:55 system_installd[415]: PackageKit: prevent user idle system sleep
22:27:55 system_installd[415]: PackageKit: suspending backupd

Next, it performed the installation:
22:27:55 system_installd[415]: PackageKit: Using system content trashcan path /.PKInstallSandboxManager-SystemSoftware/1F017040-7EA0-4EF7-8D4C-E34AAB497B24.activeSandbox/Trashes for sandbox /.PKInstallSandboxManager-SystemSoftware/1F017040-7EA0-4EF7-8D4C-E34AAB497B24.activeSandbox
22:27:55 system_installd[415]: PackageKit: Shoving /.PKInstallSandboxManager-SystemSoftware/1F017040-7EA0-4EF7-8D4C-E34AAB497B24.activeSandbox/Root (2 items) to /
22:27:55 install_monitor[7781]: Temporarily excluding: /Applications, /Library, /System, /bin, /private, /sbin, /usr
22:27:55 system_installd[415]: PackageKit: Writing system content receipt for com.apple.pkg.OSX1012IncompatibleAppList to /
22:27:56 install_monitor[7781]: Re-included: /Applications, /Library, /System, /bin, /private, /sbin, /usr

Finally, it allowed backups to be made again, and normal sleep to occur:
22:27:56 system_installd[415]: PackageKit: releasing backupd
22:27:56 system_installd[415]: PackageKit: allow user idle system sleep
22:27:56 system_installd[415]: PackageKit: ----- End install -----
22:27:56 system_installd[415]: PackageKit: 55.8s elapsed install time

So, what was different about this ‘update’ compared with a regular App Store update, or pushed security data update?

First, it was triggered and run by the System Migration daemon, not softwareupdate. It was thus not a pushed update (Apple’s servers didn’t offer the update), but was pulled by the System Migration daemon. Apparently, when that decides it wants an update, nothing is recorded in the Install History, only the install log.

Second, the download and installation were managed by the System Install daemon, which writes the receipt for the installation but doesn’t write to the Install History. It presumably leaves such record-keeping to the caller.

Third, although the System Install daemon checked one of the requested installations, com.apple.rootless.compatibility in /System/Library/Sandbox/Compatibility.bundle, a very important part of SIP, it relied on receipts to decide if it should obtain com.apple.pkg.OSX1012IncompatibleAppList. That turned out to be an incorrect decision, which resulted in an unnecessary download and installation.

As far as I can tell, this mechanism should only ever apply to com.apple.pkg.OSX1012IncompatibleAppList, which is part of the System Migration daemon. As that is a relatively unimportant data ‘package’, only used during system migration, the obfuscation which resulted is of little or no consequence. App Store and pushed security updates should not come in under the radar in this way, although I don’t know if there are any similar situations with other macOS services.

However, there are some serious health warnings too.

First, it demonstrates how daemons and other components in macOS can operate outside the limits which we normally assume of them. I did not open any of the user-controlled migration tools to trigger this, but StorageKit decided that it would. StorageKit is a private framework which suffered a glaring security bug in High Sierra 10.13, revealing encryption passwords as the password hint. Another such gaffe could inadvertently trigger other covert ‘updates’.

Second, it demonstrates a mechanism by which processes (macOS or third party) could download and install software covertly, under the radar of even the cautious user. Checking the install log in Console or using my free SystHist address that; checking the Install History in System Information is not sufficient.

Third, SIP is only good so long as it works perfectly. We expect macOS installations and updates to bypass SIP, and are (normally) present and watchful when those take place. For a daemon like System Migration to be able to pop up out of the blue, obtain authorisation to replace protected files, and to do so without any more visible record of the event, is both unnecessary and potentially dangerous.

At the very least, this is the type of event which should be recorded clearly in a security log – something which Sierra and High Sierra don’t have. At best, the user should be warned what is happening at the time.

Finally, once again we learn about important macOS behaviours not from any documentation, but by serendipitous observations.