ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux 内核态与用户态隔离机制全解析:默认权限边界、SMEP/SMAP 与 KPTI 的攻防实战指南(ctf-wiki)

Linux 内核态与用户态隔离机制全解析:默认权限边界、SMEP/SMAP 与 KPTI 的攻防实战指南(ctf-wiki) 文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载用户态与内核态之间的隔离是 Linux 内核 pwn 攻防博弈的第一道门槛攻击者抢占了内核执行流之后究竟能不能跳回用户态执行自己准备的 Shellcode、能不能在内核态读取用户态内存完全由 SMEP、SMAP、KPTI 这几项硬件与软件机制决定。本指南以 ctf-wiki 仓库中 内核态与用户态隔离 一节为核心骨架系统梳理默认隔离模型、SMEP/SMAP/KPTI 的原理、开启关闭与状态查看方法并结合仓库中的绕过章节给出 ret2usr、改 CR4、修改页表等可落地的利用思路帮助读者建立完整的防御—绕过闭环知识体系。隔离的分类为什么内核要把自己与用户态分开在 ctf-wiki 的 防御机制总览 中内核防御机制被划分为隔离、访问控制、异常检测、随机化四大类而 隔离机制导读 进一步指出根据隔离主体的不同隔离又分为两类内核态与用户态的隔离即本文讨论的主题隔离的是两个不同特权级运行环境之间的数据与代码访问边界内核自身内部不同对象间的隔离例如堆对象之间的隔离对应 inner-kernel 一节的堆块隔离内容。本文聚焦第一类。在 基础知识 中已经交代过背景Intel CPU 将权限分为 Ring0Ring3 四级现代操作系统只使用 Ring0内核态与 Ring3用户态。内核态拥有完全的硬件访问能力而用户态只有部分能力。隔离机制要解决的核心问题是这条特权级边界在代码和数据两个维度上分别由什么来守护以及被攻破之后如何防御。目标文档 readme.md 用四句话精炼地给出了完整的隔离防线清单防线防护内容默认机制用户态不可直接访问内核态的数据、执行内核态的代码SMEP内核态不可执行用户态的代码SMAP内核态不可访问用户态的数据KPTI用户态不可看到内核态的页表内核态不可执行用户态的代码模拟下面逐项展开。默认隔离用户态不可直接触碰内核态默认机制是内核与用户态隔离的第一层它由 CPU 特权级本身保证用户态运行在 Ring3不具备执行特权指令如修改 CR4、加载页表基址等的能力也不具备访问内核虚拟地址空间的权限。从 基础知识 的虚拟内存布局可知Linux 将高地址虚拟内存空间分配给内核、低地址空间分配给用户进程用户进程的页表中本就不映射内核空间因此用户态不可直接访问内核态的数据、执行内核态的代码是硬件页表与特权级共同作用下的默认状态。需要强调的是这个默认边界保护的是从用户态向内核态的越权访问。而攻击者真正关心的往往是另一个方向在内核态拿到控制流之后能否反过来利用用户态资源。后者的防护正是 SMEP、SMAP、KPTI 存在的意义。SMEP内核态不可执行用户态代码威胁模型文档 用户代码不可执行 指出起初CPU 在内核态执行代码时可以直接跳转到用户态地址执行。一旦攻击者控制了内核中的执行流例如劫持某个函数指针就可以把指令指针指到用户态地址空间——而用户态内存中的 Shellcode 是攻击者完全可控的这使攻击变得极其容易实施。这种把内核控制流重定向到用户态代码的攻击手法就是经典的ret2usrreturn-to-user。为了防范这类攻击研究者提出当 CPU 位于内核态时不允许执行用户态的代码。在 Linux 内核中这个防御措施的实现是与指令集架构相关的x86 下是 SMEPARM 下是 PXN。原理与寄存器位x86 下对应的保护机制名为SMEPSupervisor Mode Execution Protection由CR4 控制寄存器中的第 20 位标记是否开启1 为开启0 为关闭。从 cr4.png 的位域结构可以看到CR4 中除了第 20 位 SMEP 与第 21 位 SMAP 之外还包含 PCIDE第 16 位、PAE第 5 位、PSE第 4 位等众多控制位。这意味着攻击者修改 CR4 时往往不能简单地只清一位而是需要重新写入一个保留其他关键位状态的新值——这也解释了为何绕过时普遍采用 0x6f0 这样的整体赋值见下文攻击 SMEP。开启与关闭开启默认情况下 SMEP 保护是开启的。如果使用 qemu 启动内核可以在-append选项中添加smep来显式开启这也是 CTF 启动脚本中常见的写法——例如 基础知识 中 CISCN2017 babydriver 的boot.sh就使用了-cpu kvm64,smep来开启 SMEP。关闭在/etc/default/grub的如下两行中添加nosmepGRUB_CMDLINE_LINUX_DEFAULTquiet GRUB_CMDLINE_LINUXinitrd/install/initrd.gz然后运行update-grub并重启系统即可关闭 SMEP。如果使用 qemu 启动内核则在-append选项中添加nosmep来关闭。状态查看通过如下命令检查 SMEP 是否开启如果输出了smep字符串则说明开启否则未开启grep smep /proc/cpuinfo攻击 SMEP文档 用户代码不可执行 给出两种典型思路修改 CR4 寄存器把 CR4 寄存器的第 20 位置为 0 后就可以重新执行用户态代码。一般而言使用0x6f0来设置 CR4这样 SMAP 和 SMEP 都会被同时关闭。内核中修改 CR4 的代码最终会调用到native_write_cr4当我们能够劫持控制流后可以执行内核中的 gadget 来修改 CR4。从另一个维度看内核中本身存在固定的修改 CR4 的代码例如refresh_pce函数、set_tsc_mode等函数内部都有相关操作可以作为目标跳转点。ret2dir返回内核直接映射区这一思路在 基础知识 与仓库的 ret2dir 章节中均有交代——利用内核线性映射区对物理地址空间的完整映射找到用户空间对应页框的内核空间地址用这个内核地址完成对用户数据的访问/执行从而绕开不能执行用户态地址的限制。更完整的 SMEP 绕过实战可以继续阅读仓库的 bypass-smep 与 ret2usr 章节。SMAP内核态不可访问用户态数据威胁模型如果只防执行而不防访问仍然存在严重问题文档 用户数据不可访问 指出在劫持控制流后攻击者可以通过栈迁移stack pivot将栈迁移到用户态地址空间然后在用户态内存中精心布置 ROP 链进而实现提权。因为用户态内存完全可控这相当于给了攻击者一个无限大的可控栈。为防范此类攻击需要禁止内核态访问用户态的数据。原理与寄存器位x86 下对应的保护机制名为SMAPSupervisor Mode Access Protection由CR4 寄存器中的第 21 位标记是否开启。注意 user-data-access.md 原文此处存在笔误写作第 21 位用来标记是否开启 SMEP结合 cr4.png 的位域标注可以确认第 20 位是 SMEP、第 21 位是 SMAP。与 SMEP 类似该防御在 ARM 下的对应物是PANPrivileged Access Never。开启与关闭开启默认情况下 SMAP 保护是开启的。使用 qemu 启动内核时在-append选项中添加smap开启。关闭在/etc/default/grub的两行配置中添加nosmap然后运行update-grub并重启系统即可关闭。使用 qemu 启动内核时在-append选项中添加nosmap关闭。GRUB_CMDLINE_LINUX_DEFAULTquiet GRUB_CMDLINE_LINUXinitrd/install/initrd.gz状态查看grep smap /proc/cpuinfo同样地如果输出包含smap字符串则说明保护已开启。攻击 SMAP文档 用户数据不可访问 给出两种方式设置 CR4 寄存器把 CR4 的第 21 位置为 0 后即可访问用户态数据。同样推荐使用0x6f0设置 CR4一次性关闭 SMAP 与 SMEP。方式与攻 SMEP 相同劫持控制流后执行修改 CR4 的 gadget或跳转到内核中固定的写 CR4 代码段如refresh_pce、set_tsc_mode。调用copy_from_user/copy_to_user在劫持控制流后攻击者可以调用copy_from_user和copy_to_user来访问用户态内存。这两个函数是内核提供的、经过安全检查的用户空间数据拷贝接口其内部会临时清空禁止访问用户态内存的标志AC 标志因此在内核态利用这两个函数读写用户态数据是合法路径。这与 基础知识 中内核中memcpy对应copy_from_user()/copy_to_user()的映射关系一致——它们是内核态访问用户数据的主要 API。KPTI内核页表隔离介绍与动机KPTIKernel Page Table Isolation内核页表隔离最初的主要目的是缓解 KASLR 的绕过以及 CPU 侧信道攻击尤其是 Meltdown 漏洞利用 CPU 乱序执行与预测执行的硬件缺陷从用户态侧信道读取内核数据。在 KPTI 机制中内核态空间与用户态空间的内存隔离进一步得到增强其页表布局发生根本性变化内核态的页表包括用户空间内存的页表和内核空间内存的页表用户态的页表只包括用户空间内存的页表以及必要的内核空间内存的页表如用于处理系统调用、中断等信息所需的内存。Kernel page table isolation 原理图展示内核态与用户态使用不同页表集Linux 4.15 中引入了 KPTI 机制并被反向移植到 Linux 4.14.11、4.9.75、4.4.110。与 SMEP/SMAP 的关系软件模拟在 x86_64 的 PTI 机制中内核态的用户空间内存映射部分被全部标记为不可执行NX。也就是说之前不具有 SMEP 特性的硬件如果开启了 KPTI 保护也就具备了类似 SMEP 的特性。此外SMAP 模拟理论上也可以以类似方式引入只是目前尚未实现。因此在开启了 KPTI 保护的内核中如果没有开启 SMAP 保护内核仍然可以访问用户态空间的内存但不能跳转到用户态空间执行 Shellcode相当于模拟了 SMEP。这也是目标文档 readme.md 中KPTI用户态不可看到内核态的页表内核态不可执行用户态的代码模拟的含义——前半句描述页表隔离后半句描述对 SMEP 的软件模拟效果。正如 基础知识 所总结的对于开启了 KPTI 的内核而言内核页表的用户地址空间无执行权限这使得 ret2usr 彻底成为过去式。开启与关闭使用 qemu 启动内核时在-append选项中添加kpti1开启 KPTI使用 qemu 启动内核时在-append选项中添加nopti关闭 KPTI。状态查看文档 kpti.md 给出两种查看方式/home/pwn # dmesg | grep page table [ 0.000000] Kernel/User page tables isolation: enabled /home/pwn # cat /proc/cpuinfo | grep pti fpu_exception : yes flags : ... pti smep smap第一种通过内核启动日志中的Kernel/User page tables isolation: enabled确认第二种通过/proc/cpuinfo的 flags 中是否出现pti确认该方式实际反映的是硬件/启动参数的配置情况两种方法可结合使用。攻击 KPTIKPTI 机制与 SMAP、SMEP 不太一样由于与源码紧密结合似乎没有办法在运行时刻直接关闭。仓库的 kpti.md 给出了三种绕过思路修改页表开启 KPTI 后用户态空间的所有数据都被标记了 NX 权限但我们可以考虑修改对应页表项的权限使其重新获得可执行权限。当内核没有开启 SMEP 时修改页表权限后就可以返回到用户态执行用户态代码。SWITCH_TO_USER_CR3_STACK复用内核的返回用户态代码开启 KPTI 后用户态进入内核态时会进行页表切换从内核态恢复用户态时也会进行页表切换。如果能够控制内核执行返回用户态时所执行的页表切换代码也就可以正常返回用户态。通过分析内核态到用户态切换的代码可知页表切换主要依靠SWITCH_TO_USER_CR3_STACK汇编宏.macro SWITCH_TO_USER_CR3_STACK scratch_reg:req pushq %rax SWITCH_TO_USER_CR3_NOSTACK scratch_reg\scratch_reg scratch_reg2%rax popq %rax .endm .macro SWITCH_TO_USER_CR3_NOSTACK scratch_reg:req scratch_reg2:req ALTERNATIVE jmp .Lend_\, , X86_FEATURE_PTI mov %cr3, \scratch_reg ALTERNATIVE jmp .Lwrcr3_\, , X86_FEATURE_PCID /* * Test if the ASID needs a flush. */ movq \scratch_reg, \scratch_reg2 andq $(0x7FF), \scratch_reg /* mask ASID */ bt \scratch_reg, THIS_CPU_user_pcid_flush_mask jnc .Lnoflush_\ /* Flush needed, clear the bit */ btr \scratch_reg, THIS_CPU_user_pcid_flush_mask movq \scratch_reg2, \scratch_reg jmp .Lwrcr3_pcid_\ .Lnoflush_\: movq \scratch_reg2, \scratch_reg SET_NOFLUSH_BIT \scratch_reg .Lwrcr3_pcid_\: /* Flip the ASID to the user version */ orq $(PTI_USER_PCID_MASK), \scratch_reg .Lwrcr3_\: /* Flip the PGD to the user version */ orq $(PTI_USER_PGTABLE_MASK), \scratch_reg mov \scratch_reg, %cr3 .Lend_\: .endm关键点在于除了切换页表我们还需要返回到用户态因此需要复用内核中返回用户态的代码。内核返回到用户态主要有两种方式iret和sysret。iret 路径通过伪造如下栈结构跳转到swapgs_restore_regs_and_return_to_usermode中的movq %rsp, %rdi处就可以同时完成页表切换和返回用户态fake rax fake rdi RIP CS EFLAGS RSP SS对应的内核汇编片段如下省略了 POP_REGS 之后的 trampoline 栈拷贝细节movq %rsp, %rdi movq PER_CPU_VAR(cpu_tss_rw TSS_sp0), %rsp ... SWITCH_TO_USER_CR3_STACK scratch_reg%rdi popq %rdi SWAPGS INTERRUPT_RETURNsysret 路径使用 sysret 时首先需要保证rcx保存返回用户态后要执行的代码地址RIPr11保存 eflagsrcx, save the rip of the code to be executed when returning to userspace r11, save eflags然后构造如下栈fake rdi rsp, the stack of the userspace最后跳转至entry_SYSCALL_64中如下代码即可返回到用户态SWITCH_TO_USER_CR3_STACK scratch_reg%rdi popq %rdi popq %rsp swapgs sysretqsignal handler也可以在用户态注册 signal handler 来执行位于用户态的代码。这种方式的优势在于无需切换页表——当用户态进程收到信号并从内核返回时内核会经由正常的信号处理流程在内核态完成必要的页表切换后跳转到用户态的 signal handler。关于 KPTI 绕过更完整的利用链可进一步阅读仓库的 kpti-bypass 与 ret2ptregs 章节后者借助 pt_regs 中保存的寄存器在返回用户态时恢复现场。从防御到绕过四道防线的完整对抗图谱综合 基础知识 与本文前述内容可以画出一张攻防对应表防御机制防护目标绕过思路仓库对应章节默认特权隔离用户态不可访问/执行内核内容通过内核漏洞获取执行流后再反向利用基础利用SMEP内核态不可执行用户态代码修改 CR4 第 20 位ret2dirbypass-smep、ret2dirSMAP内核态不可访问用户态数据修改 CR4 第 21 位调用 copy_from/to_userbypass-smepKPTI用户态不可见内核页表模拟 SMEP修改页表权限复用 SWITCH_TO_USER_CR3_STACKsignal handlerkpti-bypass、ret2ptregs需要注意一个叠加效应KPTI 开启后即使关闭 SMEPret2usr 依然不可行因为用户态页表中根本没有内核代码的映射、且内核页表中的用户地址空间部分无执行权限。因此在现代内核上ret2usr 已彻底成为过去式主流打法转向在内核态完成全链路利用如通过 ROP 调用commit_creds(prepare_kernel_cred(init_task))提权再借助 KPTI 绕过技巧优雅返回用户态。CTF 场景中的实践要点结合 基础知识 中 CISCN2017 babydriver 的boot.sh示例CTF kernel pwn 中防御机制完全由启动参数决定分析题目时应重点观察qemu-system-x86_64 -initrd rootfs.cpio -kernel bzImage -append consolettyS0 root/dev/ram oopspanic panic1 -enable-kvm -monitor /dev/null -m 64M --nographic -smp cores1,threads1 -cpu kvm64,smep-cpu kvm64,smep在 CPU 模型中显式加入 smep同样可用smap-append内核命令行参数可包含nosmep、nosmap、nopti、kpti1等决定 SMAP/SMEP/KPTI 的开关进入题目环境后用grep smep /proc/cpuinfo、grep smap /proc/cpuinfo确认硬件保护位用dmesg | grep page table确认 KPTI 是否启用。判断清楚题目开启了哪些隔离机制再决定采用改 CR4、ret2dir、还是 KPTI 绕过路线是 kernel pwn 解题的第一步也是本文所梳理的四道防线知识的直接落点。小结用户态与内核态的隔离由四条防线共同构成默认特权级与页表隔离用户态不可越权访问内核、SMEP内核态禁执行用户态代码、SMAP内核态禁访问用户态数据、KPTI页表级隔离并软件模拟 SMEP。它们分别由 CPU 硬件特性CR4 第 20/21 位与内核软件机制页表隔离与入口/出口汇编宏实现并各自存在成熟的绕过技术。理解这四道防线的原理、开启关闭方式与对抗思路是进行 Linux 内核 pwn 分析的基本功更深入的利用链细节可以继续阅读仓库 exploitation/rop 目录下的系列章节。赞分享文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载相关推荐CTF WikiLinux 内核防御机制之隔离KPTI / SMEP / SMAP / 堆块隔离全解析CTF WikiLinux 内核防御机制之隔离KPTI / SMEP / SMAP / 堆块隔离全解析 在内核 Pwn 攻防中隔离是四类核心防御机制文档网络安全教程CTF Linux 内核 PwnSMEP/SMAP 与用户代码不可执行防护的攻防全解析CTF Linux 内核 PwnSMEP/SMAP 与用户代码不可执行防护的攻防全解析 本篇文章聚焦 CTF Linux 内核 Pwn 中最基础也最关键的防御文档网络安全教程如何在ComfyUI中快速部署DynamiCrafter模型完整安装指南与环境配置如何在ComfyUI中快速部署DynamiCrafter模型完整安装指南与环境配置 ComfyUI DynamiCrafterWrapper是一个专为Comf文档网络安全教程上一篇QQ截图独立版不装QQ也能用的离线截图与OCR工具下一篇douyin-downloader 完整指南抖音视频批量下载与去水印3 步归档一整个博主主页创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表