macOS Thread Injection via Task port
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Спочатку викликається функція task_threads()
на порту задачі для отримання списку потоків з віддаленої задачі. Один з потоків обирається для захоплення. Цей підхід відрізняється від звичайних методів ін'єкції коду, оскільки створення нового віддаленого потоку заборонено через нові заходи, що блокують thread_create_running()
.
Для контролю потоку викликається thread_suspend()
, що зупиняє його виконання.
Єдині операції, дозволені на віддаленому потоці, включають зупинку та запуск, отримання та модифікацію значень його регістрів. Віддалені виклики функцій ініціюються шляхом встановлення регістрів x0
до x7
на аргументи, налаштування pc
для націлювання на бажану функцію та активації потоку. Забезпечення того, щоб потік не зламався після повернення, вимагає виявлення повернення.
Одна зі стратегій полягає в реєстрації обробника виключень для віддаленого потоку за допомогою thread_set_exception_ports()
, встановлюючи регістр lr
на недійсну адресу перед викликом функції. Це викликає виключення після виконання функції, надсилаючи повідомлення на порт виключень, що дозволяє перевірити стан потоку для відновлення значення повернення. Альтернативно, як це було запозичено з експлойту triple_fetch Іана Біра, lr
встановлюється на безкінечний цикл. Регістри потоку потім постійно моніторяться, поки pc
не вказує на цю інструкцію.
Наступний етап полягає в створенні 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 портів, закладаючи основу для двостороннього зв'язку.
У цьому розділі увага зосереджена на використанні примітиву виконання для встановлення базових примітивів читання та запису пам'яті. Ці початкові кроки є вирішальними для отримання більшого контролю над віддаленим процесом, хоча примітиви на цьому етапі не будуть служити багатьом цілям. Незабаром вони будуть оновлені до більш просунутих версій.
Мета полягає в тому, щоб виконати читання та запис пам'яті за допомогою специфічних функцій. Для читання пам'яті використовуються функції, що нагадують наступну структуру:
І для запису в пам'ять використовуються функції, подібні до цієї структури:
Ці функції відповідають наведеним асемблерним інструкціям:
Сканування загальних бібліотек виявило відповідні кандидати для цих операцій:
Читання Пам'яті: Функція property_getName()
з бібліотеки виконання Objective-C визначена як підходяща функція для читання пам'яті. Функція описана нижче:
Ця функція фактично діє як read_func
, повертаючи перше поле objc_property_t
.
Запис пам'яті: Знайти готову функцію для запису пам'яті складніше. Однак функція _xpc_int64_set_value()
з libxpc є підходящим кандидатом з наступним дизасемблюванням:
Щоб виконати 64-бітний запис за певною адресою, віддалений виклик структурований як:
З цими примітивами встановленими, сцена готова для створення спільної пам'яті, що є значним прогресом у контролі над віддаленим процесом.
Мета полягає в тому, щоб встановити спільну пам'ять між локальними та віддаленими завданнями, спрощуючи передачу даних і полегшуючи виклик функцій з кількома аргументами. Підхід передбачає використання libxpc
та його об'єктного типу OS_xpc_shmem
, який побудований на основі записів пам'яті Mach.
Виділення пам'яті:
Виділіть пам'ять для спільного використання за допомогою mach_vm_allocate()
.
Використовуйте xpc_shmem_create()
для створення об'єкта OS_xpc_shmem
для виділеної області пам'яті. Ця функція керуватиме створенням запису пам'яті Mach і зберігатиме право на відправку Mach за зсувом 0x18
об'єкта OS_xpc_shmem
.
Створення спільної пам'яті в віддаленому процесі:
Виділіть пам'ять для об'єкта OS_xpc_shmem
в віддаленому процесі за допомогою віддаленого виклику malloc()
.
Скопіюйте вміст локального об'єкта OS_xpc_shmem
до віддаленого процесу. Однак, ця початкова копія матиме неправильні імена записів пам'яті Mach за зсувом 0x18
.
Виправлення запису пам'яті Mach:
Використовуйте метод thread_set_special_port()
для вставки права на відправку для запису пам'яті Mach у віддалене завдання.
Виправте поле запису пам'яті Mach за зсувом 0x18
, перезаписавши його іменем запису віддаленої пам'яті.
Завершення налаштування спільної пам'яті:
Перевірте віддалений об'єкт OS_xpc_shmem
.
Встановіть відображення спільної пам'яті за допомогою віддаленого виклику xpc_shmem_remote()
.
Дотримуючись цих кроків, спільна пам'ять між локальними та віддаленими завданнями буде ефективно налаштована, що дозволить здійснювати прості передачі даних і виконувати функції, які потребують кількох аргументів.
Для виділення пам'яті та створення об'єкта спільної пам'яті:
Для створення та виправлення об'єкта спільної пам'яті в віддаленому процесі:
Пам'ятайте, щоб правильно обробляти деталі Mach портів та імена записів пам'яті, щоб забезпечити правильну роботу налаштування спільної пам'яті.
Після успішного встановлення спільної пам'яті та отримання можливостей довільного виконання, ми фактично отримали повний контроль над цільовим процесом. Ключові функціональні можливості, що забезпечують цей контроль, це:
Довільні Операції з Пам'яттю:
Виконувати довільні читання пам'яті, викликаючи memcpy()
, щоб копіювати дані з спільної області.
Виконувати довільні записи пам'яті, використовуючи memcpy()
, щоб передавати дані до спільної області.
Обробка Викликів Функцій з Багатьма Аргументами:
Для функцій, які вимагають більше 8 аргументів, розмістіть додаткові аргументи на стеку відповідно до конвенції виклику.
Передача Mach Портів:
Передача Mach портів між задачами через Mach повідомлення через раніше встановлені порти.
Передача Файлових Дескрипторів:
Передача файлових дескрипторів між процесами за допомогою fileports, техніки, підкресленої Іаном Біером у triple_fetch
.
Цей всебічний контроль закріплений у бібліотеці threadexec, що надає детальну реалізацію та зручний API для взаємодії з жертвою процесом.
Забезпечте правильне використання memcpy()
для операцій читання/запису пам'яті, щоб підтримувати стабільність системи та цілісність даних.
При передачі Mach портів або файлових дескрипторів дотримуйтесь належних протоколів і відповідально обробляйте ресурси, щоб запобігти витокам або ненавмисному доступу.
Дотримуючись цих рекомендацій та використовуючи бібліотеку threadexec
, можна ефективно керувати та взаємодіяти з процесами на детальному рівні, досягаючи повного контролю над цільовим процесом.
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)