[0day] Bypassing Apple's System Integrity Protection
› abusing the local upgrade process to bypass SIP
Twitter user @mikeymikey pointed out that savy system admins have previously used this technique to customize upgrades and deployments. For more, see the following write up. Nice work guys :)
I recently found myself on yet another lonnnng flight, (~13hrs; Shanghai → back home to Hawaii) thus had some time to write! Hooray :)
Here, let's dive into the technical details of how an attacker can easily bypass Apple's System Integrity Protection (SIP) on a fully patched macOS system. Armed with this 0day attack, hackers can modify protected operating system components or make malware that is itself protected by SIP...and thus quite difficult to delete :/
Apple's System Integrity Protection
Introduced in El Capitan, System Integrity Protection or SIP (or 'rootless'), is detailed by Apple in various online documents such as "About System Integrity Protection on your Mac" and "System Integrity Protection Guide". In short SIP is a OS-level security feature that aims to protect Mac users from malicious software. Apple states:
"System Integrity Protection is a security technology in OS X El Capitan and later that's designed to help prevent potentially malicious software from modifying protected files and folders on your Mac. System Integrity protection restricts the root user account and limits the actions that the root user can perform on protected parts of the Mac operating system."
In other words, even if malware or an attacker gains root privileges, both are 'limited' by what they can do. Specifically, code, even running as root cannot:
- write to (or modify) system locations or OS components
- attach to system processes to debug or inject into
- load unsigned kernel extensions
- disable System Integrity Protection
- ...and more!
From a security point of view, SIP is great idea! Why? Well currently most Mac malware is distributed as trojans (e.g. cracked versions of Photoshop, infected BitTorrent clients, or fake installers (e.g. Flash, etc)). Such malware when executed by the user, will often display an authentication prompt. If the user naively provides their login credentials, the malicious code will be elevated root. For example here's iWorm authentication prompt:
Before El Capitan, where SIP was not present and code running as root had no permission restrictions, the malware could then do a lot of damage - such as infecting OS components in an very insidious manner. Now, thanks to SIP, gaining root does not mean total system compromise and the 'damage' such malware can achieve is limited.
As the implementation details of System Integrity Protection have been covered before, (for example in Esser's "OS X El Capitan - Sinking the S/H/IP" presentation) we won't spend too much time on them here. Basically, as described by Esser, SIP is "mostly a sandbox around the whole system/platform" that is internally called a "platform profile." This sandbox profile, enforced by the sandbox logic in the kernel (Sandbox.kext, etc), denies the aforementioned operations such as modifying OS components. (For details on the OS X sandbox, see "The Apple Sandbox").
To view the current status of System Integrity Protection, one can use the csrutil utility, with the 'status' flag:
patrick$ csrutil status
System Integrity Protection status: enabled.
This utility can also be used to turn of SIP, but only from Recovery Mode. Attempts to disable SIP from OS X/macOS will (obviously) fail:
patrick$ csrutil disable
csrutil: failed to modify system integrity configuration. This tool needs to be executed from the Recovery OS.
Under the hood, the status or state, of SIP is persistently stored in the nvram. Use the 'nvram' command with the -p flag and look for the 'csr-active-config' variable to view SIP's state in the nvram:
patrick$ nvram -p
Finally to view what files in a given directory are protected by SIP, use the 'ls' command with the -O flag. Files or directories that are marked as 'restricted' are protected by SIP:
patrick$ ls -laO /
drwxrwxr-x+ 45 root admin sunlnk 1530 Nov 22 17:35 Applications
drwxr-xr-x@ 4 root wheel restricted 136 Nov 15 01:20 System
Now obviously various Apple system components need to be able to modify OS or system files that are protected by SIP. For example, perhaps an Apple installer or OS updater application. To afford this functionally when SIP is enabled, Apple added a few new entitlements such as com.apple.rootless.install. As detailed by Esser the aforementioned presentation, entitlements are simply "snippets of XML embedded in code signature" that "give a binary special permissions"
To view the entitlements of a binary, use the codesign utility in the following manner: codesign -d --entitlements - /path/to/binary.
For example, let's dump the entitlements of a binary that's used in system installs and updates; system_shove
patrick$ codesign -d --entitlements - /System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/system_shove
<?xml version="1.0" encoding="UTF-8"?>
As expected, it contains an entitlement, com.apple.rootless.install. This says to the system, "hey OS, even if SIP is enabled, I'm allowed to update core OS/system files." Since entitlements are embedded in the code signature, and thus can be cryptographically verified, the OS will 'agree', allowing the entitled installer application to proceed and modify any file on the file system.
Bypassing System Integrity Protection
At this point, we have a decent understanding of the goals of Apple's System Integrity Protection and how, at a basic level it works and is enforced. However, what good is a security mitigation if it can be bypassed and worse yet, used in a manner that actually can afford malicious code more protections!? Good question ;)
Recently I gave a presentation at both ZeroNights in Moscow and Syscan360 in Shanghai were I detailed some research into the Recovery OS and the local OS X upgrade process (i.e. from El Capitan to macOS Sierra). Besides discovering that VmWare does not verify important boot image signatures (seriously, rly guys?!), I uncovered a way to abuse the OS upgrade/installer application to bypass the OS-level file protections provided by SIP. Via this attack, malware can modify OS components and system files, or insert itself into SIP protected directories, preventing its removal even by code running as root! As Apple has yet to fix this vulnerability, this currently is a 0day.
While the full slides of the talks can be found here; I wanted to also blog about the vulnerability. So here goes!
One of ways Apple locked down the OS to support SIP, was preventing the startup disk from being set programmatically. This makes complete sense, as if an attacker can programmatically set the startup disk they can force the system to boot into a secondary, 'attacker-controlled' OS. From this secondary OS, code can modify the original OS, for example directly modifying system files. As System Integrity Protection is a runtime 'OS-level' mechanism (of the primary OS), if the primary OS is not running - neither is SIP. As mentioned, Apple understood this risk, or attack vector quite well:
Specifically, they state, "To safeguard against disabling System Integrity Protection by modifying security configuration from another OS, the startup disk can no longer be set programmatically, such as by invoking the bless(8) command."
The bless command (/usr/sbin/bless), is designed to set volume bootability and startup disk options. However, with the introduction of System Integrity Protection, Apple attempted to limit it use. Specifically, as it does not contain any entitlements (such as com.apple.rootless.install), it appears it no longer can be used to set the boot volume:
patrick$ $ codesign -d --entitlements - /usr/sbin/bless
While this 'broke' a lot of legitimate 3rd-party tools that needed to programmatically set the setup disk, it did seem to block a very real SIP bypass attack vector:
Apple correctly understands that if an attacker can bless a malicious volume or disk image, upon reboot the system will boot into that secondary, untrusted OS. Here, an attacker's code could easily modify the primary macOS, for example by infecting, or subverting SIP protected files. However, as is often the case, Apple's attempts are trivial to circumvent (sorry guys/gals!).
For my conference talk, I researched the local OS upgrade process. My main goal for this research was to illustrate that advanced malware could 'survive' this generally destructive event. Traditionally, depending on how malware is persisted, and OS upgrade can be a death knell. For example, if malware has infected a system directory or file to ensure it is automatically restarted each time the system is rebooted, it is likely that during the upgrade process that malware will be inadvertently removed.
The upgrade process is rather involved (see the slides for details), but can be broken down into two phases. First, a user downloads the OS upgrade installer application (i.e. Install macOS Sierra.app) from the Mac App Store. Then, the upgrade/installer application sets up the system to boot off an installer disk image (that is embedded within the downloaded application). This installer disk image contains the logic to upgrade the OS, for example from OS X El Capitan, to macOS Sierra.
In order to boot the system off the upgrade/installer image (InstallESD.dmg), the Install macOS Sierra.app utilizes the bless utility:
patrick # tail -f /var/log/install.log
InstallAssistant: Blessing /Volumes/Macintosh HD --
/Volumes/Macintosh HD/macOS Install Data
InstallAssistant: ****** Setting Startup Disk ******
InstallAssistant: ****** Path: /Volumes/Macintosh HD
InstallAssistant: ****** Boot Plist: /Volumes/Macintosh HD/macOS Install Data/com.apple.Boot.plist
InstallAssistant: /usr/sbin/bless -setBoot -folder /Volumes/Macintosh HD/
macOS Install Data -bootefi /Volumes/Macintosh HD/macOS Install Data/boot.efi
-options config="\macOS Install Data\com.apple.Boot" -label macOS Installer
Previously, we mentioned that the bless utility falls under the draconian control of SIP...how then does this succeed? Well, first observe that the bless utility is still required by Apple to perform legitimate OS actions such as blessing installer/upgrade disk images. In other words, yes, SIP has limited who can use the bless command, but if you are Apple, it is still very much available for use. The mechanism to control this, are of course entitlements. Specifically, if you are correctly entitled process, you can still utilized the bless command, even when SIP is enabled.
In terms of an OS upgrade, the Install macOS Sierra.app executes a helper application, 'osishelperd' to perform various upgrade-related action. One of these actions is blessing the system to boot off the installer/upgrade disk image. Let's dump the entitlements for osishelperd:
patrick $ codesign -d --entitlements - /Applications/Install\ macOS\ Sierra.app/Contents/Frameworks/OSInstallerSetup.framework/Versions/A/Resources/osishelperd
Besides the com.apple.rootless.install entitlement (which tells the system this binary is allowed to modify SIP protected files), there is a new entitlement 'com.apple.rootless.install.heritable'. If we google this entitlement, there is only one hit; one of my previous tweets, alluding to my fondness of it :P In other words, this entitlement is undocumented.
From its name we can guess it likely affords child process it spawns the ability to inherit the parent's 'rootless' entitlement. Thus, when it executes the bless utility, bless inherits the necessary entitlements to function (i.e. bless a disk image) in a SIP environment.
Ok, so that answers the question, "how is the OS upgrade installer able to bless the system to boot off the installer disk image?" Now, on to subverting this process in order to bypass SIP!
Like most security vulnerabilities I've uncovered on OS X or macOS, in retrospect they are pretty basic. The crux of the bug is that the upgrade image that the installer/upgrade application bless does not appear to be verified....by either the bless utility nor the boot loader. What this means is that if we are able to modify the upgrade image (InstallESD.dmg) before the system boots off it, we win :)
Really, the only thing that is a touch complex, is ensuring we have a predictable and sufficient time window to infect the InstallESD.dmg before the system is restarted to boot off it. The most reliable way to accomplish this, is to subvert either the "Install macOS Sierra" application, or the osishelperd binary. As these control the upgrade progress (and restarting system to boot off the InstallESD.dmg), controlling or subverting either ensures we can dedicate their logic. For example, pausing them while we infect InstallESD.dmg in the background.
There are variety of ways to subvert an application. However, some more invasive ones such as mach-O binary infection, are off the table. This is due to the fact that if one modifies a signed-binary in any way, this will invalid its digital signature, which in turn will invalidate its entitlements (such as com.apple.rootless.install). A far more elegant approach is simply to replace a dynamic library that the application or one of its frameworks depends on. If done in the correct manner, whenever the application is started, the dynamic loader (dyld) may naively load and execute the malicious dylib in the context of the application:
Of course to replace a system library, one has to bypass SIP. However dylibs 'local' to the application (i.e. within its application bundle), can be replaced no problem. So for example, to 'inject' a malicious dylib into the installer application, one can replace (or rather proxy), its libBaseIA dylib:
Now, whenever the installer application is started by the user (i.e. to upgrade the system) our malicious dylib (named libBaseIA.dylib) will also be loaded and executed in the installer as well:
# vmmap /Install OS X El Capitan.app/Contents/MacOS/InstallAssistant
==== Non-writable regions for process 1055
__TEXT r-x/rwx SM=COW .../PlugIns/IA.bundle/Contents/MacOS/libBaseIA.dylib
Now 'inside' the installer application, we can control the this phase of the upgrade process. Since the installer will 'bless' the image, all we have to do is subvert the image, InstallESD.dmg, before it's used. Reversing the installer application reveals a candidate location to intercept installer logic in order to infect the image in a transparent manner.
The infection, or subversion of InstallESD.dmg could easily constitute an entire blog post, but I'm going to skip over some details and provide more of an overview here. First, let's cover how, or rather, 'when' to infect the it.
When the installer application is executed, it will eventually prompts the user to reboot. Reversing reveals a method within that installer application named 'startPrepareReboot' which is invoked when the user clicks 'Restart.' The 'startPrepareReboot' method invokes a helper method named 'extractBootBits' which mounts the InstallESD.dmg and then extracts various embedded files that are required to boot the system into the phase two of the install process. If we hook the 'extractBootBits' method, we can infect the InstallESD.dmg right before it's used! Perfect :)
For binaries that are written in Objective-C, one can easily hook a method, by a technique named 'swizzling.' As shown in the following image, via swizzling, one can swap, or replace, a method's implementation. Once swizzled, whenever code attempts to execute the original method, the Objective-C runtime will transparently redirect program control flow into the 'new' (in this case malicious) implementation.
Ok, so now we have a 'hook' in place that is automatically invoked right before the installer application will mount and use the InstallESD.dmg. This of course, is the perfect time to infect the disk image. So how to infect?
Turns out within the installer image (InstallESD.dmg) there is another embedded disk image BaseSystem.dmg. This is what we'll infect in order to bypass SIP:
This BaseSystem.dmg is the 'root file-system' of the upgrade code, which is actually an entire stripped-down instance of OS X. When the "Install macOS Sierra" (or "Install OS X El Capitan") application boots the system into the second phase of the install/upgrade process, code within the BaseSystem.dmg is executed:
patrick # tail -f /var/log/install.log
InstallAssistant: Generating the com.apple.Boot.plist file
"Kernel Cache" = "/macOS Install Data/prelinkedkernel";
"Kernel Flags" = "container-dmg=file:///macOSX%20Install%20Data/
Specifically, an "OS X Installer" (or "macOS Installer") application, found within the /System/Installation/CDIS/ directory is executed and kicks off the actual upgrade of the OS. By dumping the install log, we can see the output from this application (note: the binary of this installer application is named 'OSInstaller' and it utilizes the 'PackageKit' framework):
patrick # less /var/log/install.log
OSInstaller: PackageKit: ----- Begin install -----
OSInstaller: PackageKit: packages(
OSInstaller: Installed "OS X" ()
OSInstaller: PackageKit: ----- End install -----
In order to bypass SIP and modify files on the OS X/macOS instance that is being upgraded, we can simply inject a dynamic library into the BaseSystem.dmg:
By injecting a dylib into the installer application (found within the BaseSystem.dmg), our malicious code will be running within the context of a process that can modify OS-level files, normally protected by SIP. This means we can not only survive an OS upgraded, but also persist malware in very subversive manner.
As shown in the following image, I choose to persist iWorm, a well-known piece of OS X malware from 2014. Once injected into a SIP protected directory /System/Library/LaunchDaemons, later, even code that is running as root cannot delete the malware....as now its protected by SIP. Evil ;)
In short, since the OS upgrader is allowed to bless images that aren't validated, we can coerce the system to boot off a malicious image. Once this occurs, OS-level protections, such as SIP are moot.
Again, for more details, see the slides!
Q: Did you really go Russia and China and drop an 0day?
Q: Oh...why? And does Apple know about this vulnerability?
A: To get more twitter follows of course! J/K! Conferences such as ZeroNights and Syscan360 have a 'Call for Papers' that require abstracts several months before the conferences. Before submitting either abstract, I reported this SIP-bypass vulnerability to Apple. Moreover, before submitting either abstract, I confirmed that both conferences fell after the vulnerability disclosure deadline (60 days). Unfortunately, discussions with Apple just prior to the conferences, confirmed that the fix for this issue is rather complex, involving several 'delicate' components of the OS (such as the installer). Thus, at the time of my presentations, and even now, the vulnerability remains unpatched. Sure I could have pulled out of the conferences, but that's no fun ;)
Q: As a Mac user, am I at risk?
A: Yes, but only if a hacker has already has hacked your computer. That is to say, this vulnerability is not remotely exploitable. Sure, it's very useful in the second-stage of an advanced attack (for example to subvert the OS or persist malware in a rather evil manner), but any attacker first has to gain access to your Mac. Moreover, there are other ways to bypass SIP, such as loading a kernel extension that is signed by the attacker - or better yet, loading a signed, but vulnerable 3rd-party extension that can be locally exploited. (See my DefCon talk for details how one could abuse Little Snitch for such an attack).
Q: Is the only 'attack-window' during an OS-upgrade?
A: I don't believe so, and this is something I'm still investigating. The security issue, is that we can coerce the system to boot of a malicious disk image. I don't see anything that ties this to an OS upgrade per-se. In other words, it's unlikely an attacker would have to wait until the user upgrades their OS to trigger this attack...they could likely initiate it at any time. Stay tuned for more on this :)