Technical Advisory – macOS Installer Local Root Privilege Escalation (CVE-2020-9817)

Vendor: Apple
Vendor URL: https://www.apple.com
Versions affected: All versions of macOS prior to macOS Catalina 10.15.5,
Security Update 2020-003 Mojave, Security Update 2020-003 High Sierra
Author: Andy Grant
Advisory URL: https://support.apple.com/en-us/HT211170
CVE identifier: CVE-2020-9817
Risk: Medium (a local attacker can gain root privileges during the installation 
or update of misconfigured packages)

Summary

macOS Installer creates a world-readable temp directory that files are extracted into during installation. These files are usually scripts that are executed (almost always by root) during the installation process. These extracted files maintain their file ownership from the developer’s system, which means that these root-executed files may not be owned by root, but instead a UID that could also exist on the system doing the install. This allows the user associated with the owning UID (or a process running under that UID) to modify or replace the scripts, resulting in their code being executed with root privileges.

Location

The files extracted by Installer from an installation package into a temporary PKInstall sandbox directory of the form /private/tmp/PKInstallSandbox.<6-random-alphanumerics>.

Impact

A user or process with the same user ID as the extracted files can modify or replace them. These files are then executed by Installer, typically as the root user. This allows a low-privileged user or process to gain arbitrary code execution with root privileges, effectively leading to a full system compromise.

Details

When an OS X Installer flat package (.pkg file) contains pre- and post-install scripts, they are contained in a cpio archive, called Scripts, within the package. During installation, Installer needs to extract the contents of Scripts in order to execute the appropriate scripts. To do this, Installer, running as root, creates /private/tmp/PKInstallSandbox.<6-random-alphanumerics>/Scripts/<pkg-ID>.<6-random-alphanumerics>/ and extracts the contents into that directory. Nearly all installations require the user to enter administrative credentials, resulting in the scripts being executed with root privileges.

Installer assumedly uses ditto to extract the contents of the archive into the temp directory. Notably, when using ditto the “resulting files, links, and devices will have the same mode, access time, modification time, owner, and group as the source items from which they are copied.” Thus, when extracted to the above temporary directory, the pre- and post-install scripts will be owned by the same user and group IDs (with the same rwx file permissions) as they were when added to the install package.

The security issue is that even though the directory the scripts are extracted to is random, an attacker can detect the creation of the directory (or use path globbing), and if they are operating as the same user ID that owns the extracted scripts, can modify/overwrite the scripts, resulting in the attacker’s code being executed by root (privilege escalation).

A simple Bash one-liner that would exploit this could be

while true; do (cp exploit.sh /tmp/PK*/Scripts/*/preinstall; cp exploit.sh /tmp/PK*/Scripts/postinstall) 2>/dev/null; done

In testing of public packages, NCC Group discovered packages whose extracted scripts (in the PKInstallSandbox directory), were owned by user IDs: 0, 201, 501, 502, and 503, as well as very large IDs (i.e. >2000).

User ID 0 is obviously root, and thus is not an issue as long as it has sane (non-world writable) permissions.

The 500 user IDs are the IDs used by macOS for locally created users, with 501 being the first created user, 502 the second, and so on. Thus, if a developer creates the script files as a normal user, such as user 501, then user 501 on the installing system is able to change those files before they are executed (by root).

The most likely case of attack here is that the files are owned by the first created user (user 501) and user of the install target is user 501, but they are not an administrator of their system (such as in an enterprise environment) and must go ask IT to install the software. Even if the IT admin logs out of the user’s account, logs in with their admin (not user 501) account, and installs the software, the scripts are still owned by user 501 and a background process by the user can modify the scripts and gain root privileges on the system.

A less likely attack (due to the low occurrence of non-0 and non-501 owned files seen during testing) is that there exists multiple users on the system (e.g. a shared home computer) and user 501 is the admin user (e.g. a parent) and user 502 is a non-admin user (e.g. a child). If the script files are owned by user 502, then user 502 can abuse this issue when user 501 installs the vulnerable software.

User ID 201 is reserved for macOS’s guest user. If the guest user is enabled, and FileVault is not enabled, then a local attacker would be able to login as Guest, create their payload in a world-writeable location (e.g. /tmp), and initiate a background process to exploit any future runs of Installer with packages whose scripts are owned by UID 201.

The above one-liner could be adjusted to the following to allow for Guest to perform the exploit even after logging out as long as the system is not restarted.

