Perception Point researchers discovered a vulnerability in macOS which allows attackers to bypass Apple’s SIP (System Integrity Protection), and thus take full control over the system, provided that they already managed to achieve code execution with high privileges.

The vulnerability was disclosed to Apple, and the fix was announced on January 26th on the latest security updates of macOS Monterey 12.2, macOS Big Sur 11.6.3 and Security Update 2022-001 Catalina, identified as CVE-2022-22583. Apple shared the credit for this CVE between three researchers: Mickey Jin (@patch1t), Ron Hass (@ronhass7) of Perception Point and an anonymous researcher.

This article provides an overview of the SIP mechanism and the vulnerability details as disclosed to Apple by Perception Point.

What is SIP (System Integrity Protection)?

SIP (also known as “rootless”) is a security mechanism in macOS, introduced by Apple in OS X El Capitan (version 10.11), with the objective of protecting the system as a whole, even from the root user. This includes, among other things, restrictions on:

  • Loading kernel extensions (only signed extensions are allowed)
  • Tampering with critical files and directories
  • Debugging system processes
  • Programmatically setting the startup disk

These restrictions are enforced by the macOS kernel (XNU), and can be disabled only with physical access to the machine (by booting into Recovery OS). SIP can be considered as the final defense line between an attacker and full control of the system.

As previously mentioned, in order to truly protect the kernel, SIP needs to prevent modifications to critical files and directories. One example is the content of the extension “AppleKextExcludeList”, which manages a list of excluded kernel extensions. We can observe the SIP restrictions by using the flag “-O” in the “ls” command:

However, Apple needs to “preserve their right” to modify all files and directories in the system. This is required, for example, to perform system updates. The way it works is by giving specific entitlements to the needed executables. Entitlements are just pieces of data included inside a signed application, letting the system know this application is entitled to perform specific tasks. The entitlements relevant for SIP include:

  • com.apple.rootless.install – lifting all SIP’s filesystem restrictions
  • com.apple.rootless.install.heritable – same, but child processes also inherit this entitlement

 

Apple won’t allow just any application to be signed with the above entitlements, but there are a few Apple-signed executables in the system which own one of these entitlements. One example is “system_installd”, a part of the PackageKit framework, which is responsible for installing Apple-signed packages. We can observe the entitlements of an executable with the “codesign” utility:

 

As you can see, “system_installd” has the stronger “com.apple.rootless.install.heritable” entitlement, and this will be used in our vulnerability.

The Vulnerability

Let’s begin by reviewing the process we’ve gone through to find this vulnerability. On Oct 28, 2021, Microsoft published a blog post elaborating on a SIP bypass vulnerability they found, which was fixed in macOS Monterey 12.0.1 and identified as CVE-2021-30892. Microsoft named this vulnerability “shrootless”.

Microsoft researchers noticed that when the user installs an Apple-signed package, it is automatically executed by the service “system_installd”, as mentioned earlier. If the package contains any pre/post-install scripts, they are also executed by “system_installd” and thus inherit the “com.apple.rootless.install.heritable” entitlement, which effectively allows these scripts to fully bypass SIP’s filesystem restrictions. The bug was that if one of the pre/post-install scripts was a zsh script (i.e. starts with “#!/bin/zsh”), “system_installd” would execute zsh, which in turn would source the file “/etc/zshenv”. This file is not restricted by SIP and can be modified freely by root, and any code inside it would run in this “SIP-bypass context” whenever an Apple-signed package with a zsh script is installed. Apple’s fix was to prevent zsh from sourcing “/etc/zshenv” in this scenario.

To look deeper into this vulnerability, we needed an Apple-signed package with some pre/post-install scripts. After finding a candidate package, the “Suspicious Package” application can be useful to inspect it:

As you can see, this is an Apple-signed package, which contains a perl post-install script. The script actually executes several more commands, and we used the command-line utility “Process Monitor” to examine its behavior. Now, let’s look at its output (for the sake of clarity, we omitted some parts and left only “ES_EVENT_TYPE_NOTIFY_EXEC” events):

{"pid":3308,"name":"system_installd"}
{"pid":3321,"name":"perl","arguments":["/usr/bin/perl","/tmp/PKInstallSandbox.QTKhng/Scripts/com.apple.pkg.FxPlugSDK.QCq4LX/postinstall","/private/tmp/FxPlugSDK.pkg","/","/","/"],"ppid":3308}
{"pid":3321,"name":"perl5.30","ppid":3308}
{"pid":3322,"name":"pwd","ppid":3321}
{"pid":3323,"name":"sh","arguments":["sh","-c","logger -p install.info 'Running Install Scripts . . .'"],"ppid":3321}
{"pid":3323,"name":"bash","arguments":["sh","-c","logger -p install.info 'Running Install Scripts . . .'"],"ppid":3321}
{"pid":3323,"name":"logger","ppid":3321}
{"pid":3324,"name":"sh",","arguments":["sh","-c","/bin/ls \/private\/tmp\/PKInstallSandbox\.QTKhng\/Scripts\/com\.apple\.pkg\.FxPlugSDK\.QCq4LX\/postinstall_actions\/"],"ppid":3321}
{"pid":3324,"name":"bash","arguments":["sh","-c","/bin/ls \/private\/tmp\/PKInstallSandbox\.QTKhng\/Scripts\/com\.apple\.pkg\.FxPlugSDK\.QCq4LX\/postinstall_actions\/"],"ppid":3321}
{"pid":3324,"name":"ls","ppid":3321}

In this output, something immediately caught our attention: “system_installd” executed the post-install script from a temporary directory, “/tmp/PKInstallSandbox.QTKhng/Scripts/com.apple.pkg.FxPlugSDK.QCq4LX” in this case. Apparently, “system_installd” first extracts the scripts into a temporary directory, and then executes them from there. Moreover, there is a significant amount of time between the extraction and the execution (the extraction is probably done before the installation, for both pre- and post-install scripts).

Can we exploit it and simply swap the scripts without “system_installd” ever noticing?

Naturally, it’s not as simple as that. Apple considered this loophole and made this temporary directory restricted by SIP:

However, we were very intrigued when we found out that the “/tmp” directory itself is not restricted by SIP! To be precise, “/tmp” is actually a symbolic link and is restricted, but the directory itself is not.

With this information at hand, we identified how to swap the post-install script between the extraction and the execution, and the key to this is mounting. Because “/private/tmp” is not restricted by SIP, we can mount a virtual image onto this directory at any point (even if it contains a restricted directory) and change its content however we would like. We can utilize this idea in several ways to swap the script, but because the temporary directory path contains some random characters, we chose to implement the exploit as follows:

  1. Create a virtual image file and mount it onto “/private/tmp”.
  2. Install an Apple-signed package with post-install scripts.
  3. Wait for the installer to finish the extraction of the scripts directory, and gather the random parts of the extracted path.
  4. Unmount the image file, thus reverting to the contents of “/private/tmp” before the extraction. 
  5. Create the scripts directory by ourselves (with the random path we gathered earlier) and deposit there whatever scripts we want.
Figure 1: The exploit flow

Any script we put as the “post-install script” inherits the “com.apple.rootless.install.heritable” entitlement from “system_installd” and is able to fully bypass SIP restrictions.

This vulnerability is very dependent on timing – the exploit must succeed in swapping the script in the window of opportunity. However, the exploit is quite reliable and we noticed that it usually takes one or two tries to succeed, mostly because we used a package with post-install scripts, which provides us plenty of time (the whole installation time).

Figure 2: Demonstration of the exploit

How did Apple fix this?

In macOS 12.2, Apple changed the extraction destination to a new directory: “/Library/Apple/System/Library/InstallerSandboxes”. All the directories in this path are restricted, except the first one (“/Library”). Although it looks similar to the situation before, Apple correctly fixed this bug by also preventing mount attempts onto the “/Library” directory when SIP is enabled:


Error       0x0                  0      0    kernel: (Sandbox) System Policy: diskimages-helpe(725) deny(1) file-mount /Library

Vendor Communication

  • 2021-11-05: Reported to Apple Product Security.
  • 2021-12-02: We asked for an update on this issue.
  • 2021-12-03: Apple advises they are planning to address this issue.
  • 2022-01-14: Apple advises this issue will be addressed in an upcoming security update.
  • 2022-01-26: macOS 12.2 released, which addresses the reported vulnerability.

 

Here’s some related content you may enjoy:

Perception Point Discovers a MacOS Zero-day Allowing Attackers to Bypass Apple’s System Integrity Protection

What is a Zero Day Attack?