☰
ctf-wiki 内核 Pwn 专题:KASLR 内核地址空间随机化原理与绕过实战指南
2026/9/29 2:51:53 网站建设 项目流程
  • 文档
  • 网络安全
  • 教程

【免费下载链接】ctf-wiki

Come and join us, we need you!

项目地址:https://gitcode.com/gh_mirrors/ct/ctf-wiki
点击查看免费下载

导读

KASLR(Kernel Address Space Layout Randomization,内核地址空间布局随机化)是现代 Linux 内核中最基础也最常见的防御机制之一:它通过在加载时给内核镜像整体叠加一个随机偏移,使攻击者无法事先预知内核代码段、数据段的实际加载地址。本文以 ctf-wiki 仓库的 kaslr.md 为主体,结合仓库中内核 Pwn 相关文档(如 basic-knowledge.md、qemu-emulate.md、fgkaslr.md)展开:读完本文后,你将掌握 KASLR 的偏移模型、在 QEMU 中开启/关闭 KASLR 的完整方法、通过信息泄漏计算内核加载偏移的绕过思路,以及 KASLR 与 FGKASLR、KPTI 等机制的相互关系。

KASLR 是什么:内核镜像的整体随机偏移

在开启了 KASLR 的内核中,内核的代码段基地址等地址会整体偏移。也就是说,内核镜像在映射到实际地址空间时会被加上一个随机的偏移值,但内核内部的相对偏移关系保持不变。

basic-knowledge.md 给出了更精确的表述:

KASLR 即内核空间地址随机化(kernel address space layout randomize),与用户态程序的 ASLR 相类似——在内核镜像映射到实际的地址空间时加上一个偏移值(粒度为 256MB),但是内核内部的相对偏移其实还是不变的。

两个常用的参考基址(未开启 KASLR 时):

  • 内核代码段基址:0xffffffff81000000
  • direct mapping area 基址:0xffff888000000000

因此,KASLR 的本质是单个随机偏移 + 固定内部布局:只要攻击者能通过信息泄漏拿到内核中任何一个段的地址,就可以反推出整个偏移,从而算出该段内所有符号的地址。这正是后面"Attack"一节的核心逻辑。

KASLR 在仓库中的定位:内核防御体系的一环

在 ctf-wiki 中,内核防御机制被划分为隔离、访问控制、异常检测、随机化四类,见 defense/readme.md:

  • 隔离(isolation):如 KPTI(Kernel Page Table Isolation),将内核态与用户态页表进一步隔离,最初目的之一就是缓解 KASLR 被绕过以及 CPU 侧信道攻击;
  • 访问控制(access control):如 信息泄漏防护 中的dmesg_restrict与kptr_restrict,用于限制内核日志和内核指针的输出;
  • 异常检测(detection):如内核栈 canary;
  • 随机化(randomization):即本文的 KASLR,以及其进阶版 FGKASLR。

在 randomization/readme.md 中有句总结性的话:"我们可以通过增加内核的随机性来提高安全性。"KASLR 正是这一思想的基础实现。

值得注意的是,KASLR 与 KPTI 之间存在紧密关联:kpti.md 明确指出 KPTI 机制最初的主要目的是缓解 KASLR 的绕过以及 CPU 侧信道攻击。理解这一点有助于把握内核防御的整体层次:KASLR 让地址不可预测,KPTI 则进一步在页表层面限制内核内存的可见性。

开启与关闭 KASLR

在 QEMU 环境中,KASLR 通过内核启动参数(即-append选项中的内容)控制,这与仓库中 qemu-emulate.md 描述的启动方式完全一致。

开启 KASLR

如果是使用 QEMU 启动的内核,可以在-append选项中添加kaslr来开启 KASLR。参考 qemu-emulate.md 的完整启动脚本:

qemu-system-x86_64 \ -m 128M \ -kernel ./bzImage \ -hda ./rootfs.qcow2 \ -monitor /dev/null \ -append "root=/dev/sda rw rdinit=/sbin/init console=ttyS0 oops=panic panic=1 loglevel=3 quiet kaslr" \ -cpu kvm64,+smep \ -smp cores=2,threads=1 \ -nographic \ -snapshot \ -s

其中-append中的kaslr即为开启内核地址随机化的内核启动参数。CTF 中很多题目的启动脚本(start.sh)就带有kaslr,例如 rop.md 中的示例:

qemu-system-x86_64 \ -m 64M \ -kernel ./bzImage \ -initrd ./core.cpio \ -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \ -s \ -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ -nographic

关闭 KASLR

如果是使用 QEMU 启动的内核,可以在-append选项中添加nokaslr来关闭 KASLR。这在调试阶段非常常用——关闭随机化后,内核代码段基址固定为0xffffffff81000000,符号地址可预期,GDB 下断点、定位 gadget 都更方便。

qemu-emulate.md 明确给出建议:"我们还可以在启动时,指定内核关闭随机化",对应的启动脚本片段为:

qemu-system-x86_64 \ -m 128M \ -kernel ./bzImage \ -hda ./rootfs.img \ -monitor /dev/null \ -append "root=/dev/sda rw rdinit=/sbin/init console=ttyS0 oops=panic panic=1 loglevel=3 quiet nokaslr" \ -cpu kvm64,+smep \ -smp cores=2,threads=1 \ -nographic \ -s

与其它启动参数配合

在 CTF 实战中,KASLR 通常与 KPTI(pti=on)等其它防护一起出现,例如 kpti-bypass.md 中的启动参数为"... quiet kaslr pti=on"。这意味着即便你成功绕过了 KASLR 拿到内核地址,还要面对 KPTI 的页表隔离;相关绕过手法(如利用swapgs_restore_regs_and_return_to_usermode切换页表并返回用户态)可参见 kpti.md 与 kpti-bypass.md。

Attack:泄漏一个地址,推导整个段

KASLR 的绕过思路非常直观,kaslr.md 原文如下:

通过泄漏内核某个段的地址,就可以得到这个段内的所有地址。比如当我们泄漏了内核的代码段地址,就知道内核代码段的所有地址。

其数学本质是:由于 KASLR 只是给整个镜像加了一个统一偏移,内核内部符号之间的相对偏移是固定不变的。因此,攻击流程一般是:

  1. 通过某种信息泄漏漏洞(如未初始化指针、越界读、dmesg 日志中的地址打印等)拿到内核中任意一个已知符号的运行地址;
  2. 用该运行地址减去其在 vmlinux 中的静态地址,得到 KASLR 偏移kaslr_offset;
  3. 将 vmlinux 中所有符号的静态地址都加上kaslr_offset,即可得到完整的内核地址布局。

信息泄漏的入口与防护

仓库中 information-disclosure.md 专门讲述了两种限制地址泄露的机制,这与 KASLR 的绕过直接相关:

  • dmesg_restrict:控制是否可以用dmesg查看内核日志。为 0 时无限制;为 1 时只有具有CAP_SYSLOG权限的用户才能查看。
  • kptr_restrict:控制通过/proc等接口输出的内核指针。值为 0 时默认无限制;值为 1 时%pK输出的指针在无CAP_SYSLOG权限时被替换为 0;值为 2 时无论权限如何,%pK指针一律替换为 0。

当开启这些保护后,攻击者就不能通过/proc/kallsyms直接获取commit_creds、prepare_kernel_cred等敏感符号的地址了。注意 qemu-emulate.md 中的环境脚本就同时设置了:

echo 1 > /proc/sys/kernel/dmesg_restrict echo 1 > /proc/sys/kernel/kptr_restrict

这提醒我们:真实 CTF 题目的内核环境中,/proc/kallsyms往往不可直接利用,信息泄漏点只能来自漏洞本身。

从 kallsyms 获取符号地址(未限制时)

当kptr_restrict未开启、且拥有足够权限时,可以直接从/proc/kallsyms读取符号地址。仓库 basic-knowledge.md 给出了完整示例:

$ sudo cat /proc/kallsyms | grep "T commit_creds" ffffffffbb11ab20 T commit_creds $ sudo cat /proc/kallsyms | grep "T prepare_kernel_cred" ffffffffbb11b080 T prepare_kernel_cred $ sudo cat /proc/kallsyms | grep "D init_cred" ffffffffbce58840 D init_cred

而 qemu-emulate.md 中也演示了类似操作:

# cat /proc/kallsyms | grep prepare_kernel_cred ffffffffa66d0b90 T __pfx_prepare_kernel_cred ffffffffa66d0ba0 T prepare_kernel_cred ffffffffa8061668 r __ksymtab_prepare_kernel_cred

在未开启 KASLR 时,commit_creds、prepare_kernel_cred的地址应当与 vmlinux 中的静态地址一致;开启 KASLR 后则会整体偏移。这组地址正是后续构造内核 ROP 链提权(commit_creds(prepare_kernel_cred(&init_task)),见 basic-knowledge.md)所需的关键输入。