screen -dm bash -c "while true; do (cp /tmp/exploit.sh /tmp/PK*/Scripts/*/preinstall; cp /tmp/exploit.sh /tmp/PK*/Scripts/postinstall) 2>/dev/null; done"

The owner and permission inheritance can be seen below in the file listings of temporary directories observed during installation for example packages NCC Group created.

# postinstall created by user 501, pkg created by user 501
/private/tmp/PKInstallSandbox.lig66K/Scripts/com.nccgroup.pkg.501-package.jTXNOS:
drwxr-xr-x 3 0    0   96 Nov  5 13:52 .
drwxr-xr-x 3 0    0   96 Nov  5 13:52 ..
-rwxr-xr-x 1 501  20  26 Nov  5 13:40 postinstall

# postinstall created by user 0, pkg created by user 501
/private/tmp/PKInstallSandbox.KB4xeC/Scripts/com.nccgroup.pkg.0-package.ViBtho:
drwxr-xr-x 3 0  0   96 Nov  5 13:55 .
drwxr-xr-x 3 0  0   96 Nov  5 13:55 ..
-rwxr-xr-x 1 0  20  26 Nov  5 13:54 postinstall

# postinstall created by user 502, pkg created by user 501
/private/tmp/PKInstallSandbox.Osfl5c/Scripts/com.nccgroup.pkg.502-package.dEmLLK:
drwxr-xr-x 3 0    0   96 Nov  5 13:57 .
drwxr-xr-x 3 0    0   96 Nov  5 13:57 ..
-rwxr-xr-x 1 502  20  26 Nov  5 13:56 postinstall

# postinstall created by user 501 (with chmod 777), pkg created by user 501
/private/tmp/PKInstallSandbox.zSgdsA/Scripts/com.nccgroup.pkg.501-writeall-package.9Yvsqk:
drwxr-xr-x 3 0    0   96 Nov  5 14:00 .
drwxr-xr-x 3 0    0   96 Nov  5 14:00 ..
-rwxrwxrwx 1 501  20  26 Nov  5 13:58 postinstall

# postinstall created by user 501, pkg created by user 0
/private/tmp/PKInstallSandbox.Jswp6d/Scripts/com.nccgroup.pkg.501-0-built-package.3CQ1DC:
drwxr-xr-x 3 0    0   96 Nov  5 14:07 .
drwxr-xr-x 3 0    0   96 Nov  5 14:07 ..
-rwxr-xr-x 1 501  20  26 Nov  5 13:40 postinstall

Recommendation

While this issue can be addressed by each individual developer by ensuring correct ownership and permissions of files included in an install package, this could also be addressed systemically within Installer. Installer can ensure that file ownerships and permissions are not retained when extracting from the installer package archives, or making the PKInstallSandbox temporary directories not world read-execute.

Apple has fixed this issue by the ensuring ownership of temporary files match the user of the process doing the installation. Thus, when Installer runs with root privileges, the extracted files are owned by root.

Vendor Communication

2019-10-24 - NCC Group sends initial email to vendor asking to discuss a
                      possible security issue related to Installer
2019-10-30 - Vendor requests additional details
2019-10-30 - NCC Group provides preliminary details
2019-11-11 - NCC Group provides a detailed walkthrough of the issue with
                      new details from ongoing research
2019-11-14 - Vendor states internal investigation is taking place
2019-12-19 - Vendor shares that their investigations are not aligning with
                      NCC Group’s findings
2019-12-19 - NCC Group clarifies details of the issue and provides exploit code
                      to demonstrate the issue for a specific public package
2020-01-06 - Vendor requests proof-of-concept packages that are vulnerable
                      and demonstrate the issue
2020-01-06 - NCC Group provides requested packages
2020-02-04 - Vendor confirms the issue and states mitigations are being
                      investigated
2020-02-26 - Vendor updates NCC Group with planned timeline for release of a fix
2020-06-06 - Vendor requests NCC Group verify fix released in a beta build
2020-06-11 - NCC Group confirms the fix addresses the issue
2020-05-26 - Vendor assigns CVE-2020-9817 to the issue and releases the fix
                      as part of macOS 10.15.5 and security updates to previous versions
                      (https://support.apple.com/en-us/HT211170)

About NCC Group

NCC Group is a global expert in cyber security and risk mitigation, working with businesses to protect their brand, value and reputation against the ever-evolving threat landscape.

With our knowledge, experience and global footprint, we are best placed to help businesses identify, assess, mitigate and respond to the risks they face.

We are passionate about making the Internet safer and revolutionizing the way in which organizations think about cybersecurity.