macOS Thread Injection via Task port

Support HackTricks

Code

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 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

Визначення Підходящих Функцій

Сканування загальних бібліотек виявило відповідні кандидати для цих операцій:

  1. Читання Пам'яті: Функція property_getName() з бібліотеки виконання Objective-C визначена як підходяща функція для читання пам'яті. Функція описана нижче:

const char *property_getName(objc_property_t prop) {
return prop->name;
}

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

  1. Запис пам'яті: Знайти готову функцію для запису пам'яті складніше. Однак функція _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.

Огляд процесу:

  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().

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

Додаткові фрагменти коду

Для виділення пам'яті та створення об'єкта спільної пам'яті:

mach_vm_allocate();
xpc_shmem_create();

Для створення та виправлення об'єкта спільної пам'яті в віддаленому процесі:

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, можна ефективно керувати та взаємодіяти з процесами на детальному рівні, досягаючи повного контролю над цільовим процесом.

Посилання

Support HackTricks

Last updated