利用Linux内核CVE-2021-26708漏洞研究LKRG防护机制
研究背景
在本研究中,我继续深入分析Linux内核中的CVE-2021-26708漏洞。通过改进漏洞利用原型,我从攻击者角度研究了Linux内核运行时防护机制(LKRG)。本文将详细介绍如何发现新的LKRG绕过方法,以及如何进行负责任的漏洞披露。
研究动机
在最初的研究中,我描述了针对x86_64平台Fedora 33 Server的本地权限提升漏洞利用原型。Linux内核虚拟套接字实现中的竞争条件可能导致内核内存中四个字节的损坏。攻击者可以逐步将这种错误转化为任意读写内核内存的能力,从而提升系统权限。但这种权限提升方法存在一些限制,阻碍了我在LKRG保护下的系统中进行实验。
漏洞利用技术细节
控制流劫持
我的漏洞利用原型通过劫持被攻击内核对象sk_buff中destructor_arg的析构函数调用来实现任意写入:
void (*callback)(struct ubuf_info *, bool zerocopy_success);
当内核在skb_zcopy_clear()函数中调用此析构函数时,RDI寄存器包含函数的第一个参数(ubuf_info结构的地址),RSI寄存器存储第二个参数值1。
ROP链构造
由于寄存器限制,我找到了特殊的ROP gadget:
mov rdx, qword ptr [rdi + 8]
mov qword ptr [rdx + rcx*8], rsi
ret
这个gadget允许在不切换内核栈的情况下写入内核内存,通过将cred结构中的uid、gid、effective uid和effective gid字段置零来提升权限。
寄存器控制分析
通过调试分析,发现RBP寄存器包含skb_shared_info的地址,这指向攻击者控制的内存区域。这为成功利用提供了希望。
LKRG绕过技术
初始尝试
最初尝试通过修改/etc/passwd文件来重置root密码,但由于内核权限检查和SELinux策略限制,这种方法未能成功。
直接攻击LKRG
通过分析LKRG代码,发现两个关键函数:
- p_check_integrity(): 执行内核代码完整性检查
- p_cmp_creds(): 比较系统进程权限与LKRG内部数据库
代码修补攻击
最终成功的攻击方法是通过ROP链直接重写这两个函数的代码。使用指令字节0x48 0x31 0xc0 0xc3(xor rax, rax ; ret)替换原有代码,使这些函数直接返回0。
ROP链实现
完整的ROP链包括以下部分:
- 保存RSP:恢复初始栈指针值
- 销毁LKRG:调用kallsyms_lookup_name()查找目标函数
- 条件处理:处理LKRG模块是否加载的情况
- 代码修补:使用text_poke()函数修改内核代码
- 权限提升:修改cred结构提升权限
- 恢复执行:恢复RSP并继续正常执行
技术挑战
动态代码修改
Linux内核在运行时可以通过CONFIG_DYNAMIC_FTRACE等机制修改自身代码,这导致许多预期的JOP gadget失效。
调试困难
LKRG会检测内核代码修改,当gdb设置断点修改内核指令时,LKRG会将其视为"完整性错误"并导致系统崩溃。
负责任披露
2021年6月10日,我向Adam Zabrocki和Alexander Peslyak报告了LKRG绕过方法的实验结果。经过详细讨论后,于7月3日在lkrg-users邮件列表中公开了研究结果。
结论与建议
LKRG是一个优秀的项目,但在内核级别检测内核漏洞利用后果存在根本性限制。建议将LKRG移植到hypervisor级别或Arm Trusted Execution Environment中,以提供更有效的防护。
这项研究不仅改进了CVE-2021-26708的利用技术,还提供了针对内核运行时防护系统的深入分析,对Linux开发社区具有重要的实践价值。