Extended Attributes and TCC on macOS
Introduction
This blogpost will describe how Transparency, Consent, and Control (TCC) affects extended attributes on macOS.
TCC is a control that limits an application’s ability to interact with various services on the operating system. It controls access to services such as camera, microphone, contacts, photos, folders on disc, location services, and more. If you want to know more about TCC, I highly recommend you read this blogpost by Keith Johnson. This blogpost will assume the reader has a basic understanding of TCC.
While doing Hermes development, I ran into an issue where my ls
implementation caused a TCC popup when attempting to list out the user’s home directory (/Users/slyd0g
). This blogpost details the issue in my original implementation, journey investigating the root cause, and how I fixed the issue within Hermes.
Hermes Generating TCC Popups
In Hermes’ original ls
implementation, I pull as much data as possible from files/folders. This data includes:
- Name
- Size
- Posix permissions
- Owner
- Group
- Hidden
- Create time
- Modify time
- Extended attributes
Whenever ls ~/
was issued in Hermes, a TCC popup was generated to access the user’s TCC-protected folders: ~/Downloads
, ~/Documents
, and ~/Desktop
.
This was especially concerning because in previous testing this did not occur and this could potentially ruin a red team operation if a user reports the suspicious activity. My ls
implementation was almost identical to Cody Thomas’ within his Apfell agent so I also tried it ls ~/
from Apfell. This also caused a TCC popup.
What data are we pulling that causes this? The ls
call does not access anything within the TCC-protected folder which would definitely cause a popup.
Root Cause Analysis
I re-wrote my ls
implementation in a basic Swift program to see if I could pin down what exactly was generating this popup.
The culprit?
let attributes = try fileManager.attributesOfItem(atPath: fullPath)
This returns a FileAttributeKey
struct which holds various information about a file/folder. Why would accessing this data cause a TCC popup for the TCC-protected folders?
Comparing Attributes
I decided to print the FileAttributeKey
struct for a single TCC-protected folder under two scenarios:
- TCC-prompt denied
/Users/slyd0g/Downloads[__C.NSFileAttributeKey(_rawValue: NSFileOwnerAccountID): 501, __C.NSFileAttributeKey(_rawValue: NSFileGroupOwnerAccountID): 20, __C.NSFileAttributeKey(_rawValue: NSFilePosixPermissions): 448, __C.NSFileAttributeKey(_rawValue: NSFileGroupOwnerAccountName): staff, __C.NSFileAttributeKey(_rawValue: NSFileOwnerAccountName): slyd0g, __C.NSFileAttributeKey(_rawValue: NSFileCreationDate): 2020-01-01 08:00:00 +0000, __C.NSFileAttributeKey(_rawValue: NSFileSize): 192, __C.NSFileAttributeKey(_rawValue: NSFileSystemNumber): 16777221, __C.NSFileAttributeKey(_rawValue: NSFileExtensionHidden): 0, __C.NSFileAttributeKey(_rawValue: NSFileSystemFileNumber): 492303, __C.NSFileAttributeKey(_rawValue: NSFileType): NSFileTypeDirectory, __C.NSFileAttributeKey(_rawValue: NSFileModificationDate): 2022-03-04 06:02:47 +0000, __C.NSFileAttributeKey(_rawValue: NSFileProtectionKey): NSFileProtectionCompleteUntilFirstUserAuthentication, __C.NSFileAttributeKey(_rawValue: NSFileReferenceCount): 6]
- TCC-prompt accepted
/Users/slyd0g/Downloads[__C.NSFileAttributeKey(_rawValue: NSFileOwnerAccountID): 501, __C.NSFileAttributeKey(_rawValue: NSFileGroupOwnerAccountName): staff, __C.NSFileAttributeKey(_rawValue: NSFileProtectionKey): NSFileProtectionCompleteUntilFirstUserAuthentication, __C.NSFileAttributeKey(_rawValue: NSFileExtendedAttributes): {"com.apple.macl" = {length = 72, bytes = 0x0400859d 0df21250 44afa8ed e5ec9f0d ... 00000000 00000000 };}, __C.NSFileAttributeKey(_rawValue: NSFilePosixPermissions): 448, __C.NSFileAttributeKey(_rawValue: NSFileReferenceCount): 6, __C.NSFileAttributeKey(_rawValue: NSFileCreationDate): 2020-01-01 08:00:00 +0000, __C.NSFileAttributeKey(_rawValue: NSFileSystemFileNumber): 492303, __C.NSFileAttributeKey(_rawValue: NSFileType): NSFileTypeDirectory, __C.NSFileAttributeKey(_rawValue: NSFileModificationDate): 2022-03-04 06:02:47 +0000, __C.NSFileAttributeKey(_rawValue: NSFileExtensionHidden): 0, __C.NSFileAttributeKey(_rawValue: NSFileSystemNumber): 16777221, __C.NSFileAttributeKey(_rawValue: NSFileGroupOwnerAccountID): 20, __C.NSFileAttributeKey(_rawValue: NSFileSize): 192, __C.NSFileAttributeKey(_rawValue: NSFileOwnerAccountName): slyd0g]
Ah ha! The difference in data generated when TCC permissions were granted was NSFileExtendedAttributes
. This is weird though, I know that I’ve printed extended attributes before with xattr
without generating any TCC prompts.
Investigating Extended Attributes with xattr
Before investigating anything related to TCC, it’s always a good idea to reset your TCC database.
sudo tccutil reset All
After resetting the TCC database, I looked at the extended attributes for all of the TCC-protected folders.
This did not cause any TCC popups. Does xattr
have some sort of entitlement that might allow it to access this information without generating a TCC popup?
xattr
isn’t signed and doesn’t have any special entitlements. On macOS Big Sur, xattr
is simple a wrapper for the Python xattr
package.
While enumerating the FileAttributeKey
struct, there appeared to be some binary blob associated with the extended attribute.
__C.NSFileAttributeKey(_rawValue: NSFileExtendedAttributes): {"com.apple.macl" = {length = 72, bytes = 0x0400859d 0df21250 44afa8ed e5ec9f0d ... 00000000 00000000 };}
Let’s try to enumerate this binary blob with xattr
Interesting! It appears that the extended attribute is a key-value pair where the key is not protected by TCC, but the value is protected by TCC.
After accepting the TCC popup, we see the binary blob that matches our Swift program.
Fixing Hermes ls Implementation
In Hermes, I have two situational awareness commands to help enumerate your current TCC permissions:
- fda_check
- tcc_folder_check
The former attempts to grab a file handle to ~/Library/Application Support/com.apple.TCC/TCC.db
, if a file handle is obtained, our current process has Full Disk Access
permissions. The latter uses MDQuery*
APIs to determine if you have access to TCC-protected folders. Both techniques were inspired by @cedowens here.
If fda_check
or tcc_folder_check
is successful, I set various global variables that allow future ls
calls to enumerate the attributes for TCC-protected folders.
I went with this approach to minimize the amount of API calls within the ls
command and to hopefully minimize behavior that could be signatured. This way the TCC enumeration does not have to happen for every single ls
command and only needs to be done once per agent.
Conclusion
In this post, I walked through the initial bug, root cause, things learned along the way, and how I handle the bug in Hermes. To recap, attempting to get folder attributes for TCC-protected folders using Swift/ObjC APIs will result in a TCC popup.
The TCC popup occurs because the extended attribute value is protected by TCC, while the extended attribute key is not.
Thanks for taking the time to read this post, I hope you learned a little about macOS, TCC, and extended attributes!