macOS Процесне Зловживання

Reading time: 13 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks

Основна Інформація про Процеси

Процес - це екземпляр виконуваного файлу, однак процеси не виконують код, це потоки. Тому процеси - це лише контейнери для виконуваних потоків, які забезпечують пам'ять, дескриптори, порти, дозволи...

Традиційно, процеси запускалися в межах інших процесів (за винятком PID 1) шляхом виклику fork, що створювало точну копію поточного процесу, а потім дочірній процес зазвичай викликав execve для завантаження нового виконуваного файлу та його виконання. Потім vfork був введений для прискорення цього процесу без копіювання пам'яті.
Потім був введений posix_spawn, який поєднує vfork та execve в одному виклику та приймає прапори:

  • POSIX_SPAWN_RESETIDS: Скинути ефективні ідентифікатори до реальних
  • POSIX_SPAWN_SETPGROUP: Встановити приналежність до групи процесів
  • POSUX_SPAWN_SETSIGDEF: Встановити поведінку за замовчуванням сигналу
  • POSIX_SPAWN_SETSIGMASK: Встановити маску сигналу
  • POSIX_SPAWN_SETEXEC: Виконати в тому ж процесі (як execve з більшою кількістю опцій)
  • POSIX_SPAWN_START_SUSPENDED: Запустити в підвішеному стані
  • _POSIX_SPAWN_DISABLE_ASLR: Запустити без ASLR
  • _POSIX_SPAWN_NANO_ALLOCATOR: Використовувати Nano аллокатор libmalloc
  • _POSIX_SPAWN_ALLOW_DATA_EXEC: Дозволити rwx на сегментах даних
  • POSIX_SPAWN_CLOEXEC_DEFAULT: Закрити всі файлові дескриптори за замовчуванням при exec(2)
  • _POSIX_SPAWN_HIGH_BITS_ASLR: Випадковим чином змінити високі біти слайду ASLR

Більше того, posix_spawn дозволяє вказати масив posix_spawnattr, який контролює деякі аспекти створеного процесу, та posix_spawn_file_actions для зміни стану дескрипторів.

Коли процес завершує свою роботу, він надсилає код повернення батьківському процесу (якщо батьківський процес завершився, новим батьком є PID 1) з сигналом SIGCHLD. Батьківський процес повинен отримати це значення, викликавши wait4() або waitid(), і до того часу дочірній процес залишається в зомбі-стані, де він все ще вказується, але не споживає ресурси.

PIDs

PID, ідентифікатори процесів, ідентифікують унікальний процес. У XNU PIDs мають 64 біти, зростають монотонно і ніколи не обертаються (щоб уникнути зловживань).

Групи Процесів, Сесії та Коаліції

Процеси можуть бути об'єднані в групи, щоб полегшити їх обробку. Наприклад, команди в оболонці будуть в одній групі процесів, тому їх можна сигналізувати разом за допомогою kill, наприклад.
Також можливо групувати процеси в сесії. Коли процес починає сесію (setsid(2)), дочірні процеси потрапляють у цю сесію, якщо вони не починають свою власну сесію.

Коаліція - це ще один спосіб групувати процеси в Darwin. Процес, що приєднується до коаліції, отримує доступ до ресурсів пулу, ділиться реєстром або стикається з Jetsam. Коаліції мають різні ролі: Лідер, XPC сервіс, Розширення.

Облікові Дані та Персони

Кожен процес має облікові дані, які ідентифікують його привілеї в системі. Кожен процес матиме один основний uid та один основний gid (хоча може належати до кількох груп).
Також можливо змінити ідентифікатор користувача та групи, якщо бінарний файл має біт setuid/setgid.
Існує кілька функцій для встановлення нових uid/gid.

Системний виклик persona надає альтернативний набір облікових даних. Прийняття персони передбачає прийняття її uid, gid та членства в групах одразу. У джерельному коді можна знайти структуру:

c
struct kpersona_info { uint32_t persona_info_version;
uid_t    persona_id; /* overlaps with UID */
int      persona_type;
gid_t    persona_gid;
uint32_t persona_ngroups;
gid_t    persona_groups[NGROUPS];
uid_t    persona_gmuid;
char     persona_name[MAXLOGNAME + 1];

/* TODO: MAC policies?! */
}

Основна інформація про потоки

  1. POSIX потоки (pthreads): macOS підтримує POSIX потоки (pthreads), які є частиною стандартного API потоків для C/C++. Реалізація pthreads в macOS знаходиться в /usr/lib/system/libsystem_pthread.dylib, яка походить з публічно доступного проекту libpthread. Ця бібліотека надає необхідні функції для створення та управління потоками.
  2. Створення потоків: Функція pthread_create() використовується для створення нових потоків. Внутрішньо ця функція викликає bsdthread_create(), яка є системним викликом нижчого рівня, специфічним для ядра XNU (ядро, на якому базується macOS). Цей системний виклик приймає різні прапори, отримані з pthread_attr (атрибути), які вказують на поведінку потоку, включаючи політики планування та розмір стеку.
  • Розмір стеку за замовчуванням: Розмір стеку за замовчуванням для нових потоків становить 512 КБ, що є достатнім для типових операцій, але може бути відкориговано через атрибути потоку, якщо потрібно більше або менше місця.
  1. Ініціалізація потоку: Функція __pthread_init() є критично важливою під час налаштування потоку, використовуючи аргумент env[] для парсингу змінних середовища, які можуть містити деталі про місцезнаходження та розмір стеку.

Завершення потоків в macOS

  1. Вихід з потоків: Потоки зазвичай завершуються викликом pthread_exit(). Ця функція дозволяє потоку вийти коректно, виконуючи необхідні дії з очищення та дозволяючи потоку надіслати значення повернення назад до будь-яких приєднувачів.
  2. Очищення потоків: Після виклику pthread_exit() викликається функція pthread_terminate(), яка обробляє видалення всіх асоційованих структур потоку. Вона звільняє порти потоків Mach (Mach є підсистемою зв'язку в ядрі XNU) і викликає bsdthread_terminate, системний виклик, який видаляє структури на рівні ядра, пов'язані з потоком.

Механізми синхронізації

Для управління доступом до спільних ресурсів та уникнення станів гонки macOS надає кілька примітивів синхронізації. Вони є критично важливими в середовищах з багатопоточністю для забезпечення цілісності даних та стабільності системи:

  1. М'ютекси:
  • Звичайний м'ютекс (Signature: 0x4D555458): Стандартний м'ютекс з обсягом пам'яті 60 байт (56 байт для м'ютекса та 4 байти для підпису).
  • Швидкий м'ютекс (Signature: 0x4d55545A): Схожий на звичайний м'ютекс, але оптимізований для швидших операцій, також 60 байт в розмірі.
  1. Змінні умов:
  • Використовуються для очікування настання певних умов, з розміром 44 байти (40 байт плюс 4-байтовий підпис).
  • Атрибути змінних умов (Signature: 0x434e4441): Атрибути конфігурації для змінних умов, розміром 12 байт.
  1. Змінна Once (Signature: 0x4f4e4345):
  • Забезпечує виконання певного коду ініціалізації лише один раз. Її розмір становить 12 байт.
  1. Замки читання-запису:
  • Дозволяють одночасно кільком читачам або одному записувачу, сприяючи ефективному доступу до спільних даних.
  • Замок читання-запису (Signature: 0x52574c4b): Розміром 196 байт.
  • Атрибути замків читання-запису (Signature: 0x52574c41): Атрибути для замків читання-запису, 20 байт в розмірі.

tip

Останні 4 байти цих об'єктів використовуються для виявлення переповнень.

Локальні змінні потоку (TLV)

Локальні змінні потоку (TLV) в контексті файлів Mach-O (формат для виконуваних файлів в macOS) використовуються для оголошення змінних, які є специфічними для кожного потоку в багатопоточному додатку. Це забезпечує, що кожен потік має свій власний окремий екземпляр змінної, що дозволяє уникати конфліктів і підтримувати цілісність даних без необхідності в явних механізмах синхронізації, таких як м'ютекси.

У C та суміжних мовах ви можете оголосити локальну змінну потоку, використовуючи ключове слово __thread. Ось як це працює у вашому прикладі:

c
cCopy code__thread int tlv_var;

void main (int argc, char **argv){
tlv_var = 10;
}

Цей фрагмент визначає tlv_var як змінну, локальну для потоку. Кожен потік, що виконує цей код, матиме свою власну tlv_var, і зміни, які один потік вносить у tlv_var, не вплинуть на tlv_var в іншому потоці.

У бінарному файлі Mach-O дані, пов'язані з локальними для потоку змінними, організовані в специфічні секції:

  • __DATA.__thread_vars: Ця секція містить метадані про локальні для потоку змінні, такі як їх типи та статус ініціалізації.
  • __DATA.__thread_bss: Ця секція використовується для локальних для потоку змінних, які не ініціалізовані явно. Це частина пам'яті, відведена для даних, ініціалізованих нулем.

Mach-O також надає специфічний API під назвою tlv_atexit для управління локальними для потоку змінними, коли потік завершує свою роботу. Цей API дозволяє реєструвати деструктори — спеціальні функції, які очищають локальні для потоку дані, коли потік завершується.

Пріоритети потоків

Розуміння пріоритетів потоків передбачає вивчення того, як операційна система вирішує, які потоки виконувати і коли. Це рішення впливає на рівень пріоритету, призначений кожному потоку. У macOS та Unix-подібних системах це обробляється за допомогою концепцій, таких як nice, renice та класи якості обслуговування (QoS).

Nice і Renice

  1. Nice:
  • Значення nice процесу — це число, яке впливає на його пріоритет. Кожен процес має значення nice в діапазоні від -20 (найвищий пріоритет) до 19 (найнижчий пріоритет). Значення nice за замовчуванням, коли процес створюється, зазвичай становить 0.
  • Нижче значення nice (ближче до -20) робить процес більш "егоїстичним", надаючи йому більше часу ЦП у порівнянні з іншими процесами з вищими значеннями nice.
  1. Renice:
  • renice — це команда, яка використовується для зміни значення nice вже запущеного процесу. Це можна використовувати для динамічного коригування пріоритету процесів, або підвищуючи, або знижуючи їх виділення часу ЦП на основі нових значень nice.
  • Наприклад, якщо процесу тимчасово потрібно більше ресурсів ЦП, ви можете знизити його значення nice за допомогою renice.

Класи якості обслуговування (QoS)

Класи QoS є більш сучасним підходом до управління пріоритетами потоків, особливо в системах, таких як macOS, які підтримують Grand Central Dispatch (GCD). Класи QoS дозволяють розробникам категоризувати роботу на різні рівні залежно від їх важливості або терміновості. macOS автоматично управляє пріоритизацією потоків на основі цих класів QoS:

  1. Користувацький інтерактивний:
  • Цей клас призначений для завдань, які в даний момент взаємодіють з користувачем або вимагають негайних результатів для забезпечення хорошого користувацького досвіду. Ці завдання отримують найвищий пріоритет, щоб інтерфейс залишався чутливим (наприклад, анімації або обробка подій).
  1. Ініційований користувачем:
  • Завдання, які ініціює користувач і очікує негайних результатів, такі як відкриття документа або натискання кнопки, що вимагає обчислень. Це високий пріоритет, але нижче, ніж користувацький інтерактивний.
  1. Утиліта:
  • Ці завдання є тривалими і зазвичай показують індикатор прогресу (наприклад, завантаження файлів, імпорт даних). Вони мають нижчий пріоритет, ніж ініційовані користувачем завдання, і не потребують негайного завершення.
  1. Фонові:
  • Цей клас призначений для завдань, які працюють у фоновому режимі і не видимі для користувача. Це можуть бути завдання, такі як індексація, синхронізація або резервне копіювання. Вони мають найнижчий пріоритет і мінімальний вплив на продуктивність системи.

Використовуючи класи QoS, розробники не повинні управляти точними номерами пріоритету, а скоріше зосереджуватися на природі завдання, а система оптимізує ресурси ЦП відповідно.

Більше того, існують різні політики планування потоків, які визначають набір параметрів планування, які планувальник враховуватиме. Це можна зробити за допомогою thread_policy_[set/get]. Це може бути корисно в атаках на умови гонки.

Зловживання процесами в MacOS

MacOS, як і будь-яка інша операційна система, надає різноманітні методи та механізми для взаємодії, комунікації та обміну даними між процесами. Хоча ці техніки є важливими для ефективного функціонування системи, їх також можуть зловживати зловмисники для виконання шкідливих дій.

Ін'єкція бібліотек

Ін'єкція бібліотек — це техніка, при якій зловмисник примушує процес завантажити шкідливу бібліотеку. Після ін'єкції бібліотека виконується в контексті цільового процесу, надаючи зловмиснику ті ж дозволи та доступ, що й процес.

macOS Library Injection

Хук функцій

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

macOS Function Hooking

Міжпроцесна комунікація

Міжпроцесна комунікація (IPC) відноситься до різних методів, за допомогою яких окремі процеси діляться та обмінюються даними. Хоча IPC є основоположним для багатьох легітимних застосувань, його також можна зловживати для підриву ізоляції процесів, витоку чутливої інформації або виконання несанкціонованих дій.

macOS IPC - Inter Process Communication

Ін'єкція додатків Electron

