In this post I will document two simple logic bugs I reported to Apple last year. The first, CVE-2019-8855, had been previously found by their own team and a fix was deployed last year. The second, CVE-2020-3889 was fixed in the latest security update.
The two bugs are very similar, and both can be exploited with a few commands in a terminal. They are quite simple, and they demonstrate that sometimes a security boundary can be challenged with some guesswork, without knowing all the intricate details of the implementation.
Both bugs allowed access to files that should have been protected by the privacy functionality of macOS. This functionality is in place to prevent applications from reading certain files if they do not have explicit permissions granted to them. Permissions can come in the form of entitlements granted to built-in applications or via the user explicitly granting access upon request. Depending on the files attempted accessed, different permissions apply, like “Access to Calendars”, “Access to Photos” or the more widely permissive “Full Disk Access”, meant for backup solutions and security products.
This privacy functionality coexists with the basic Unix-style permissions of macOS, so for an application to access the Calendar data of a user, it would need to be granted “Access to Calendars” and be running as that user or as root. Being root does not in itself sidestep this privacy functionality.
During an engagement, after using this bug to achieve code execution as root, I looked for interesting files to access, to demonstrate the effects of the privilege escalation. As access to some of the most interesting files were being blocked, even for root, I decided to explore the privacy functionality closer, to gauge the strength of it as a security boundary. I quickly turned my attention to Time Machine, macOS’ backup and restore facility. My assumption was this: If you can back up a Mac and later restore it fully, all the files must exist in the backup, including any protected files. The file I initially attempted to access was
/private/var/db/dslocal/nodes/Default/users/user.plist (containing the password hash of my user), and as seen from the image below, it is not accessible, even to root. There is another way to extract the password hashes (
dscl . read /Users/user dsAttrTypeNative:ShadowHashData), but what we are interested in now is reading the file directly, to see if Time Machine can be used to circumvent the added protection of the file.
Creating a Time Machine image can be done with the following steps:
The image above shows the usage of exclusions to keep the image to a minimum, and the backup should be complete in a few minutes.
Now we have a file we can exfiltrate and carve. On Linux the image can be extracted and mounted with a few commands, if the necessary applications and drivers are installed:
And the file is now accessible:
So my initial assumption that Time Machine images would contain the protected files is correct. While you can cut down what files are backed up to limit the size of the image, it would still be preferable to mount the image locally and extract the needed files. Doing this, and attempting to access a protected file, results in this message:
Apparently, there is an attempt to maintain the privacy functionality on locally mounted Time Machine volumes. A small test shows it to be a flimsy control, as a simple renaming of a folder breaks it, leaving the file readable:
Note that the creation of a Time Machine image requires root privileges, so regular users will not be able to circumvent the privacy functionality with this bug. Apple fixed this bug by requiring the “Full Disk Access” privilege to modify the destination of Time Machine backups:
I consider this bug version 2 of the one above, but without the requirement to run as root. The regular file permissions still apply, though, so to read other users files you will need root access. After testing the deployed fix for the first bug, I decided to further examine the issue in the fixed Catalina version.
I revisited the tmutil command, looking for more implicit ways of performing a snapshot, without needing the “Full Disk Access” permission. Looking at the man page, I found references to local snapshots.
As it turns out, these snapshots could be performed without the Full Disk Access privilege and even by non-root users.
Now I needed to find where these local snapshots were stored. Some googling this led me to pages mentioning that the snapshot data was stored in /.MobileBackups and mounted under /Volumes/MobileBackups. These folders did not exist on my system, and further googling revealed that with the newer APFS filesystem, these snapshots are integrated at the filesystem level, unreachable from the user. I decided to look closer at the tmutil executable to see if there could be undocumented features that looked interesting. A closer inspection of the image revealed the following string:
While not appearing in the man pages, an option to mount the local snapshots seemed to be present. Running it showed me the needed arguments:
The following command allowed me to mount a local snapshot, but I found that the files were still not accessible.
Renaming the folder seems to be accounted for, as I am not allowed to rename the mounted folder.
Since the local snapshots were mounted at the same mountpoint every time, I tried to see what would happen if I mounted something else at that place first.
As seen above, no new volumes appeared to be mounted in /Volumes. I was still not able to access any protected files, but the lack of error messages when mounting the snapshot on top of the image hinted at some confusion. I tried taking advantage of this by renaming my mounted image using diskutils. This worked, and I was now able to access files that should have been protected. In the image below I am accessing the Safari folder, containing my browsing history. If I were running as root, I could have accessed the plist file containing the users hash I used to demonstrate the bug above, but since this bug is usable by non-root users, I have demonstrated it with some of the users own protected files.
I haven't really looked into the fix, but on superficial inspection, it appears to be preventing the mounting of snapshots when a volume is already mounted in that place: