macOS Thread Injection via Task port
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Code
- https://github.com/bazad/threadexec
- https://gist.github.com/knightsc/bd6dfeccb02b77eb6409db5601dcef36
1. Thread Hijacking
Спочатку викликається функція task_threads()
на порту задачі для отримання списку потоків з віддаленої задачі. Вибирається потік для захоплення. Цей підхід відрізняється від звичайних методів ін'єкції коду, оскільки створення нового віддаленого потоку заборонено через міру, яка блокує thread_create_running()
.
Для контролю потоку викликається thread_suspend()
, що зупиняє його виконання.
Єдині операції, дозволені на віддаленому потоці, включають зупинку та початок його роботи, а також отримання/модифікацію значень його регістрів. Віддалені виклики функцій ініціюються шляхом встановлення регістрів x0
до x7
на аргументи, налаштування pc
на цільову функцію та відновлення потоку. Забезпечення того, щоб потік не зламався після повернення, вимагає виявлення повернення.
Одна зі стратегій полягає в реєстрації обробника виключень для віддаленого потоку за допомогою thread_set_exception_ports()
, встановлюючи регістр lr
на недійсну адресу перед викликом функції. Це викликає виключення після виконання функції, надсилаючи повідомлення на порт виключень, що дозволяє перевірити стан потоку для відновлення значення повернення. Альтернативно, як це було прийнято з експлойту triple_fetch Іана Біра, lr
встановлюється на безкінечний цикл; регістри потоку потім постійно моніторяться, поки pc
не вказує на цю інструкцію.
2. Mach ports for communication
Наступний етап полягає в створенні 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. Basic Memory Read/Write Primitives
У цьому розділі увага зосереджена на використанні примітиву виконання для встановлення базових примітивів читання/запису пам'яті. Ці початкові кроки є важливими для отримання більшого контролю над віддаленим процесом, хоча примітиви на цьому етапі не будуть мати багато застосувань. Незабаром вони будуть оновлені до більш просунутих версій.
Memory reading and writing using the execute primitive
Мета полягає в тому, щоб виконати читання та запис пам'яті за допомогою специфічних функцій. Для читання пам'яті:
uint64_t read_func(uint64_t *address) {
return *address;
}
Для запису пам'яті:
void write_func(uint64_t *address, uint64_t value) {
*address = value;
}
Ці функції відповідають наступному асемблеру:
_read_func:
ldr x0, [x0]
ret
_write_func:
str x1, [x0]
ret
Визначення підходящих функцій
Сканування загальних бібліотек виявило відповідні кандидати для цих операцій:
- Читання пам'яті —
property_getName()
(libobjc):
const char *property_getName(objc_property_t prop) {
return prop->name;
}
- Запис пам'яті —
_xpc_int64_set_value()
(libxpc):
__xpc_int64_set_value:
str x1, [x0, #0x18]
ret
Щоб виконати запис 64-бітного значення за довільною адресою:
_xpc_int64_set_value(address - 0x18, value);
З цими примітивами встановленими, сцена готова для створення спільної пам'яті, що є значним прогресом у контролі над віддаленим процесом.
4. Налаштування спільної пам'яті
Мета полягає в тому, щоб встановити спільну пам'ять між локальними та віддаленими завданнями, спрощуючи передачу даних і полегшуючи виклик функцій з кількома аргументами. Підхід використовує libxpc
та його об'єкт типу OS_xpc_shmem
, який побудований на основі записів пам'яті Mach.
Огляд процесу
- Виділення пам'яті
- Виділити пам'ять для спільного використання за допомогою
mach_vm_allocate()
. - Використати
xpc_shmem_create()
для створення об'єктаOS_xpc_shmem
для виділеної області.
- Створення спільної пам'яті в віддаленому процесі
- Виділити пам'ять для об'єкта
OS_xpc_shmem
у віддаленому процесі (remote_malloc
). - Скопіювати локальний шаблон об'єкта; виправлення вбудованого права на відправлення Mach за зміщенням
0x18
все ще необхідне.
- Виправлення запису пам'яті Mach
- Вставити право на відправлення за допомогою
thread_set_special_port()
і перезаписати поле0x18
ім'ям віддаленого запису.
- Завершення
- Перевірити віддалений об'єкт і відобразити його за допомогою віддаленого виклику
xpc_shmem_remote()
.
5. Досягнення повного контролю
Якщо доступне довільне виконання та канал зворотного зв'язку зі спільною пам'яттю, ви ефективно володієте цільовим процесом:
- Довільний R/W пам'яті — використовуйте
memcpy()
між локальними та спільними регіонами. - Виклики функцій з > 8 аргументами — розмістіть додаткові аргументи на стеку відповідно до виклику arm64.
- Передача порту Mach — передавайте права в повідомленнях Mach через встановлені порти.
- Передача дескриптора файлу — використовуйте fileports (див. triple_fetch).
Усе це обгорнуто в бібліотеці threadexec
для зручного повторного використання.
6. Особливості Apple Silicon (arm64e)
На пристроях Apple Silicon (arm64e) Коди автентифікації вказівників (PAC) захищають всі адреси повернення та багато вказівників функцій. Техніки захоплення потоків, які повторно використовують існуючий код, продовжують працювати, оскільки оригінальні значення в lr
/pc
вже мають дійсні підписи PAC. Проблеми виникають, коли ви намагаєтеся стрибнути до пам'яті, контрольованої зловмисником:
- Виділити виконувану пам'ять всередині цілі (віддалений
mach_vm_allocate
+mprotect(PROT_EXEC)
). - Скопіювати ваш вантаж.
- Всередині віддаленого процесу підписати вказівник:
uint64_t ptr = (uint64_t)payload;
ptr = ptrauth_sign_unauthenticated((void*)ptr, ptrauth_key_asia, 0);
- Встановіть
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, який виводить події віддалених потоків:
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()
Запит з osquery ≥ 5.8:
SELECT target_pid, source_pid, target_path
FROM es_process_events
WHERE event_type = 'REMOTE_THREAD_CREATE';
Розгляди щодо захищеного виконання
Розповсюдження вашого додатку без права com.apple.security.get-task-allow
запобігає не-root зловмисникам від отримання його task-port. Захист цілісності системи (SIP) все ще блокує доступ до багатьох бінарних файлів Apple, але стороннє програмне забезпечення повинно явно відмовитися від цього.
8. Останні публічні інструменти (2023-2025)
Інструмент | Рік | Зауваження |
---|---|---|
task_vaccine | 2023 | Компактний PoC, що демонструє захоплення потоків з урахуванням PAC на Ventura/Sonoma |
remote_thread_es | 2024 | Допоміжний засіб EndpointSecurity, що використовується кількома постачальниками EDR для відображення подій REMOTE_THREAD_CREATE |
Читання вихідного коду цих проектів корисне для розуміння змін API, введених в macOS 13/14, і для підтримки сумісності між Intel ↔ Apple Silicon.
Посилання
- https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/
- https://github.com/rodionovd/task_vaccine
- https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.