Translated ['src/macos-hardening/macos-security-and-privilege-escalation

This commit is contained in:
Translator 2025-07-23 10:11:23 +00:00
parent 11f6d55daa
commit 4fadd43d53

View File

@ -7,49 +7,49 @@
- [https://github.com/bazad/threadexec](https://github.com/bazad/threadexec)
- [https://gist.github.com/knightsc/bd6dfeccb02b77eb6409db5601dcef36](https://gist.github.com/knightsc/bd6dfeccb02b77eb6409db5601dcef36)
## 1. Захоплення потоку
## 1. Thread Hijacking
Спочатку функція **`task_threads()`** викликається на порту завдання для отримання списку потоків з віддаленого завдання. Один з потоків обирається для захоплення. Цей підхід відрізняється від звичайних методів ін'єкції коду, оскільки створення нового віддаленого потоку заборонено через нові заходи, що блокують `thread_create_running()`.
Спочатку викликається функція `task_threads()` на порту задачі для отримання списку потоків з віддаленої задачі. Вибирається потік для захоплення. Цей підхід відрізняється від звичайних методів ін'єкції коду, оскільки створення нового віддаленого потоку заборонено через міру, яка блокує `thread_create_running()`.
Для контролю потоку викликається **`thread_suspend()`**, що зупиняє його виконання.
Для контролю потоку викликається `thread_suspend()`, що зупиняє його виконання.
Єдині операції, дозволені на віддаленому потоці, включають **зупинку** та **початок** його, **отримання** та **модифікацію** значень його регістрів. Віддалені виклики функцій ініціюються шляхом налаштування регістрів `x0` до `x7` на **аргументи**, налаштування **`pc`** на цільову функцію та активації потоку. Забезпечення того, щоб потік не зламався після повернення, вимагає виявлення повернення.
Єдині операції, дозволені на віддаленому потоці, включають **зупинку** та **початок** його роботи, а також **отримання**/**модифікацію** значень його регістрів. Віддалені виклики функцій ініціюються шляхом встановлення регістрів `x0` до `x7` на **аргументи**, налаштування `pc` на цільову функцію та відновлення потоку. Забезпечення того, щоб потік не зламався після повернення, вимагає виявлення повернення.
Одна зі стратегій полягає в **реєстрації обробника виключень** для віддаленого потоку за допомогою `thread_set_exception_ports()`, налаштовуючи регістр `lr` на недійсну адресу перед викликом функції. Це викликає виключення після виконання функції, надсилаючи повідомлення на порт виключень, що дозволяє перевірити стан потоку для відновлення значення повернення. Альтернативно, як це було прийнято з експлойту Іана Біра triple_fetch, `lr` налаштовується на безкінечний цикл. Регістри потоку потім постійно моніторяться, поки **`pc` не вказує на цю інструкцію**.
Одна зі стратегій полягає в реєстрації **обробника виключень** для віддаленого потоку за допомогою `thread_set_exception_ports()`, встановлюючи регістр `lr` на недійсну адресу перед викликом функції. Це викликає виключення після виконання функції, надсилаючи повідомлення на порт виключень, що дозволяє перевірити стан потоку для відновлення значення повернення. Альтернативно, як це було прийнято з експлойту *triple_fetch* Іана Біра, `lr` встановлюється на безкінечний цикл; регістри потоку потім постійно моніторяться, поки `pc` не вказує на цю інструкцію.
## 2. Mach порти для зв'язку
## 2. Mach ports for communication
Наступний етап полягає в створенні Mach портів для полегшення зв'язку з віддаленим потоком. Ці порти є важливими для передачі довільних прав на відправлення та отримання між завданнями.
Наступний етап полягає в створенні Mach портів для полегшення зв'язку з віддаленим потоком. Ці порти є важливими для передачі довільних прав на відправлення/отримання між задачами.
Для двостороннього зв'язку створюються два права на отримання Mach: одне в локальному, а інше в віддаленому завданні. Потім право на відправлення для кожного порту передається до відповідного завдання, що дозволяє обмінюватися повідомленнями.
Для двостороннього зв'язку створюються два права на отримання Mach: одне в локальній, а інше в віддаленій задачі. Потім право на відправлення для кожного порту передається до відповідної задачі, що дозволяє обмінюватися повідомленнями.
Зосереджуючись на локальному порту, право на отримання утримується локальним завданням. Порт створюється за допомогою `mach_port_allocate()`. Виклик полягає в передачі права на відправлення до цього порту в віддалене завдання.
Зосереджуючись на локальному порту, право на отримання утримується локальною задачею. Порт створюється за допомогою `mach_port_allocate()`. Виклик виклику полягає в передачі права на відправлення до цього порту в віддалену задачу.
Одна зі стратегій полягає в використанні `thread_set_special_port()` для розміщення права на відправлення до локального порту в `THREAD_KERNEL_PORT` віддаленого потоку. Потім віддаленому потоку надається команда викликати `mach_thread_self()`, щоб отримати право на відправлення.
Стратегія полягає в використанні `thread_set_special_port()`, щоб помістити право на відправлення до локального порту в `THREAD_KERNEL_PORT` віддаленого потоку. Потім віддаленому потоку вказується викликати `mach_thread_self()`, щоб отримати право на відправлення.
Для віддаленого порту процес в основному обернений. Віддаленому потоку надається команда створити Mach порт за допомогою `mach_reply_port()` (оскільки `mach_port_allocate()` не підходить через свій механізм повернення). Після створення порту викликається `mach_port_insert_right()` в віддаленому потоці для встановлення права на відправлення. Це право потім зберігається в ядрі за допомогою `thread_set_special_port()`. Повертаючись до локального завдання, використовується `thread_get_special_port()` на віддаленому потоці для отримання права на відправлення до новоствореного Mach порту в віддаленому завданні.
Для віддаленого порту процес в основному обернений. Віддаленому потоку вказується створити Mach порт за допомогою `mach_reply_port()` (оскільки `mach_port_allocate()` не підходить через свій механізм повернення). Після створення порту викликається `mach_port_insert_right()` в віддаленому потоці для встановлення права на відправлення. Це право потім зберігається в ядрі за допомогою `thread_set_special_port()`. Повертаючись до локальної задачі, використовується `thread_get_special_port()` на віддаленому потоці, щоб отримати право на відправлення до новоствореного Mach порту в віддаленій задачі.
Завершення цих кроків призводить до створення Mach портів, закладаючи основу для двостороннього зв'язку.
## 3. Основні примітиви читання/запису пам'яті
## 3. Basic Memory Read/Write Primitives
У цьому розділі увага зосереджена на використанні примітиву виконання для встановлення основних примітивів читання та запису пам'яті. Ці початкові кроки є вирішальними для отримання більшого контролю над віддаленим процесом, хоча примітиви на цьому етапі не виконуватимуть багато функцій. Незабаром вони будуть оновлені до більш просунутих версій.
У цьому розділі увага зосереджена на використанні примітиву виконання для встановлення базових примітивів читання/запису пам'яті. Ці початкові кроки є важливими для отримання більшого контролю над віддаленим процесом, хоча примітиви на цьому етапі не будуть мати багато застосувань. Незабаром вони будуть оновлені до більш просунутих версій.
### Читання та запис пам'яті за допомогою примітиву виконання
### Memory reading and writing using the execute primitive
Мета полягає в тому, щоб виконати читання та запис пам'яті за допомогою специфічних функцій. Для читання пам'яті використовуються функції, що нагадують наступну структуру:
Мета полягає в тому, щоб виконати читання та запис пам'яті за допомогою специфічних функцій. Для **читання пам'яті**:
```c
uint64_t read_func(uint64_t *address) {
return *address;
}
```
І для запису в пам'ять використовуються функції, подібні до цієї структури:
Для **запису пам'яті**:
```c
void write_func(uint64_t *address, uint64_t value) {
*address = value;
}
```
Ці функції відповідають наведеним асемблерним інструкціям:
Ці функції відповідають наступному асемблеру:
```
_read_func:
ldr x0, [x0]
@ -62,100 +62,112 @@ ret
Сканування загальних бібліотек виявило відповідні кандидати для цих операцій:
1. **Читання пам'яті:**
Функція `property_getName()` з [Objective-C runtime library](https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-runtime-new.mm.auto.html) визначена як підходяща функція для читання пам'яті. Функція описана нижче:
1. **Читання пам'яті — `property_getName()`** (libobjc):
```c
const char *property_getName(objc_property_t prop) {
return prop->name;
}
```
Ця функція фактично діє як `read_func`, повертаючи перше поле `objc_property_t`.
2. **Запис пам'яті:**
Знайти готову функцію для запису пам'яті складніше. Однак функція `_xpc_int64_set_value()` з libxpc є відповідним кандидатом з наступним дизасемблюванням:
2. **Запис пам'яті — `_xpc_int64_set_value()`** (libxpc):
```c
__xpc_int64_set_value:
str x1, [x0, #0x18]
ret
```
Щоб виконати запис 64-бітного значення за певною адресою, віддалений виклик структурований як:
Щоб виконати запис 64-бітного значення за довільною адресою:
```c
_xpc_int64_set_value(address - 0x18, value)
_xpc_int64_set_value(address - 0x18, value);
```
З встановленими цими примітивами, сцена готова для створення спільної пам'яті, що є значним прогресом у контролі віддаленого процесу.
З цими примітивами встановленими, сцена готова для створення спільної пам'яті, що є значним прогресом у контролі над віддаленим процесом.
## 4. Налаштування спільної пам'яті
Мета полягає в тому, щоб встановити спільну пам'ять між локальними та віддаленими завданнями, спрощуючи передачу даних і полегшуючи виклик функцій з кількома аргументами. Підхід передбачає використання `libxpc` та його об'єктного типу `OS_xpc_shmem`, який побудований на основі записів пам'яті Mach.
Мета полягає в тому, щоб встановити спільну пам'ять між локальними та віддаленими завданнями, спрощуючи передачу даних і полегшуючи виклик функцій з кількома аргументами. Підхід використовує `libxpc` та його об'єкт типу `OS_xpc_shmem`, який побудований на основі записів пам'яті Mach.
### Огляд процесу:
### Огляд процесу
1. **Виділення пам'яті**:
1. **Виділення пам'яті**
* Виділити пам'ять для спільного використання за допомогою `mach_vm_allocate()`.
* Використати `xpc_shmem_create()` для створення об'єкта `OS_xpc_shmem` для виділеної області.
2. **Створення спільної пам'яті в віддаленому процесі**
* Виділити пам'ять для об'єкта `OS_xpc_shmem` у віддаленому процесі (`remote_malloc`).
* Скопіювати локальний шаблон об'єкта; виправлення вбудованого права на відправлення Mach за зміщенням `0x18` все ще необхідне.
3. **Виправлення запису пам'яті Mach**
* Вставити право на відправлення за допомогою `thread_set_special_port()` і перезаписати поле `0x18` ім'ям віддаленого запису.
4. **Завершення**
* Перевірити віддалений об'єкт і відобразити його за допомогою віддаленого виклику `xpc_shmem_remote()`.
- Виділіть пам'ять для спільного використання за допомогою `mach_vm_allocate()`.
- Використовуйте `xpc_shmem_create()` для створення об'єкта `OS_xpc_shmem` для виділеної області пам'яті. Ця функція керуватиме створенням запису пам'яті Mach і зберігатиме право на відправку Mach за зсувом `0x18` об'єкта `OS_xpc_shmem`.
## 5. Досягнення повного контролю
2. **Створення спільної пам'яті в віддаленому процесі**:
Якщо доступне довільне виконання та канал зворотного зв'язку зі спільною пам'яттю, ви ефективно володієте цільовим процесом:
- Виділіть пам'ять для об'єкта `OS_xpc_shmem` в віддаленому процесі за допомогою віддаленого виклику `malloc()`.
- Скопіюйте вміст локального об'єкта `OS_xpc_shmem` до віддаленого процесу. Однак ця початкова копія матиме неправильні імена записів пам'яті Mach за зсувом `0x18`.
* **Довільний R/W пам'яті** — використовуйте `memcpy()` між локальними та спільними регіонами.
* **Виклики функцій з > 8 аргументами** — розмістіть додаткові аргументи на стеку відповідно до виклику arm64.
* **Передача порту Mach** — передавайте права в повідомленнях Mach через встановлені порти.
* **Передача дескриптора файлу** — використовуйте fileports (див. *triple_fetch*).
3. **Виправлення запису пам'яті Mach**:
Усе це обгорнуто в бібліотеці [`threadexec`](https://github.com/bazad/threadexec) для зручного повторного використання.
- Використовуйте метод `thread_set_special_port()` для вставки права на відправку для запису пам'яті Mach у віддалене завдання.
- Виправте поле запису пам'яті Mach за зсувом `0x18`, перезаписавши його іменем запису віддаленої пам'яті.
---
4. **Завершення налаштування спільної пам'яті**:
- Перевірте віддалений об'єкт `OS_xpc_shmem`.
- Встановіть відображення спільної пам'яті за допомогою віддаленого виклику `xpc_shmem_remote()`.
## 6. Особливості Apple Silicon (arm64e)
Дотримуючись цих кроків, спільна пам'ять між локальними та віддаленими завданнями буде ефективно налаштована, що дозволить здійснювати прості передачі даних і виконувати функції, які потребують кількох аргументів.
На пристроях Apple Silicon (arm64e) **Коди автентифікації вказівників (PAC)** захищають всі адреси повернення та багато вказівників функцій. Техніки захоплення потоків, які *повторно використовують існуючий код*, продовжують працювати, оскільки оригінальні значення в `lr`/`pc` вже мають дійсні підписи PAC. Проблеми виникають, коли ви намагаєтеся стрибнути до пам'яті, контрольованої зловмисником:
## Додаткові фрагменти коду
Для виділення пам'яті та створення об'єкта спільної пам'яті:
1. Виділити виконувану пам'ять всередині цілі (віддалений `mach_vm_allocate` + `mprotect(PROT_EXEC)`).
2. Скопіювати ваш вантаж.
3. Всередині *віддаленого* процесу підписати вказівник:
```c
mach_vm_allocate();
xpc_shmem_create();
uint64_t ptr = (uint64_t)payload;
ptr = ptrauth_sign_unauthenticated((void*)ptr, ptrauth_key_asia, 0);
```
Для створення та виправлення об'єкта спільної пам'яті в віддаленому процесі:
```c
malloc(); // for allocating memory remotely
thread_set_special_port(); // for inserting send right
4. Встановіть `pc = ptr` у стані захопленого потоку.
Альтернативно, дотримуйтесь стандарту PAC, з'єднуючи існуючі гаджети/функції (традиційний ROP).
## 7. Виявлення та зміцнення з EndpointSecurity
Фреймворк **EndpointSecurity (ES)** відкриває події ядра, які дозволяють захисникам спостерігати або блокувати спроби ін'єкції потоків:
* `ES_EVENT_TYPE_AUTH_GET_TASK` спрацьовує, коли процес запитує порт іншого завдання (наприклад, `task_for_pid()`).
* `ES_EVENT_TYPE_NOTIFY_REMOTE_THREAD_CREATE` виводиться щоразу, коли створюється потік у *іншому* завданні.
* `ES_EVENT_TYPE_NOTIFY_THREAD_SET_STATE` (додано в macOS 14 Sonoma) вказує на маніпуляцію реєстрами існуючого потоку.
Мінімальний клієнт Swift, який виводить події віддалених потоків:
```swift
import EndpointSecurity
let client = try! ESClient(subscriptions: [.notifyRemoteThreadCreate]) {
(_, msg) in
if let evt = msg.remoteThreadCreate {
print("[ALERT] remote thread in pid \(evt.target.pid) by pid \(evt.thread.pid)")
}
}
RunLoop.main.run()
```
Пам'ятайте, щоб правильно обробляти деталі Mach портів та імен записів пам'яті, щоб забезпечити правильну роботу налаштування спільної пам'яті.
Запит з **osquery** ≥ 5.8:
```sql
SELECT target_pid, source_pid, target_path
FROM es_process_events
WHERE event_type = 'REMOTE_THREAD_CREATE';
```
### Розгляди щодо захищеного виконання
## 5. Досягнення Повного Контролю
Розповсюдження вашого додатку **без** права `com.apple.security.get-task-allow` запобігає не-root зловмисникам від отримання його task-port. Захист цілісності системи (SIP) все ще блокує доступ до багатьох бінарних файлів Apple, але стороннє програмне забезпечення повинно явно відмовитися від цього.
Після успішного встановлення спільної пам'яті та отримання можливостей довільного виконання, ми фактично отримали повний контроль над цільовим процесом. Ключові функціональні можливості, що забезпечують цей контроль, це:
## 8. Останні публічні інструменти (2023-2025)
1. **Довільні Операції з Пам'яттю**:
| Інструмент | Рік | Зауваження |
|------------|-----|------------|
| [`task_vaccine`](https://github.com/rodionovd/task_vaccine) | 2023 | Компактний PoC, що демонструє захоплення потоків з урахуванням PAC на Ventura/Sonoma |
| `remote_thread_es` | 2024 | Допоміжний засіб EndpointSecurity, що використовується кількома постачальниками EDR для відображення подій `REMOTE_THREAD_CREATE` |
- Виконувати довільні читання пам'яті, викликаючи `memcpy()`, щоб копіювати дані з спільної області.
- Виконувати довільні записи пам'яті, використовуючи `memcpy()`, щоб передавати дані до спільної області.
2. **Обробка Викликів Функцій з Багатьма Аргументами**:
- Для функцій, які вимагають більше 8 аргументів, розмістіть додаткові аргументи на стеку відповідно до конвенції виклику.
3. **Передача Mach Портів**:
- Передача Mach портів між задачами через Mach повідомлення через раніше встановлені порти.
4. **Передача Дескрипторів Файлів**:
- Передача дескрипторів файлів між процесами за допомогою fileports, техніки, підкресленої Іаном Бірем у `triple_fetch`.
Цей всебічний контроль закріплений у бібліотеці [threadexec](https://github.com/bazad/threadexec), що надає детальну реалізацію та зручний API для взаємодії з жертвою процесом.
## Важливі Міркування:
- Забезпечте правильне використання `memcpy()` для операцій читання/запису пам'яті, щоб підтримувати стабільність системи та цілісність даних.
- При передачі Mach портів або дескрипторів файлів дотримуйтесь належних протоколів і відповідально обробляйте ресурси, щоб запобігти витокам або ненавмисному доступу.
Дотримуючись цих рекомендацій та використовуючи бібліотеку `threadexec`, можна ефективно керувати та взаємодіяти з процесами на детальному рівні, досягаючи повного контролю над цільовим процесом.
> Читання вихідного коду цих проектів корисне для розуміння змін API, введених в macOS 13/14, і для підтримки сумісності між Intel ↔ Apple Silicon.
## Посилання
- [https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/](https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/)
- [https://github.com/rodionovd/task_vaccine](https://github.com/rodionovd/task_vaccine)
- [https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create](https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create)
{{#include ../../../../banners/hacktricks-training.md}}