macOS Thread Injection via Task port

Reading time: 7 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks

Code

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 визначена як підходяща функція для читання пам'яті. Функція описана нижче:
c
const char *property_getName(objc_property_t prop) {
return prop->name;
}

Ця функція фактично діє як read_func, повертаючи перше поле objc_property_t.

  1. Запис пам'яті: Знайти готову функцію для запису пам'яті складніше. Однак функція _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.
  1. Створення спільної пам'яті в віддаленому процесі:
  • Виділіть пам'ять для об'єкта OS_xpc_shmem в віддаленому процесі за допомогою віддаленого виклику malloc().
  • Скопіюйте вміст локального об'єкта OS_xpc_shmem до віддаленого процесу. Однак ця початкова копія матиме неправильні імена записів пам'яті Mach за зсувом 0x18.
  1. Виправлення запису пам'яті Mach:
  • Використовуйте метод thread_set_special_port() для вставки права на відправку для запису пам'яті Mach у віддалене завдання.
  • Виправте поле запису пам'яті Mach за зсувом 0x18, перезаписавши його іменем запису віддаленої пам'яті.
  1. Завершення налаштування спільної пам'яті:
  • Перевірте віддалений об'єкт 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(), щоб передавати дані до спільної області.
  1. Обробка Викликів Функцій з Багатьма Аргументами:
  • Для функцій, які вимагають більше 8 аргументів, розмістіть додаткові аргументи на стеку відповідно до конвенції виклику.
  1. Передача Mach Портів:
  • Передача Mach портів між задачами через Mach повідомлення через раніше встановлені порти.
  1. Передача Дескрипторів Файлів:
  • Передача дескрипторів файлів між процесами за допомогою fileports, техніки, підкресленої Іаном Бірем у triple_fetch.

Цей всебічний контроль закріплений у бібліотеці threadexec, що надає детальну реалізацію та зручний API для взаємодії з жертвою процесом.

Важливі Міркування:

  • Забезпечте правильне використання memcpy() для операцій читання/запису пам'яті, щоб підтримувати стабільність системи та цілісність даних.
  • При передачі Mach портів або дескрипторів файлів дотримуйтесь належних протоколів і відповідально обробляйте ресурси, щоб запобігти витокам або ненавмисному доступу.

Дотримуючись цих рекомендацій та використовуючи бібліотеку threadexec, можна ефективно керувати та взаємодіяти з процесами на детальному рівні, досягаючи повного контролю над цільовим процесом.

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks