mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/macos-hardening/macos-security-and-privilege-escalation
This commit is contained in:
parent
11f6d55daa
commit
4fadd43d53
@ -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}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user