Airo AV |
The Transparency, Consent, and Control
(TCC
) Framework is an Apple subsystem which denies installed applications access to ‘sensitive’ user data without explicit permission from the user (generally in the form of a pop-up message):
While TCC
also runs on iOS, this bug is restricted to the OS X variant. To learn more about how TCC
works, especially with Catalina, I recommend reading this article
If an application attempts to access files in a directory protected by TCC
without user authorization, the file operation will fail. TCC
stores these user-level entitlements in a SQLite3 database on disk at $HOME/Library/Application Support/com.apple.TCC/TCC.db
. Apple uses a dedicated daemon, tccd
, for each logged-in user (and one system level daemon) to handle TCC
requests. These daemons sit idle until they receive an access request from the OS for an application attempting to access protected data.
When the daemon receives such a request, it first checks the TCC
database to see if the user has either allowed or denied access to the requested data before from this application. If so, TCC uses the previous decision; otherwise it prompts the user to choose whether to allow the application access or not. Thus, if an application can gain write access to this TCC
database, it can not only give itself all TCC
entitlements, but also do it without ever prompting the user.
Obviously being able to write directly to the database completely defeats the purpose of TCC
, so Apple protects this database itself with TCC
and System Integrity Protection
(SIP
). Even a program running as root cannot modify this database unless it has the com.apple.private.tcc.manager
and com.apple.rootless.storage
.TCC
entitlements. However, the database is still technically owned and readable/writeable by the currently running user, so as long as we can find a program with those entitlements, we can control the database.
Since the TCC
daemon is directly responsible for reading and writing to the TCC
database, it’s a prime candidate!
Immediately after opening the TCC
daemon in Ghidra and looking for code that was related to handling database operations, I noticed something that didn’t seem right:
Essentially, when the TCC
daemon attempts to open the database, the program tries to directly open (or create if not already existing) the SQLite3 database at $HOME/Library/Application Support/com.apple.TCC/TCC.db
. While this seems inconspicuous at first, it becomes more interesting when you realize that you can control the location that the TCC daemon reads and writes to if you can control what the $HOME
environment variable contains.
I initially dismissed this as a fun trick as the actual TCC
daemon running via launchd
completely ignores this database and is the only daemon the OS communicates with when doing authorization events. However, a few days later I stumbled across this Stack Exchange post and realized that since the TCC
daemon is running via launchd
within the current user’s domain, I could also control all environment variables passed to it when launched! Thus, I could set the $HOME
environment variable in launchctl
to point to a directory I control, restart the TCC
daemon, and then directly modify the TCC
database to give myself every TCC
entitlement available without ever prompting the end user. As this doesn’t actually modify the SIP
-protected TCC
database, this bug also has the added benefit of completely resetting TCC
to its previous state once $HOME
is unset in launchctl
and the daemon is restarted!
The POC for this bug is actually pretty simple and requires no code to be written:
# reset database just in case (no cheating!) $ tccutil reset All # mimic TCC's directory structure from ~/Library $ mkdir -p "/tmp/tccbypass/Library/Application Support/com.apple.TCC" # cd into the new directory $ cd "/tmp/tccbypass/Library/Application Support/com.apple.TCC/" # set launchd $HOME to this temporary directory $ launchctl setenv HOME /tmp/tccbypass # restart the TCC daemon $ launchctl stop com.apple.tccd && launchctl start com.apple.tccd # print out contents of TCC database and then give Terminal access to Documents $ sqlite3 TCC.db .dump $ sqlite3 TCC.db "INSERT INTO access VALUES('kTCCServiceSystemPolicyDocumentsFolder', 'com.apple.Terminal', 0, 1, 1, X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003', NULL, NULL, 'UNUSED', NULL, NULL, 1333333333333337);" # list Documents directory without prompting the end user $ ls ~/Documents
I also have a full Swift (because why not) writeup available on Github.
Security Update 2020–004
).