Додатки Electron, виконувані з певними змінними середовища, можуть бути вразливими до ін'єкції процесів:

macOS Electron Applications Injection

Ін'єкція Chromium

Можна використовувати параметри --load-extension та --use-fake-ui-for-media-stream, щоб виконати атаку "людина в браузері", що дозволяє красти натискання клавіш, трафік, куки, ін'єктувати скрипти на сторінках...:

macOS Chromium Injection

Брудний NIB

Файли NIB визначають елементи інтерфейсу користувача (UI) та їх взаємодії в межах програми. Однак вони можуть виконувати довільні команди, і Gatekeeper не зупиняє вже виконувану програму від виконання, якщо файл NIB модифіковано. Тому їх можна використовувати для виконання довільних команд:

macOS Dirty NIB

Ін'єкція Java-додатків

Можна зловживати певними можливостями Java (такими як змінна середовища _JAVA_OPTS), щоб змусити Java-додаток виконувати довільний код/команди.

macOS Java Applications Injection

Ін'єкція .Net-додатків

Можна ін'єктувати код у .Net-додатки, зловживаючи функціональністю налагодження .Net (яка не захищена такими захистами macOS, як посилене виконання).

macOS .Net Applications Injection

Ін'єкція Perl

Перевірте різні варіанти, щоб змусити скрипт Perl виконувати довільний код у:

macOS Perl Applications Injection

Ін'єкція Ruby

Також можливо зловживати змінними середовища Ruby, щоб змусити довільні скрипти виконувати довільний код:

macOS Ruby Applications Injection

Ін'єкція Python

Якщо змінна середовища PYTHONINSPECT встановлена, процес Python перейде в CLI Python після завершення. Також можливо використовувати PYTHONSTARTUP, щоб вказати скрипт Python для виконання на початку інтерактивної сесії.
Однак зверніть увагу, що скрипт PYTHONSTARTUP не буде виконано, коли PYTHONINSPECT створює інтерактивну сесію.

Інші змінні середовища, такі як PYTHONPATH та PYTHONHOME, також можуть бути корисними для виконання довільного коду Python.

Зверніть увагу, що виконувані файли, скомпільовані за допомогою pyinstaller, не використовуватимуть ці змінні середовища, навіть якщо вони виконуються за допомогою вбудованого Python.

caution

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

mv /opt/homebrew/bin/python3 /opt/homebrew/bin/python3.old
cat > /opt/homebrew/bin/python3 <<EOF
#!/bin/bash
# Додатковий код захоплення
/opt/homebrew/bin/python3.old "$@"
EOF
chmod +x /opt/homebrew/bin/python3

Навіть root виконає цей код, коли запустить Python.

Виявлення

Shield

Shield (Github) — це відкритий додаток, який може виявляти та блокувати дії ін'єкції процесів:

  • Використовуючи змінні середовища: Він буде контролювати наявність будь-якої з наступних змінних середовища: DYLD_INSERT_LIBRARIES, CFNETWORK_LIBRARY_PATH, RAWCAMERA_BUNDLE_PATH та ELECTRON_RUN_AS_NODE.
  • Використовуючи виклики task_for_pid: Щоб дізнатися, коли один процес хоче отримати порт завдання іншого, що дозволяє ін'єктувати код у процес.
  • Параметри додатків Electron: Хтось може використовувати аргументи командного рядка --inspect, --inspect-brk та --remote-debugging-port, щоб запустити додаток Electron у режимі налагодження, і таким чином ін'єктувати код у нього.
  • Використовуючи символьні посилання або жорсткі посилання: Зазвичай найпоширеніше зловживання полягає в розміщенні посилання з нашими привілеями та вказуванні на місце з вищими привілеями. Виявлення дуже просте для обох жорстких і символьних посилань. Якщо процес, що створює посилання, має інший рівень привілеїв, ніж цільовий файл, ми створюємо попередження. На жаль, у випадку символьних посилань блокування неможливе, оскільки у нас немає інформації про призначення посилання до його створення. Це обмеження фреймворку EndpointSecurity Apple.

Виклики, зроблені іншими процесами

У цьому блозі ви можете дізнатися, як можна використовувати функцію task_name_for_pid, щоб отримати інформацію про інші процеси, що ін'єктують код у процес і потім отримати інформацію про цей інший процес.

Зверніть увагу, що для виклику цієї функції вам потрібно бути тим же uid, що й той, хто виконує процес, або root (і вона повертає інформацію про процес, а не спосіб ін'єкції коду).

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks