mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
162 lines
15 KiB
Markdown
162 lines
15 KiB
Markdown
# macOS Thread Injection via Task port
|
||
|
||
{{#include ../../../../banners/hacktricks-training.md}}
|
||
|
||
## Code
|
||
|
||
- [https://github.com/bazad/threadexec](https://github.com/bazad/threadexec)
|
||
- [https://gist.github.com/knightsc/bd6dfeccb02b77eb6409db5601dcef36](https://gist.github.com/knightsc/bd6dfeccb02b77eb6409db5601dcef36)
|
||
|
||
## 1. Захоплення потоку
|
||
|
||
Спочатку функція **`task_threads()`** викликається на порту завдання для отримання списку потоків з віддаленого завдання. Один з потоків обирається для захоплення. Цей підхід відрізняється від звичайних методів ін'єкції коду, оскільки створення нового віддаленого потоку заборонено через нові заходи, що блокують `thread_create_running()`.
|
||
|
||
Для контролю потоку викликається **`thread_suspend()`**, що зупиняє його виконання.
|
||
|
||
Єдині операції, дозволені на віддаленому потоці, включають **зупинку** та **початок** його, **отримання** та **модифікацію** значень його регістрів. Віддалені виклики функцій ініціюються шляхом налаштування регістрів `x0` до `x7` на **аргументи**, налаштування **`pc`** на цільову функцію та активації потоку. Забезпечення того, щоб потік не зламався після повернення, вимагає виявлення повернення.
|
||
|
||
Одна зі стратегій полягає в **реєстрації обробника виключень** для віддаленого потоку за допомогою `thread_set_exception_ports()`, налаштовуючи регістр `lr` на недійсну адресу перед викликом функції. Це викликає виключення після виконання функції, надсилаючи повідомлення на порт виключень, що дозволяє перевірити стан потоку для відновлення значення повернення. Альтернативно, як це було прийнято з експлойту Іана Біра triple_fetch, `lr` налаштовується на безкінечний цикл. Регістри потоку потім постійно моніторяться, поки **`pc` не вказує на цю інструкцію**.
|
||
|
||
## 2. Mach порти для зв'язку
|
||
|
||
Наступний етап полягає в створенні Mach портів для полегшення зв'язку з віддаленим потоком. Ці порти є важливими для передачі довільних прав на відправлення та отримання між завданнями.
|
||
|
||
Для двостороннього зв'язку створюються два права на отримання Mach: одне в локальному, а інше в віддаленому завданні. Потім право на відправлення для кожного порту передається до відповідного завдання, що дозволяє обмінюватися повідомленнями.
|
||
|
||
Зосереджуючись на локальному порту, право на отримання утримується локальним завданням. Порт створюється за допомогою `mach_port_allocate()`. Виклик полягає в передачі права на відправлення до цього порту в віддалене завдання.
|
||
|
||
Одна зі стратегій полягає в використанні `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 портів, закладаючи основу для двостороннього зв'язку.
|
||
|
||
## 3. Основні примітиви читання/запису пам'яті
|
||
|
||
У цьому розділі увага зосереджена на використанні примітиву виконання для встановлення основних примітивів читання та запису пам'яті. Ці початкові кроки є вирішальними для отримання більшого контролю над віддаленим процесом, хоча примітиви на цьому етапі не виконуватимуть багато функцій. Незабаром вони будуть оновлені до більш просунутих версій.
|
||
|
||
### Читання та запис пам'яті за допомогою примітиву виконання
|
||
|
||
Мета полягає в тому, щоб виконати читання та запис пам'яті за допомогою специфічних функцій. Для читання пам'яті використовуються функції, що нагадують наступну структуру:
|
||
```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]
|
||
ret
|
||
_write_func:
|
||
str x1, [x0]
|
||
ret
|
||
```
|
||
### Визначення підходящих функцій
|
||
|
||
Сканування загальних бібліотек виявило відповідні кандидати для цих операцій:
|
||
|
||
1. **Читання пам'яті:**
|
||
Функція `property_getName()` з [Objective-C runtime library](https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-runtime-new.mm.auto.html) визначена як підходяща функція для читання пам'яті. Функція описана нижче:
|
||
```c
|
||
const char *property_getName(objc_property_t prop) {
|
||
return prop->name;
|
||
}
|
||
```
|
||
Ця функція фактично діє як `read_func`, повертаючи перше поле `objc_property_t`.
|
||
|
||
2. **Запис пам'яті:**
|
||
Знайти готову функцію для запису пам'яті складніше. Однак функція `_xpc_int64_set_value()` з libxpc є відповідним кандидатом з наступним дизасемблюванням:
|
||
```c
|
||
__xpc_int64_set_value:
|
||
str x1, [x0, #0x18]
|
||
ret
|
||
```
|
||
Щоб виконати запис 64-бітного значення за певною адресою, віддалений виклик структурований як:
|
||
```c
|
||
_xpc_int64_set_value(address - 0x18, value)
|
||
```
|
||
З встановленими цими примітивами, сцена готова для створення спільної пам'яті, що є значним прогресом у контролі віддаленого процесу.
|
||
|
||
## 4. Налаштування спільної пам'яті
|
||
|
||
Мета полягає в тому, щоб встановити спільну пам'ять між локальними та віддаленими завданнями, спрощуючи передачу даних і полегшуючи виклик функцій з кількома аргументами. Підхід передбачає використання `libxpc` та його об'єктного типу `OS_xpc_shmem`, який побудований на основі записів пам'яті Mach.
|
||
|
||
### Огляд процесу:
|
||
|
||
1. **Виділення пам'яті**:
|
||
|
||
- Виділіть пам'ять для спільного використання за допомогою `mach_vm_allocate()`.
|
||
- Використовуйте `xpc_shmem_create()` для створення об'єкта `OS_xpc_shmem` для виділеної області пам'яті. Ця функція керуватиме створенням запису пам'яті Mach і зберігатиме право на відправку Mach за зсувом `0x18` об'єкта `OS_xpc_shmem`.
|
||
|
||
2. **Створення спільної пам'яті в віддаленому процесі**:
|
||
|
||
- Виділіть пам'ять для об'єкта `OS_xpc_shmem` в віддаленому процесі за допомогою віддаленого виклику `malloc()`.
|
||
- Скопіюйте вміст локального об'єкта `OS_xpc_shmem` до віддаленого процесу. Однак ця початкова копія матиме неправильні імена записів пам'яті Mach за зсувом `0x18`.
|
||
|
||
3. **Виправлення запису пам'яті Mach**:
|
||
|
||
- Використовуйте метод `thread_set_special_port()` для вставки права на відправку для запису пам'яті Mach у віддалене завдання.
|
||
- Виправте поле запису пам'яті Mach за зсувом `0x18`, перезаписавши його іменем запису віддаленої пам'яті.
|
||
|
||
4. **Завершення налаштування спільної пам'яті**:
|
||
- Перевірте віддалений об'єкт `OS_xpc_shmem`.
|
||
- Встановіть відображення спільної пам'яті за допомогою віддаленого виклику `xpc_shmem_remote()`.
|
||
|
||
Дотримуючись цих кроків, спільна пам'ять між локальними та віддаленими завданнями буде ефективно налаштована, що дозволить здійснювати прості передачі даних і виконувати функції, які потребують кількох аргументів.
|
||
|
||
## Додаткові фрагменти коду
|
||
|
||
Для виділення пам'яті та створення об'єкта спільної пам'яті:
|
||
```c
|
||
mach_vm_allocate();
|
||
xpc_shmem_create();
|
||
```
|
||
Для створення та виправлення об'єкта спільної пам'яті в віддаленому процесі:
|
||
```c
|
||
malloc(); // for allocating memory remotely
|
||
thread_set_special_port(); // for inserting send right
|
||
```
|
||
Пам'ятайте, щоб правильно обробляти деталі Mach портів та імен записів пам'яті, щоб забезпечити правильну роботу налаштування спільної пам'яті.
|
||
|
||
## 5. Досягнення Повного Контролю
|
||
|
||
Після успішного встановлення спільної пам'яті та отримання можливостей довільного виконання, ми фактично отримали повний контроль над цільовим процесом. Ключові функціональні можливості, що забезпечують цей контроль, це:
|
||
|
||
1. **Довільні Операції з Пам'яттю**:
|
||
|
||
- Виконувати довільні читання пам'яті, викликаючи `memcpy()`, щоб копіювати дані з спільної області.
|
||
- Виконувати довільні записи пам'яті, використовуючи `memcpy()`, щоб передавати дані до спільної області.
|
||
|
||
2. **Обробка Викликів Функцій з Багатьма Аргументами**:
|
||
|
||
- Для функцій, які вимагають більше 8 аргументів, розмістіть додаткові аргументи на стеку відповідно до конвенції виклику.
|
||
|
||
3. **Передача Mach Портів**:
|
||
|
||
- Передача Mach портів між задачами через Mach повідомлення через раніше встановлені порти.
|
||
|
||
4. **Передача Дескрипторів Файлів**:
|
||
- Передача дескрипторів файлів між процесами за допомогою fileports, техніки, підкресленої Іаном Бірем у `triple_fetch`.
|
||
|
||
Цей всебічний контроль закріплений у бібліотеці [threadexec](https://github.com/bazad/threadexec), що надає детальну реалізацію та зручний API для взаємодії з жертвою процесом.
|
||
|
||
## Важливі Міркування:
|
||
|
||
- Забезпечте правильне використання `memcpy()` для операцій читання/запису пам'яті, щоб підтримувати стабільність системи та цілісність даних.
|
||
- При передачі Mach портів або дескрипторів файлів дотримуйтесь належних протоколів і відповідально обробляйте ресурси, щоб запобігти витокам або ненавмисному доступу.
|
||
|
||
Дотримуючись цих рекомендацій та використовуючи бібліотеку `threadexec`, можна ефективно керувати та взаємодіяти з процесами на детальному рівні, досягаючи повного контролю над цільовим процесом.
|
||
|
||
## Посилання
|
||
|
||
- [https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/](https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/)
|
||
|
||
{{#include ../../../../banners/hacktricks-training.md}}
|