macOS XPC
Reading time: 12 minutes
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Основна інформація
XPC, що означає XNU (ядро, яке використовується в macOS) міжпроцесна комунікація, є фреймворком для комунікації між процесами на macOS та iOS. XPC надає механізм для виконання безпечних, асинхронних викликів методів між різними процесами в системі. Це частина парадигми безпеки Apple, що дозволяє створювати програми з розділеними привілеями, де кожен компонент працює з тільки тими правами, які йому потрібні для виконання своєї роботи, тим самим обмежуючи потенційні збитки від скомпрометованого процесу.
XPC використовує форму міжпроцесної комунікації (IPC), яка є набором методів для різних програм, що працюють на одній системі, для обміну даними.
Основні переваги XPC включають:
- Безпека: Розділяючи роботу на різні процеси, кожному процесу можуть бути надані лише ті права, які йому потрібні. Це означає, що навіть якщо процес буде скомпрометований, його можливості завдати шкоди будуть обмежені.
- Стабільність: XPC допомагає ізолювати збої до компонента, в якому вони відбуваються. Якщо процес зазнає збою, його можна перезапустити без впливу на решту системи.
- Продуктивність: XPC дозволяє легко виконувати кілька завдань одночасно в різних процесах.
Єдиний недолік полягає в тому, що розділення програми на кілька процесів, які спілкуються через XPC, є менш ефективним. Але в сучасних системах це майже не помітно, а переваги переважають.
Специфічні для програми XPC сервіси
XPC компоненти програми знаходяться всередині самої програми. Наприклад, у Safari ви можете знайти їх у /Applications/Safari.app/Contents/XPCServices
. Вони мають розширення .xpc
(як com.apple.Safari.SandboxBroker.xpc
) і також є пакетами з основним бінарним файлом всередині: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker
та Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist
Як ви, можливо, думаєте, компонент XPC матиме різні права та привілеї в порівнянні з іншими компонентами XPC або основним бінарним файлом програми. ОКРІМ випадку, якщо XPC сервіс налаштований з JoinExistingSession встановленим на “True” у його Info.plist файлі. У цьому випадку XPC сервіс працюватиме в тій же сесії безпеки, що й програма, яка його викликала.
XPC сервіси запускаються за допомогою launchd за потреби і закриваються після завершення всіх завдань, щоб звільнити системні ресурси. Специфічні для програми XPC компоненти можуть використовуватися лише самою програмою, що зменшує ризик, пов'язаний з потенційними вразливостями.
Системні XPC сервіси
Системні XPC сервіси доступні всім користувачам. Ці сервіси, або launchd, або Mach-типу, повинні бути визначені в plist файлах, розташованих у вказаних каталогах, таких як /System/Library/LaunchDaemons
, /Library/LaunchDaemons
, /System/Library/LaunchAgents
, або /Library/LaunchAgents
.
Ці plist файли матимуть ключ під назвою MachServices
з назвою сервісу та ключ під назвою Program
з шляхом до бінарного файлу:
cat /Library/LaunchDaemons/com.jamf.management.daemon.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Program</key>
<string>/Library/Application Support/JAMF/Jamf.app/Contents/MacOS/JamfDaemon.app/Contents/MacOS/JamfDaemon</string>
<key>AbandonProcessGroup</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.jamf.management.daemon</string>
<key>MachServices</key>
<dict>
<key>com.jamf.management.daemon.aad</key>
<true/>
<key>com.jamf.management.daemon.agent</key>
<true/>
<key>com.jamf.management.daemon.binary</key>
<true/>
<key>com.jamf.management.daemon.selfservice</key>
<true/>
<key>com.jamf.management.daemon.service</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Ті, що в LaunchDameons
, виконуються від імені root. Тому, якщо процес без привілеїв може спілкуватися з одним з них, він може отримати підвищені привілеї.
XPC Об'єкти
xpc_object_t
Кожне XPC повідомлення є об'єктом словника, який спрощує серіалізацію та десеріалізацію. Більше того, libxpc.dylib
оголошує більшість типів даних, тому можливо перевірити, що отримані дані мають очікуваний тип. У C API кожен об'єкт є xpc_object_t
(і його тип можна перевірити за допомогою xpc_get_type(object)
).
Більше того, функцію xpc_copy_description(object)
можна використовувати для отримання рядкового представлення об'єкта, що може бути корисним для налагодження.
Ці об'єкти також мають деякі методи, які можна викликати, такі як xpc_<object>_copy
, xpc_<object>_equal
, xpc_<object>_hash
, xpc_<object>_serialize
, xpc_<object>_deserialize
...
xpc_object_t
створюються шляхом виклику функції xpc_<objetType>_create
, яка внутрішньо викликає _xpc_base_create(Class, Size)
, де вказується тип класу об'єкта (один з XPC_TYPE_*
) і його розмір (додаткові 40B будуть додані до розміру для метаданих). Це означає, що дані об'єкта почнуться з офсету 40B.
Отже, xpc_<objectType>_t
є своєрідним підкласом xpc_object_t
, який буде підкласом os_object_t*
.
warning
Зверніть увагу, що саме розробник повинен використовувати xpc_dictionary_[get/set]_<objectType>
, щоб отримати або встановити тип і реальне значення ключа.
xpc_pipe
xpc_pipe
- це FIFO труба, яку процеси можуть використовувати для спілкування (спілкування використовує повідомлення Mach).
Можливо створити XPC сервер, викликавши xpc_pipe_create()
або xpc_pipe_create_from_port()
, щоб створити його, використовуючи конкретний Mach порт. Потім, щоб отримувати повідомлення, можна викликати xpc_pipe_receive
і xpc_pipe_try_receive
.
Зверніть увагу, що об'єкт xpc_pipe
є xpc_object_t
з інформацією в його структурі про два Mach порти, що використовуються, і ім'я (якщо є). Ім'я, наприклад, демон secinitd
у його plist /System/Library/LaunchDaemons/com.apple.secinitd.plist
налаштовує трубу, названу com.apple.secinitd
.
Приклад xpc_pipe
- це bootstrap pipe, створена launchd
, що робить можливим спільне використання Mach портів.
NSXPC*
Це об'єкти високого рівня Objective-C, які дозволяють абстрагувати XPC з'єднання.
Більше того, їх легше налагоджувати за допомогою DTrace, ніж попередні.
GCD Queues
XPC використовує GCD для передачі повідомлень, більше того, він генерує певні черги диспетчеризації, такі як xpc.transactionq
, xpc.io
, xpc-events.add-listenerq
, xpc.service-instance
...
XPC Сервіси
Це пакети з розширенням .xpc
, розташовані всередині папки XPCServices
інших проектів, і в Info.plist
у них встановлено CFBundlePackageType
на XPC!
.
Цей файл має інші ключі конфігурації, такі як ServiceType
, які можуть бути Application, User, System або _SandboxProfile
, які можуть визначати пісочницю, або _AllowedClients
, які можуть вказувати права або ID, необхідні для контакту з сервісом. Ці та інші параметри конфігурації будуть корисні для налаштування сервісу під час запуску.
Запуск Сервісу
Додаток намагається підключитися до XPC сервісу, використовуючи xpc_connection_create_mach_service
, потім launchd знаходить демон і запускає xpcproxy
. xpcproxy
забезпечує виконання налаштованих обмежень і створює сервіс з наданими FDs і Mach портами.
Щоб покращити швидкість пошуку XPC сервісу, використовується кеш.
Можливо відстежувати дії xpcproxy
, використовуючи:
supraudit S -C -o /tmp/output /dev/auditpipe
Бібліотека XPC використовує kdebug
для логування дій, викликаючи xpc_ktrace_pid0
та xpc_ktrace_pid1
. Коди, які вона використовує, не задокументовані, тому їх потрібно додати до /usr/share/misc/trace.codes
. Вони мають префікс 0x29
, і, наприклад, один з них - 0x29000004
: XPC_serializer_pack
.
Утиліта xpcproxy
використовує префікс 0x22
, наприклад: 0x2200001c: xpcproxy:will_do_preexec
.
XPC Повідомлення подій
Додатки можуть підписуватися на різні події повідомлення, що дозволяє їм ініціюватися за запитом, коли такі події відбуваються. Налаштування для цих сервісів виконується в файлах plist launchd, розташованих у тих же каталогах, що й попередні, і містять додатковий ключ LaunchEvent
.
Перевірка процесу підключення XPC
Коли процес намагається викликати метод через XPC-з'єднання, сервіс XPC повинен перевірити, чи дозволено цьому процесу підключатися. Ось поширені способи перевірки цього та поширені помилки:
macOS XPC Connecting Process Check
Авторизація XPC
Apple також дозволяє додаткам налаштовувати деякі права та способи їх отримання, тому якщо викликаючий процес має їх, йому буде дозволено викликати метод з сервісу XPC:
XPC Сніфер
Щоб перехоплювати повідомлення XPC, ви можете використовувати xpcspy, який використовує Frida.
# Install
pip3 install xpcspy
pip3 install xpcspy --no-deps # To not make xpcspy install Frida 15 and downgrade your Frida installation
# Start sniffing
xpcspy -U -r -W <bundle-id>
## Using filters (i: for input, o: for output)
xpcspy -U <prog-name> -t 'i:com.apple.*' -t 'o:com.apple.*' -r
Ще одним можливим інструментом для використання є XPoCe2.
Приклад коду C для XPC зв'язку
// gcc xpc_server.c -o xpc_server
#include <xpc/xpc.h>
static void handle_event(xpc_object_t event) {
if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {
// Print received message
const char* received_message = xpc_dictionary_get_string(event, "message");
printf("Received message: %s\n", received_message);
// Create a response dictionary
xpc_object_t response = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(response, "received", "received");
// Send response
xpc_connection_t remote = xpc_dictionary_get_remote_connection(event);
xpc_connection_send_message(remote, response);
// Clean up
xpc_release(response);
}
}
static void handle_connection(xpc_connection_t connection) {
xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
handle_event(event);
});
xpc_connection_resume(connection);
}
int main(int argc, const char *argv[]) {
xpc_connection_t service = xpc_connection_create_mach_service("xyz.hacktricks.service",
dispatch_get_main_queue(),
XPC_CONNECTION_MACH_SERVICE_LISTENER);
if (!service) {
fprintf(stderr, "Failed to create service.\n");
exit(EXIT_FAILURE);
}
xpc_connection_set_event_handler(service, ^(xpc_object_t event) {
xpc_type_t type = xpc_get_type(event);
if (type == XPC_TYPE_CONNECTION) {
handle_connection(event);
}
});
xpc_connection_resume(service);
dispatch_main();
return 0;
}
# Compile the server & client
gcc xpc_server.c -o xpc_server
gcc xpc_client.c -o xpc_client
# Save server on it's location
cp xpc_server /tmp
# Load daemon
sudo cp xyz.hacktricks.service.plist /Library/LaunchDaemons
sudo launchctl load /Library/LaunchDaemons/xyz.hacktricks.service.plist
# Call client
./xpc_client
# Clean
sudo launchctl unload /Library/LaunchDaemons/xyz.hacktricks.service.plist
sudo rm /Library/LaunchDaemons/xyz.hacktricks.service.plist /tmp/xpc_server
XPC Комунікація Приклад коду Objective-C
// gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server
#include <Foundation/Foundation.h>
@protocol MyXPCProtocol
- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply;
@end
@interface MyXPCObject : NSObject <MyXPCProtocol>
@end
@implementation MyXPCObject
- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply {
NSLog(@"Received message: %@", some_string);
NSString *response = @"Received";
reply(response);
}
@end
@interface MyDelegate : NSObject <NSXPCListenerDelegate>
@end
@implementation MyDelegate
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)];
MyXPCObject *my_object = [MyXPCObject new];
newConnection.exportedObject = my_object;
[newConnection resume];
return YES;
}
@end
int main(void) {
NSXPCListener *listener = [[NSXPCListener alloc] initWithMachServiceName:@"xyz.hacktricks.svcoc"];
id <NSXPCListenerDelegate> delegate = [MyDelegate new];
listener.delegate = delegate;
[listener resume];
sleep(10); // Fake something is done and then it ends
}
# Compile the server & client
gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server
gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client
# Save server on it's location
cp oc_xpc_server /tmp
# Load daemon
sudo cp xyz.hacktricks.svcoc.plist /Library/LaunchDaemons
sudo launchctl load /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist
# Call client
./oc_xpc_client
# Clean
sudo launchctl unload /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist
sudo rm /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist /tmp/oc_xpc_server
Клієнт всередині коду Dylb
// gcc -dynamiclib -framework Foundation oc_xpc_client.m -o oc_xpc_client.dylib
// gcc injection example:
// DYLD_INSERT_LIBRARIES=oc_xpc_client.dylib /path/to/vuln/bin
#import <Foundation/Foundation.h>
@protocol MyXPCProtocol
- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply;
@end
__attribute__((constructor))
static void customConstructor(int argc, const char **argv)
{
NSString* _serviceName = @"xyz.hacktricks.svcoc";
NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096];
[_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)]];
[_agentConnection resume];
[[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
(void)error;
NSLog(@"Connection Failure");
}] sayHello:@"Hello, Server!" withReply:^(NSString *response) {
NSLog(@"Received response: %@", response);
} ];
NSLog(@"Done!");
return;
}
Remote XPC
Ця функціональність, надана RemoteXPC.framework
(з libxpc
), дозволяє спілкуватися через XPC між різними хостами.
Служби, які підтримують віддалений XPC, матимуть у своєму plist ключ UsesRemoteXPC, як це відбувається у випадку з /System/Library/LaunchDaemons/com.apple.SubmitDiagInfo.plist
. Однак, хоча служба буде зареєстрована з launchd
, саме UserEventAgent
з плагінами com.apple.remoted.plugin
та com.apple.remoteservicediscovery.events.plugin
надає цю функціональність.
Більше того, RemoteServiceDiscovery.framework
дозволяє отримувати інформацію з com.apple.remoted.plugin
, відкриваючи функції, такі як get_device
, get_unique_device
, connect
...
Як тільки використовується connect і сокет fd
служби зібрано, можна використовувати клас remote_xpc_connection_*
.
Можливо отримати інформацію про віддалені служби, використовуючи інструмент cli /usr/libexec/remotectl
з параметрами, такими як:
/usr/libexec/remotectl list # Get bridge devices
/usr/libexec/remotectl show ...# Get device properties and services
/usr/libexec/remotectl dumpstate # Like dump withuot indicateing a servie
/usr/libexec/remotectl [netcat|relay] ... # Expose a service in a port
...
Зв'язок між BridgeOS та хостом відбувається через спеціальний інтерфейс IPv6. MultiverseSupport.framework
дозволяє встановлювати сокети, fd
яких буде використовуватися для зв'язку.
Можна знайти ці комунікації, використовуючи netstat
, nettop
або відкриту альтернативу, netbottom
.
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.