计算 KASLR 偏移的实战模式

结合仓库中 ROP 章节的示例(如 rop.md 中打印的kaslr offset、kpti-bypass.md 中的SUCCESS_MSG(" , kaslr offset: ")),一个典型的计算流程是:

leaked_addr = 从漏洞 / dmesg / 其它泄露点获得的内核地址 static_addr = 该符号在 vmlinux 中的静态地址(readelf/nm 查得) kaslr_offset = leaked_addr - static_addr commit_creds = static_commit_creds + kaslr_offset prepare_kernel_cred = static_prepare_kernel_cred + kaslr_offset

只要泄漏点足够准确,这个偏移一旦算出,整个内核的代码段、数据段布局对攻击者而言就完全是透明的——这正是 KASLR 的弱点所在,也是其后续演进为 FGKASLR 的直接动机。

从 KASLR 到 FGKASLR:函数级随机化的演进

KASLR 虽然在一定程度上能够缓解攻击,但若攻击者通过信息泄露漏洞获取到内核中的某个地址,仍能直接得知内核加载地址偏移,从而得知整个内核地址布局。因此有研究者基于 KASLR 实现了 FGKASLR(Function Granular KASLR),以函数粒度重新排布内核代码,相关说明见 basic-knowledge.md 与 fgkaslr.md。

两者的核心差异:

特性KASLRFGKASLR
随机化粒度整个内核镜像整体偏移函数粒度(.text.* 节区重排)
相对偏移是否固定固定(可推导整个段)不固定(函数之间相对偏移改变)
绕过难度泄漏一个地址即可算全段泄漏单个函数地址无法直接推导全部
架构支持通用目前仅支持 x86_64

FGKASLR 的实现要点(详见 fgkaslr.md):

  • 编译阶段:利用 gcc 的-ffunction-sections选项,将内核中不同的函数放到不同的 section 中;编译后的 vmlinux 保留所有节区头(Section Headers),以便知道每个函数的地址范围,同时还有一个重定位地址的扩展表,供内核解压后乱序排列函数;
  • 加载阶段:解压内核后先检查保留的符号信息,寻找需要随机化的.text.*节区,其中第一个合并节区.text会被跳过(系统调用入口等汇编代码位于其中);其余节区地址会被随机化但仍与.text相邻;
  • 随机化条件:只有同时满足"节区名以.text开头、section flags 包含SHF_ALLOC、SHF_EXECINSTR"的节区才会参与随机化,因此__ksymtab与.data不参与随机化——这意味着它们相对于内核镜像的偏移仍是固定的,攻击者依然可以利用__ksymtab中的kernel_symbol结构(value_offset/name_offset/namespace_offset)逐项推导出prepare_kernel_cred、commit_creds等符号地址。

可见,KASLR 是理解后续一切内核随机化攻防的起点:掌握了"整体偏移"模型,才能理解 FGKASLR 为什么要打散函数、KPTI 为什么要隔离页表、__ksymtab为什么是绕过随机化的稳定锚点。

小结

关键点结论
KASLR 本质内核镜像整体加上随机偏移(粒度 256MB),内部相对偏移不变
默认基址代码段0xffffffff81000000,direct map0xffff888000000000(未开启时)
开启方式QEMU-append中加入kaslr
关闭方式QEMU-append中加入nokaslr(便于调试)
绕过原理泄漏内核任意段地址 → 计算偏移 → 推导全段符号地址
关键文件/proc/kallsyms(受kptr_restrict限制)、__ksymtab、vmlinux 静态符号
演进方向FGKASLR 函数级随机化、KPTI 页表隔离

参考

  • KASLR 文档(本文主体)
  • FGKASLR 文档
  • 内核基础知识:KASLR 与符号地址
  • QEMU 内核环境搭建:启动参数与 kaslr/nokaslr
  • 信息泄漏防护:dmesg_restrict 与 kptr_restrict
  • KPTI 与 KASLR 的关系
  • 内核 ROP:kaslr 场景下的提权示例
  • 文档
  • 网络安全
  • 教程

【免费下载链接】ctf-wiki

Come and join us, we need you!

项目地址:https://gitcode.com/gh_mirrors/ct/ctf-wiki
点击查看免费下载

相关推荐

上一篇:PromptX开源项目完整指南:从零构建AI专家系统
下一篇:MKS TinyBee智能主板:无线控制的3D打印技术突破

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询