
Client VMWare Horizon per macOS con LPE dovuta a un difetto logico XPC. POC tardivo per una 0-day che ho divulgato responsabilmente a Omnissa.
Il sistema operativo macOS utilizza i servizi XPC per la comunicazione inter-processo di base tra vari processi, ad esempio tra il demone XPC Service e gli strumenti helper privilegiati di applicazioni di terze parti. Il client VMware Horizon, al momento dell'installazione, registra l'helper privilegiato com.vmware.horizon.CDSHelper nella directory /Library/PrivilegedHelperTools/.
Va inoltre notato che il nome mach di com.vmware.horizon.CDSHelper viene utilizzato dall'helper privilegiato per rimanere in ascolto delle connessioni XPC in arrivo, come mostrato sopra. Successivamente, sono stati utilizzati gli strumenti di sviluppo predefiniti di macOS per estrarre i simboli dal binario dell'helper, al fine di verificare che incorporasse il comportamento XPC:
» 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
Lo strumento Hopper Disassembler è stato quindi utilizzato per caricare l'helper in un decompilatore per un'ulteriore ingegneria inversa, come mostrato di seguito: open -a /Applications/Hopper\ Disassembler\ v4.app /Library/PrivilegedHelperTools/com.vmware.horizon.CDSHelper La funzione EntryPoint() è stata individuata e analizzata, confermando che rappresentava il punto di ingresso principale di CDSHelper.
La funzione sub_100005fa0 è stata quindi decompilata, confermando che lo strumento helper privilegiato del client VMware Horizon utilizzava l'API dei servizi XPC — questa è l'API di livello C inferiore fornita da Apple, a differenza dell'API di connessione XPC che risiede sul livello Objective-C/Swift:
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;
}
Dopo la creazione del servizio mach (tramite xpc_connection_create_mach_service(com.vmware.horizon.CDSHelper)), i messaggi tra il client e il servizio XPC del client Horizon vengono scambiati tramite oggetti dizionario XPC creati con il metodo xpc_dictionary_create(). È possibile aggiungere valori a questo dizionario utilizzando i metodi che Apple mette a disposizione degli sviluppatori; questi in genere iniziano con xpc_dictionary_set_. Il client può inviare questi oggetti dizionario XPC al servizio XPC tramite metodi come xpc_connection_send_. Sul lato del servizio XPC del client Horizon, viene registrato un handler in grado di gestire gli oggetti dizionario XPC ricevuti dal client. Questo viene fatto utilizzando il metodo xpc_connection_set_event_handler(). Infine, i valori dell'oggetto dizionario XPC ricevuto vengono letti utilizzando metodi come xpc_dictionary_get_*. Dopo che EntryPoint() di CDSHelper restituisce il controllo, viene chiamata la funzione sub_100056d0:
A questo punto, l'esecuzione passa a sub_1000056ec, che gestisce la maggior parte del lavoro di elaborazione dei messaggi XPC in arrivo. L'intera funzione decompilata è riportata di seguito. Le porzioni vulnerabili salienti vengono esaminate subito dopo.
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;