- 文档
- 网络安全
- 教程
【免费下载链接】ctf-wiki
Come and join us, we need you!
内核日志(dmesg)与符号表(/proc/kallsyms)是 kernel pwn 中攻击者获取内核地址信息的两个核心情报源:前者可能直接打印出内核地址或敏感数据,后者则直接暴露 commit_creds、prepare_kernel_cred 等关键符号的加载地址。本文以 ctf-wiki 仓库中 信息泄漏防护文档 为主体,系统讲解 Linux 内核针对这两类信息泄漏途径的内核级访问控制机制——dmesg_restrict与kptr_restrict的语义、配置方法、在内核源码中的底层实现,以及在 CTF 内核题环境中的实际影响与实战应对。
背景:为什么内核要限制信息输出
在 Linux kernel pwn 中,攻击者最常用的提权手段是执行commit_creds(prepare_kernel_cred(&init_task)),将当前进程的凭证(cred)替换为 root 权限的凭证。正如仓库的 基础知识文档 所述,这两个函数与init_cred等关键符号的地址,传统上都可以通过/proc/kallsyms查看(较老的内核版本中是/proc/ksyms),例如:
$ 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与此同时,内核函数printk()的输出虽然不一定显示在终端上,但一定会写入内核日志缓冲区,可以通过dmesg命令查看。驱动在printk中泄漏的地址、flag 加载地址等信息,都可能成为攻击链条上的关键拼图。
因此,内核提供了一系列访问控制手段,对这些对象的信息输出施加"不可读"限制。这正属于仓库中 访问控制(Access Control)专题 的范畴:通过给内核对象添加访问控制,使其具备不可写或不可读等约束。其中针对信息泄漏的两个核心开关就是dmesg_restrict与kptr_restrict。
dmesg_restrict:限制内核日志的读取
选项语义
dmesg_restrict用于控制是否允许使用dmesg命令查看内核日志缓冲区(kernel's log buffer)中的消息。内核文档给出的精确定义如下:
dmesg_restrict: This toggle indicates whether unprivileged users are prevented from using dmesg(8) to view messages from the kernel's log buffer. When dmesg_restrict is set to (0) there are no restrictions. When dmesg_restrict is set set to (1), users must have CAP_SYSLOG to use dmesg(8). The kernel config option CONFIG_SECURITY_DMESG_RESTRICT sets the default value of dmesg_restrict.可以归纳为:
- 0(默认):无任何限制,任何用户都可以通过
dmesg查看内核日志。 - 1:只有拥有
CAP_SYSLOG权限的用户才可以通过dmesg查看内核日志。
其中CAP_SYSLOG是 Linux capability 中的一项,通常只有 root 用户(或具备该 capability 的进程)持有。这一设计的动机在于:内核日志中可能包含地址信息或敏感信息,研究者和内核维护者因此提出需要限制对内核日志的访问。
默认值来源:CONFIG_SECURITY_DMESG_RESTRICT
值得注意的一点是,dmesg_restrict的默认值由内核编译选项CONFIG_SECURITY_DMESG_RESTRICT决定。也就是说,出题者在编译内核时就可以决定题目环境默认是否开启该保护。在 CTF 内核题目中,除了编译期默认值外,启动脚本(init)也可以动态写入该开关进行覆盖,二者协同决定最终环境状态。
运行时配置方法
dmesg_restrict作为一个 sysctl 内核参数,可以通过/proc/sys/kernel/dmesg_restrict在运行时动态开启或关闭:
# 开启:限制非特权用户读取内核日志 echo 1 > /proc/sys/kernel/dmesg_restrict # 关闭:允许任意用户读取内核日志 echo 0 > /proc/sys/kernel/dmesg_restrict在 CTF 题目中,这一开关通常被写入文件系统的启动脚本init中。仓库的 内核 ROP 实战文档 展示了一个典型的内核题启动脚本,其中echo 1 > /proc/sys/kernel/dmesg_restrict与echo 1 > /proc/sys/kernel/kptr_restrict成对出现:
#!/bin/sh mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t devtmpfs none /dev /sbin/mdev -s mkdir -p /dev/pts mount -vt devpts -o gid=4,mode=620 none /dev/pts chmod 666 /dev/ptmx cat /proc/kallsyms > /tmp/kallsyms echo 1 > /proc/sys/kernel/kptr_restrict echo 1 > /proc/sys/kernel/dmesg_restrict ifconfig eth0 up udhcpc -i eth0 ifconfig eth0 10.0.2.15 netmask 255.255.255.0 route add default gw 10.0.2.2 insmod /core.ko ...仓库的 qemu 环境搭建文档 中也给出了同样的配置模式,即在启动脚本中通过以下两行同时开启两项保护:
echo 1 > /proc/sys/kernel/dmesg_restrict echo 1 > /proc/sys/kernel/kptr_restrict从源码结构看,这种"先保存 kallsyms 快照、再开启限制"的写法(见上例第 9 行)在真实题目中相当常见——出题者在 root 权限下先把符号表内容备份到可读文件,再收紧权限,从而既保证题目可解(借助备份文件),又模拟了真实环境中的信息泄漏防护。
kptr_restrict:限制内核地址的输出
选项语义
kptr_restrict用于控制输出内核地址时施加的限制,主要限制以下接口:
- 通过
/proc获取的内核地址(最典型的是/proc/kallsyms); - 通过其它接口(有待研究)获取的地址。
内核文档给出的精确定义如下:
kptr_restrict: This toggle indicates whether restrictions are placed on exposing kernel addresses via /proc and other interfaces. When kptr_restrict is set to 0 (the default) the address is hashed before printing. (This is the equivalent to %p.) When kptr_restrict is set to (1), kernel pointers printed using the %pK format specifier will be replaced with 0's unless the user has CAP_SYSLOG and effective user and group ids are equal to the real ids. This is because %pK checks are done at read() time rather than open() time, so if permissions are elevated between the open() and the read() (e.g via a setuid binary) then %pK will not leak kernel pointers to unprivileged users. Note, this is a temporary solution only. The correct long-term solution is to do the permission checks at open() time. Consider removing world read permissions from files that use %pK, and using dmesg_restrict to protect against uses of %pK in dmesg(8) if leaking kernel pointer values to unprivileged users is a concern. When kptr_restrict is set to (2), kernel pointers printed using %pK will be replaced with 0's regardless of privileges.具体输出的内容与该选项配置的值有关:
- 0(默认):没有限制,但地址在打印前会进行哈希处理(equivalent to
%p,即等价于使用%p格式化符输出哈希值)。 - 1:使用
%pK输出的内核指针地址将被替换为 0,除非用户具有CAP_SYSLOG特权,并且有效用户 ID/组 ID(effective user and group ids)与真实 ID(real ids)相等。 - 2:使用
%pK输出的内核指针都将被替换为 0,即与权限无关,任何权限都无法读到真实地址。
关键技术细节:read() 时校验
文档中特别强调了一个实现细节:%pK的权限检查是在read() 时进行的,而不是在 open() 时进行。这意味着:如果一个特权进程在 open() 与 read() 之间提升了权限(例如通过 setuid 二进制),%pK依然不会把内核指针泄漏给非特权用户。
同时内核文档也指出,这是一种临时性解决方案(temporary solution only),正确的长期方案是在 open() 时做权限检查,并建议:如果担心把内核指针值泄漏给非特权用户,应当移除使用%pK的文件的 world 读权限,并配合dmesg_restrict来保护 dmesg(8) 中对%pK的使用。
运行时配置方法
# 级别 0:默认,地址哈希后打印(%p) echo 0 > /proc/sys/kernel/kptr_restrict # 级别 1:非特权用户看到的 %pK 输出被替换为 0 echo 1 > /proc/sys/kernel/kptr_restrict # 级别 2:所有用户的 %pK 输出都被替换为 0 echo 2 > /proc/sys/kernel/kptr_restrict对 /proc/kallsyms 的实际影响
当开启kptr_restrict保护后,攻击者就不能通过/proc/kallsyms获取内核中某些敏感的地址了,如commit_creds、prepare_kernel_cred。这也是 kernel pwn 中 ROP 链构造最依赖的两个符号。
开启保护前后/proc/kallsyms的典型差异可以通过仓库 qemu 环境搭建文档 中的示例观察:在未开启kptr_restrict时,root 用户可以直接查询到符号的真实地址:
# cat /proc/kallsyms | grep prepare_kernel_cred ffffffffa66d0b90 T __pfx_prepare_kernel_cred ffffffffa66d0ba0 T prepare_kernel_cred ffffffffa8061668 r __ksymtab_prepare_kernel_cred而当kptr_restrict=1(或 2)且当前进程不具备CAP_SYSLOG权限时,%pK输出的内核指针将被替换为 0,非特权用户看到的内容会退化为全零地址,无法直接用于 ROP 计算。
需要特别说明的边界情况是:kptr_restrict限制的是实时查询。如果题目启动脚本在开启保护之前,就已经以 root 权限将/proc/kallsyms的内容备份到了普通用户可读的文件(如/tmp/kallsyms),那么攻击者依然可以从备份文件中读取符号地址。这正是 内核 ROP 实战文档 中分析过的场景:
- 启动脚本第 9 行把
kallsyms的内容保存到了/tmp/kallsyms,那么就能从/tmp/kallsyms中读取commit_creds、prepare_kernel_cred的地址; - 第 10 行把
kptr_restrict设为 1,这样就不能通过/proc/kallsyms实时查看函数地址了,但第 9 行已经把其中的信息保存到了一个可读的文件中,这句就无关紧要了; - 第 11 行把
dmesg_restrict设为 1,这样就不能通过dmesg查看 kernel 的信息了。
这也揭示了 CTF 内核题的一个通用规律:信息泄漏防护的强度,取决于符号地址泄露渠道是否被彻底封死。若备份文件存在,KASLR 之类基于隐藏地址的防护(参见仓库 FGKASLR 文档)也会被一并削弱。
与其它内核防护机制的协同
与 KASLR / FGKASLR 的关系
kptr_restrict限制的是"谁能看到地址",而 KASLR(内核地址空间布局随机化)限制的是"地址是否可预测"。二者互补:KASLR 虽然能在一定程度上缓解攻击,但若攻击者通过信息泄漏漏洞获取到内核中的某个地址,仍能直接推算出内核加载偏移进而得知整个内核地址布局(基础知识文档 中对此有说明)。因此,kptr_restrict正是为了封堵/proc/kallsyms这条最直接的地址泄漏途径。FGKASLR 更进一步,不仅随机化加载基址,还在函数粒度重排内核代码,并且(如仓库 FGKASLR 文档 所述)为了隐藏新的内存布局,/proc/kallsyms中符号使用随机的顺序排列,与kptr_restrict一起增加了从符号表恢复布局的难度。
与 KPTI 绕过链的配合
在 KPTI 绕过文档 中,swapgs_restore_regs_and_return_to_usermode等关键函数的地址也需要从/proc/kallsyms中获得。这些利用链中普遍采用的/tmp/kallsyms备份读取模式(见 bypass-smep.md、ret2usr.md 等文档中的fopen("/tmp/kallsyms", "r")代码)说明:即便开启了kptr_restrict,只要题目保留了符号表备份,信息泄漏防护就形同虚设,出题与解题的博弈点往往就落在这个备份文件上。
与 dmesg 信息泄漏的关联
dmesg_restrict与kptr_restrict常常成对开启,因为dmesg输出中也可能包含%pK打印的内核指针。内核文档明确指出,可以"使用 dmesg_restrict 来保护 dmesg(8) 中对 %pK 的使用",即防止通过日志间接泄漏内核指针。在 double-fetch 利用文档 中也能看到反面案例:当驱动通过printk输出 flag 的加载地址时,攻击者正是通过dmesg读取该地址(exp 中system("dmesg | grep flag > /tmp/addr.txt")),因此解题前置条件之一就是"关闭dmesg_restrict,否则无法查看 printk 信息",具体操作是在启动脚本中加入:
echo 0 > /proc/sys/kernel/dmesg_restrict这从正反两面印证了dmesg_restrict在实战中对信息泄漏链路的决定性影响。
实战视角:如何分析一道内核题的保护强度
综合以上机制,拿到一道 CTF 内核题时,可以从以下角度评估其信息泄漏防护强度:
- 检查启动脚本(init):搜索
dmesg_restrict、kptr_restrict的写入值。若二者均被设为 1 或 2,说明题目限制了 dmesg 与 kallsyms 的直接读取。 - 检查符号表备份:寻找
cat /proc/kallsyms > /tmp/kallsyms之类的语句。存在备份则意味着符号地址仍可获取,ROP 链可以照常构造。 - 检查 printk 泄漏面:逆向驱动中是否有
printk输出地址或敏感数据;若有,需确认dmesg_restrict是否开启,以及当前用户是否具备读取权限。 - 结合 KASLR 状态:若同时存在符号表备份且未开启
nokaslr,通常仍需借助泄漏出的单个地址推算基址;若符号表不可读且无备份,则需依赖其它信息泄漏漏洞(如未初始化内存、堆风水)来恢复地址。
上述检查均基于仓库中真实题目环境(rop.md、qemu-emulate.md、writable-root.md 等文档所描述的启动脚本与利用代码),可作为可复用的分析方法。
小结
dmesg_restrict与kptr_restrict是 Linux 内核针对信息泄漏的两道基础访问控制闸门:前者以CAP_SYSLOG为门槛限制内核日志的读取,后者通过%pK格式化符在输出层面对内核地址进行哈希或清零处理,配合 KASLR/FGKASLR、KPTI 等机制共同构筑内核地址空间的保密防线。对 CTF kernel pwn 而言,理解这两个开关的语义与配置位置,是评估题目难度、选择信息泄漏利用路径的第一步——无论目标是构造commit_creds(prepare_kernel_cred(&init_task))提权链,还是通过 dmesg 中的 printk 输出定位 flag,都必须先厘清这两道闸门的状态与绕过空间。
参考
- 信息泄漏(本文主体文档)
- 访问控制专题索引
- 内核 pwn 基础知识(kallsyms 与 commit_creds)
- 内核 ROP 实战(启动脚本中的防护配置)
- qemu 环境搭建与调试(kallsyms 查询示例)
- double-fetch 利用(dmesg 读取 printk 信息)
- 文档
- 网络安全
- 教程
【免费下载链接】ctf-wiki
Come and join us, we need you!
相关推荐
ctf-wiki 内核 Pwn 防御机制解读:dmesg_restrict 与 kptr_restrict 信息泄漏防护详解
ctf wiki 内核 Pwn 防御机制解读:dmesg_restrict 与 kptr_restrict 信息泄漏防护详解 导读 本文聚焦 ctf wiki
文档网络安全教程CTF 内核 pwn 防护系列:Linux 内核 SMAP(Supervisor Mode Access Protection)原理、开关配置与绕过手法详解
CTF 内核 pwn 防护系列:Linux 内核 SMAP(Supervisor Mode Access Protection)原理、开关配置与绕过手法详解 在
文档网络安全教程CTF 内核利用中的 KASLR:原理、QEMU 开关实战与绕过思路(ctf-wiki 内核防护篇)
CTF 内核利用中的 KASLR:原理、QEMU 开关实战与绕过思路(ctf wiki 内核防护篇) 导读 KASLR(Kernel Address Space
文档网络安全教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考