
VMWare Horizon клиент для macOS LPE из-за логической ошибки XPC. Запоздалый POC для 0-day, который я ответственно раскрыл Omnissa.
Операционная система macOS использует службы XPC для базовой межпроцессной связи между различными процессами, например, между демоном XPC Service и привилегированными вспомогательными инструментами сторонних приложений. Клиент VMware Horizon Client при установке регистрирует привилегированный помощник com.vmware.horizon.CDSHelper в каталоге /Library/PrivilegedHelperTools/.
Следует также отметить, что имя mach com.vmware.horizon.CDSHelper используется привилегированным помощником для прослушивания входящих XPC-соединений, как показано выше. Затем с помощью стандартных инструментов разработчика macOS были извлечены символы из двоичного файла помощника, чтобы убедиться, что он использует 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
Затем инструмент Hopper Disassembler был использован для загрузки помощника в декомпилятор для дальнейшего реверс-инжиниринга, как показано ниже: open -a /Applications/Hopper\ Disassembler\ v4.app /Library/PrivilegedHelperTools/com.vmware.horizon.CDSHelper Функция EntryPoint() была найдена и проанализирована, подтвердив, что она представляет собой основную точку входа CDSHelper.
Функция sub_100005fa0 была декомпилирована, что подтвердило использование привилегированным помощником клиента VMWare Horizon Client API служб XPC — это API нижнего уровня на C, предоставляемый Apple, в отличие от API соединений XPC, который находится на уровне 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;
}
После создания службы mach (через xpc_connection_create_mach_service(com.vmware.horizon.CDSHelper)) обмен сообщениями между клиентом и XPC-службой Horizon Client осуществляется через объекты словарей XPC, создаваемые методом xpc_dictionary_create(). В этот словарь можно добавлять значения с помощью методов, предоставляемых Apple разработчикам; обычно они начинаются с xpc_dictionary_set_. Клиент может отправлять эти объекты словаря XPC в XPC-службу через такие методы, как xpc_connection_send_. На стороне XPC-службы Horizon Client регистрируется обработчик, который может обрабатывать полученные от клиента объекты словаря XPC. Это делается с помощью метода xpc_connection_set_event_handler(). Наконец, значения из полученного объекта словаря XPC считываются с помощью таких методов, как xpc_dictionary_get_*. После возврата EntryPoint() из CDSHelper вызывается функция sub_100056d0:
На этом этапе управление передаётся sub_1000056ec, которая выполняет основную работу по обработке входящих XPC-сообщений. Полный декомпилированный код функции приведён ниже. Уязвимые участки будут рассмотрены сразу после него.
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;