Поділіться своїми хакерськими трюками, надсилайте PR доHackTricksіHackTricks Cloudрепозиторіїв на GitHub.
MIG був створений для спрощення процесу створення коду Mach IPC. Він в основному генерує необхідний код для сервера та клієнта для спілкування з заданою визначенням. Навіть якщо згенерований код виглядає некрасиво, розробнику просто потрібно буде імпортувати його, і його код буде набагато простішим, ніж раніше.
Приклад
Створіть файл визначення, у цьому випадку з дуже простою функцією:
myipc.defs
subsystem myipc 500; // Arbitrary name and iduserprefix USERPREF; // Prefix for created functions in the clientserverprefix SERVERPREF; // Prefix for created functions in the server#include<mach/mach_types.defs>#include<mach/std_types.defs>simpleroutineSubtract(server_port : mach_port_t;n1 : uint32_t;n2 : uint32_t);
Тепер використовуйте mig для генерації коду сервера та клієнта, які зможуть спілкуватися між собою, щоб викликати функцію Subtract:
Кілька нових файлів буде створено в поточному каталозі.
У файлах myipcServer.c та myipcServer.h ви можете знайти декларацію та визначення структури SERVERPREFmyipc_subsystem, яка в основному визначає функцію для виклику на основі отриманого ідентифікатора повідомлення (ми вказали початковий номер 500):
/* Description of this subsystem, for use in direct RPC */conststruct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = {myipc_server_routine,500, // start ID501, // end ID(mach_msg_size_t)sizeof(union __ReplyUnion__SERVERPREFmyipc_subsystem),(vm_address_t)0,{{ (mig_impl_routine_t) 0,// Function to call(mig_stub_routine_t) _XSubtract,3,0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__Subtract_t)},}};
macOS MIG (Mach Interface Generator)
MIG is a tool used to define inter-process communication (IPC) interfaces for Mach-based systems like macOS. It generates server-side code to handle messages sent between processes.
To create a MIG server, you need to define the message formats in a .defs file, then run MIG to generate the server-side code. This code will include functions to handle incoming messages and send responses back.
Here's a basic example of a MIG server:
#include<mach/mach.h>#include<servers/bootstrap.h>#include"myipcServer.h"kern_return_tmyipc_server(mach_msg_header_t*InHeadP,mach_msg_header_t*OutHeadP){// Handle incoming messages and send responsesreturn KERN_SUCCESS;}intmain(){mach_port_t server_port;kern_return_t result; result =bootstrap_check_in(bootstrap_port,"com.example.myipc",&server_port);if (result != KERN_SUCCESS) {return1; } result =mach_msg_server(myipc_server,2048, server_port);if (result != KERN_SUCCESS) {return1; }return0;}
In this example, myipc_server is the function that handles incoming messages. The server registers itself with a bootstrap port and then starts the MIG server loop using mach_msg_server.
By understanding and manipulating MIG servers, an attacker could potentially abuse IPC mechanisms to escalate privileges or perform other malicious actions on a macOS system.
/* Description of this subsystem, for use in direct RPC */externconststruct SERVERPREFmyipc_subsystem {mig_server_routine_t server; /* Server routine */mach_msg_id_t start; /* Min routine number */mach_msg_id_t end; /* Max routine number + 1 */unsignedint maxsize; /* Max msg size */vm_address_t reserved; /* Reserved */struct routine_descriptor /* Array of routine descriptors */routine[1];} SERVERPREFmyipc_subsystem;
На основі попередньої структури функція myipc_server_routine отримає ідентифікатор повідомлення та поверне відповідну функцію для виклику:
У цьому прикладі ми визначили лише 1 функцію в визначеннях, але якби ми визначили більше функцій, вони були б у масиві SERVERPREFmyipc_subsystem, і перша була б призначена для ID 500, друга для ID 501...
Фактично, можна ідентифікувати цей зв'язок у структурі subsystem_to_name_map_myipc з myipcServer.h:
Перевірте попередньо підсвічені рядки, отримуючи доступ до функції для виклику за ідентифікатором.
У наступному код для створення простого сервера та клієнта, де клієнт може викликати функції від сервера:
// gcc myipc_server.c myipcServer.c -o myipc_server#include<stdio.h>#include<mach/mach.h>#include<servers/bootstrap.h>#include"myipcServer.h"kern_return_tSERVERPREFSubtract(mach_port_t server_port,uint32_t n1,uint32_t n2){printf("Received: %d - %d = %d\n", n1, n2, n1 - n2);return KERN_SUCCESS;}intmain() {mach_port_t port;kern_return_t kr;// Register the mach servicekr =bootstrap_check_in(bootstrap_port,"xyz.hacktricks.mig",&port);if (kr != KERN_SUCCESS) {printf("bootstrap_check_in() failed with code 0x%x\n", kr);return1;}// myipc_server is the function that handles incoming messages (check previous exlpanation)mach_msg_server(myipc_server,sizeof(union __RequestUnion__SERVERPREFmyipc_subsystem), port, MACH_MSG_TIMEOUT_NONE);}
// gcc myipc_client.c myipcUser.c -o myipc_client#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<mach/mach.h>#include<servers/bootstrap.h>#include"myipcUser.h"intmain() {// Lookup the receiver port using the bootstrap server.mach_port_t port;kern_return_t kr =bootstrap_look_up(bootstrap_port,"xyz.hacktricks.mig",&port);if (kr != KERN_SUCCESS) {printf("bootstrap_look_up() failed with code 0x%x\n", kr);return1;}printf("Port right name %d\n", port);USERPREFSubtract(port,40,2);}
Аналіз бінарних файлів
Оскільки багато бінарних файлів зараз використовують MIG для виходу на зв'язок з портами mach, цікаво знати, як визначити, що був використаний MIG та функції, які виконує MIG з кожним ідентифікатором повідомлення.
jtool2 може розбирати інформацію MIG з бінарного файлу Mach-O, вказуючи ідентифікатор повідомлення та ідентифікуючи функцію для виконання:
jtool2-d__DATA.__constmyipc_server|grepMIG
Було зазначено, що функція, яка буде відповідати за виклик правильної функції в залежності від отриманого ідентифікатора повідомлення, - це myipc_server. Однак, зазвичай у вас не буде символів бінарного файлу (назв функцій), тому цікаво перевірити, як виглядає декомпільована версія, оскільки вона завжди буде дуже схожа (код цієї функції незалежний від викладених функцій):
int_myipc_server(int arg0,int arg1) {var_10 = arg0;var_18 = arg1;// Початкові інструкції для знаходження відповідних вказівників функцій*(int32_t*)var_18 =*(int32_t*)var_10 &0x1f;*(int32_t*)(var_18 +0x8) =*(int32_t*)(var_10 +0x8);*(int32_t*)(var_18 +0x4) =0x24;*(int32_t*)(var_18 +0xc) =0x0;*(int32_t*)(var_18 +0x14) =*(int32_t*)(var_10 +0x14) +0x64;*(int32_t*)(var_18 +0x10) =0x0;if (*(int32_t*)(var_10 +0x14) <=0x1f4&&*(int32_t*)(var_10 +0x14) >=0x1f4) {rax =*(int32_t*)(var_10 +0x14);// Виклик sign_extend_64, який може допомогти ідентифікувати цю функцію// Це зберігає в rax вказівник на виклик, який потрібно викликати// Перевірте використання адреси 0x100004040 (масив адрес функцій)// 0x1f4 = 500 (початковий ID) rax =*(sign_extend_64(rax -0x1f4)*0x28+0x100004040); var_20 = rax;// Якщо - інакше, якщо if повертає false, тоді else викликає правильну функцію та повертає trueif (rax ==0x0) {*(var_18 +0x18) =**_NDR_record;*(int32_t*)(var_18 +0x20) =0xfffffffffffffed1;var_4 =0x0;}else {// Розрахована адреса, яка викликає відповідну функцію з 2 аргументами (var_20)(var_10, var_18); var_4 =0x1;}}else {*(var_18 +0x18) =**_NDR_record;*(int32_t*)(var_18 +0x20) =0xfffffffffffffed1;var_4 =0x0;}rax = var_4;return rax;}
Це та сама функція, декомпільована в іншій безкоштовній версії Hopper:
Фактично, якщо ви перейдете до функції 0x100004000, ви знайдете масив структур routine_descriptor. Перший елемент структури - це адреса, де реалізована функція, і структура займає 0x28 байтів, тому кожні 0x28 байтів (починаючи з байту 0) ви можете отримати 8 байтів, і це буде адреса функції, яка буде викликана: