Нещодавно я натрапив на функцію постійності в macOS, яка пов’язана з плагінами плиток Dock. 

Плитки Dock – це маленькі іконки, які з’являються на панелі Dock під час запуску програми. Плагіни для цих плиток Dock доступні починаючи з macOS Snow Leopard (10.6). У своїй документації для розробників Apple розповідає про них таке: 

Набір методів, реалізованих за допомогою плагінів… дозволяє налаштовувати плитку Dock програми, поки програма не запущена.

Наприклад, завдяки таким плагінам на плитці дока Facetime можуть відображатися нещодавні дзвінки:

У документації також сказано: 

Плагін завантажується в системний процес під час входу в систему або коли плитку програми додано в Dock.

«Завантажується в системний процес під час входу в систему» означає присутність цього шматочка коду, незалежно від того, чим займається користувач. Якщо ці плагіни мають вразливість, така постійна присутність означає, що вона може бути використана. 

 

Вразливість

 

Якщо додаток з плиткою Dock розміщено в директорії, наприклад, /Users/Shared, де його може «бачити» кожен користувач, то плагін буде розпізнано і завантажено в процес кожного користувача. Якщо звичайний користувач розмістить додаток у такому каталозі, а користувач з правами адміністратора увійде в систему, плагін буде виконано в контексті користувача з правами адміністратора, що призведе до підвищення привілеїв від звичайного користувача до користувача з правами адміністратора.

Ось кілька логів з розробленого мною тестового прикладу. Ми бачимо, що плагін завантажується в різні процеси (1605 і 1606). 

test@vm ~ % log stream | grep BEYOND

2023-10-02 10:58:46.049386-0700 0x5ad0 Default 0x0    1606   0 com.apple.dock.external.extra.arm64: (DuckDockTilePlugin) BEYOND setDockTile was called

2023-10-02 10:58:57.619373-0700 0x5aba Default 0x0    1605   0 com.apple.dock.external.extra.arm64: (DuckDockTilePlugin) BEYOND doSomething was called

Якщо ми перевіримо процеси в Моніторі активності, то побачимо, що вони належать двом окремим користувачам: rookie (стандартний) і Lizard (адміністратор):

За допомогою Провідника завдань ми можемо переконатися, що плагін дійсно було завантажено з /Users/Shared:

 

VM Escape та його обмеження

 

Оскільки ці плагіни автоматично знаходять і запускаються системним процесом, якщо зловмисник закине такий додаток до системи, він може отримати виконання коду. (Автоматичний запуск працює лише за відсутності атрибуту карантину, тому ми не можемо використовувати його для обходу Gatekeeper). Якщо на віртуальній машині увімкнено спільні папки, хтось може скинути додаток з плагіном на хост-систему, і він буде виконаний; це, по суті, універсальний спосіб втечі з віртуальної машини. 

При цьому не має значення, де ви розміщуєте програму. Це тому, що плиткові плагіни Dock шукаються і завантажуються Dock, який перевіряє лише певні місця. У методі commonInit джерела LPAppSource програми ми можемо знайти каталоги, які скануються:

void __cdecl -[LPAppSource commonInit](LPAppSource *self, SEL a2)
{
    NSString *path; // r14
    __CFString *v4; // rax
    NSString *v5; // rax
    NSString *v6; // rax
    NSString *v7; // rax
    NSString *v8; // rdi
    NSOperationQueue *v9; // rax
    NSOperationQueue *processQueue; // rdi
    id v11; // rdi
    _QWORD block[7]; // [rsp+8h] [rbp-38h] BYREF

    switch ( self->_location )
    {
        case 0LL:
        path = self->_path;
        self->_path = 0LL;
        goto LABEL_10;
        case 1LL:
        path = self->_path;
        v4 = CFSTR("/System/Applications");
        goto LABEL_9;
        case 2LL:
        path = self->_path;
        v4 = CFSTR("/Applications");
        goto LABEL_9;
        case 3LL:
        v5 = NSHomeDirectory();
        path = objc_retainAutoreleasedReturnValue(v5);
        a2 = "stringByAppendingPathComponent:";
        v6 = -[NSString stringByAppendingPathComponent:](path, "stringByAppendingPathComponent:", CFSTR("Applications"));
        v7 = objc_retainAutoreleasedReturnValue(v6);
        v8 = self->_path;
        self->_path = v7;
        objc_release(v8);
        goto LABEL_10;
        case 4LL:
        path = self->_path;
        v4 = CFSTR("/Users/Shared");
        goto LABEL_9;
        case 5LL:
        path = self->_path;
        v4 = CFSTR("/AppleInternal/Applications");
        goto LABEL_9;
        case 6LL:
        path = self->_path;
        v4 = CFSTR("/System/Volumes/Preboot/Cryptexes/App/System/Applications");
LABEL_9:
    self->_path = &v4->isa;
LABEL_10:
    objc_release(path);
    break;
    default:
    break;
    }

 

Він сканує всі стандартні папки /Application і /Users/Shared. Оскільки я використовую цей каталог для спільних папок віртуальних машин, я можу використовувати його для втечі з віртуальної машини. Хоча підвищення привілеїв завжди буде працювати в багатокористувацькій системі, втеча з віртуальної машини є більш обмеженою.

 

Виправлення

 

Компанія Apple виправила дану уразливість в macOS Sonoma 14.4.  Було створено новий клас AppDataContainer, за допомогою якого плагін перевіряється з допомогою методу initWithAppBundleURL:.

AppDataContainer *__cdecl -[AppDataContainer initWithAppBundleURL:](AppDataContainer *self, SEL a2, id a3) 
...
    container_query_set_class(v13, 2LL);
    container_query_operation_set_flags(v14, 0x900000002LL);
    container_query_set_persona_unique_string(v14, CONTAINER_PERSONA_PRIMARY);
    container_query_set_identifiers(v14, v12);
    single_result = container_query_get_single_result(v14);
    if ( !single_result )
    {
        NSLog((NSString *)CFSTR("No data container for app bundle %@"), v3);
        container_query_free(v14);
        goto LABEL_31;
    }
    v16 = single_result;
    v32 = v12;
    path = (const char *)container_get_path();
    if ( !path )
    {
        NSLog(&CFSTR("No data container path for app bundle %@").isa, v3);
        goto LABEL_30;
    }
    v18 = path;
    v29 = strlen(path);
    if ( !v29 )
    {
        NSLog((NSString *)CFSTR("Zero length data container path for app bundle %@"), v3);
        goto LABEL_30;
    }
...

 

За допомогою функції container_query* здійснюється виклик containermanagerd для перевірки наявності контейнера для програми. Ця перевірка буде пройдена лише у тому випадку, якщо основний додаток був запущений користувачем хоча б один раз. Якщо програму запускав інший користувач, контейнер існуватиме лише для цього користувача, але не для інших. Таким чином, ми не можемо використовувати його для ескалації привілеїв. Оскільки плагін не виконується автоматично, ми також не можемо використовувати його для втечі з віртуальної машини.

 

Висновки

 

Встановлення оновлень macOS є критично важливим, оскільки вони закривають вразливості, які можуть виникати навіть у віртуальних машинах. Ці вразливості можуть бути використані зловмисниками для компрометації системи та викрадення даних. Особливо це важливо для IT-фахівців та системних адміністраторів, які активно використовують віртуальні машини для тестування, розробки та адміністрування мереж. Оновлення системи забезпечують додатковий рівень безпеки та стабільності, що є ключовим фактором для ефективної роботи та захисту корпоративних даних.