news 2026/7/31 6:34:01

GhostLock (CVE-2026-43499):潜伏十五年的内核栈UAF完整利用链

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GhostLock (CVE-2026-43499):潜伏十五年的内核栈UAF完整利用链

作者注:本文基于在 Google kernelCTF 中成功利用 CVE-2026-43499 的实战经验撰写。该漏洞自 2011 年 Linux 2.6.39 引入,至 2026 年修复,横跨十五年,影响所有主流发行版。


一、漏洞概述

1.1 基本信息

CVE 编号:CVE-2026-43499
漏洞名称:GhostLock
漏洞类型:基于栈的 Use-After-Free (UAF) / 本地权限提升 (LPE)
CVSS 3.1 评分:7.8 (High)
影响范围:Linux 2.6.39 至 7.1(不含修复版本),具体为 v2.6.39 ≤ kernel < v6.1.175、v6.2 ≤ kernel < v6.6.140、v6.7 ≤ kernel < v6.12.86、v6.13 ≤ kernel < v6.18.27、v6.19 ≤ kernel < v7.0.4
引入时间:2011 年(Linux 2.6.39)
修复提交3bfdc63936dd("rtmutex: Use waiter::task instead of current in remove_waiter()")

1.2 前置依赖

该漏洞的触发仅依赖于CONFIG_FUTEX_PI——该选项在几乎所有发行版内核中均为默认启用。无需任何特权能力(no capabilities required),普通本地用户即可触发,且可从容器内部逃逸至宿主机。

1.3 漏洞本质

漏洞位于kernel/locking/rtmutex.c的实时互斥量(rtmutex)优先级继承(Priority Inheritance, PI)路径中。核心问题是remove_waiter()函数在FUTEX_CMP_REQUEUE_PI的代理锁(proxy-lock)回滚路径中,错误地对current而非waiter->task执行清理操作。这导致三个并发问题:

  1. 红黑树出队(rbtree dequeue)未持有waiter->task->pi_lock

  2. waiter 任务的pi_blocked_on状态未被清除,留下一个指向已返回内核栈帧的悬垂指针(dangling pointer)

  3. rt_mutex_adjust_prio_chain()操作了错误的 task


二、根本原因(Root Cause)

2.1 代码路径分析

remove_waiter()本为 slowlock 路径设计,假定其清理的 waiter 始终属于当前运行任务。但在futex_requeue()调用的rt_mutex_start_proxy_lock()代理锁回滚场景中,waiter 属于另一个正在睡眠的线程——内核在检测到死锁循环后以-EDEADLK进行回滚。

关键代码逻辑如下(漏洞版本):

c

// kernel/locking/rtmutex.c (vulnerable) static void remove_waiter(struct rt_mutex *lock, struct rt_mutex_waiter *waiter) { // 错误:在 proxy-lock 回滚场景下,waiter->task != current // 但以下操作全部基于 current raw_spin_lock(&current->pi_lock); // ← 锁错了任务! rt_mutex_dequeue(lock, waiter); // ← 从红黑树出队 current->pi_blocked_on = NULL; // ← 清空了 current 而非 waiter->task! raw_spin_unlock(&current->pi_lock); // ... }

rt_mutex_start_proxy_lock()中:

c

int rt_mutex_start_proxy_lock(struct rt_mutex *lock, struct rt_mutex_waiter *waiter, struct task_struct *task) { // ... ret = rt_mutex_wait_proxy_lock(lock, waiter, task); if (unlikely(ret)) // ← -EDEADLK 回滚路径 remove_waiter(lock, waiter); // ← 错误!waiter->task != current // ... }

2.2 悬垂指针的形成

-EDEADLK回滚发生时:

  1. waiter结构体位于task 的内核栈上(作为局部变量);

  2. remove_waiter()调用current->pi_blocked_on = NULL——但应当被清除的是waiter->task->pi_blocked_on

  3. 结果:waiter->task->pi_blocked_on仍然指向栈上的waiter地址

  4. waiter->taskrt_mutex_start_proxy_lock()返回用户空间后,其内核栈帧被销毁并重用;

  5. pi_blocked_on成为一个指向已释放内核栈内存的悬垂指针

2.3 死锁循环构造

要触发该漏洞,攻击者需要构造一个优先级反转死锁(priority-inversion deadlock),涉及三个 futex 和多线程协调。核心思路:

  • 线程 A 持有一个 PI-futex 锁,被线程 B 阻塞;

  • 线程 C 通过FUTEX_CMP_REQUEUE_PI尝试代理锁;

  • 内核检测到死锁循环,返回-EDEADLK并触发上述回滚路径;

  • 回滚完成后,线程 B 的pi_blocked_on指向已释放的栈内存。

此时,攻击者获得了一个可预测的 UAF 窗口——pi_blocked_on指向的内存区域随后可被攻击者控制的数据重新占据。


三、利用原语与栈回收

3.1 栈上对象的特殊性

该漏洞的 UAF 对象位于内核栈上,而非堆(heap)。这带来了独特的挑战与机遇:

  • 挑战:栈帧的回收和重用由内核的栈管理机制控制,难以像堆 spray 那样精确控制;

  • 机遇:内核栈地址具有相对可预测性(尤其在未启用RANDOMIZE_KSTACK_OFFSET时),且同一 CPU 上的后续系统调用会重用相同的栈区域。

3.2 PR_SET_MM_MAP:精确控制栈内存

为精确控制被释放栈帧的内容,攻击者利用prctl(PR_SET_MM_MAP, ...)接口。

PR_SET_MM_MAP允许无特权用户(在CONFIG_CHECKPOINT_RESTORE=y内核上)修改进程的mm_struct边界和saved_auxv 向量。攻击者通过精心构造的 auxv 向量,将伪造的rt_mutex_waiter结构体放置在目标栈地址上。

关键 Spray 策略:

c

struct prctl_mm_map { // 控制 mmap 基址、栈顶等,间接影响内核栈布局 unsigned long start_code, end_code; unsigned long start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; unsigned long *auxv; // ← 关键:指向用户空间构造的 auxv 向量 unsigned long auxv_size; };

通过反复调用PR_SET_MM_MAP并配合pselect()等系统调用消耗栈空间,攻击者可以在目标栈地址上精确布置伪造的rt_mutex_waiter

3.3 栈帧回收的时间窗口

完整的栈回收利用流程:

  1. 触发 UAF:构造死锁 → 触发-EDEADLK回滚 →pi_blocked_on悬垂;

  2. 栈帧释放:waiter 任务返回用户空间,其内核栈帧被标记为可重用;

  3. 栈 Spray:通过PR_SET_MM_MAP+ 精心构造的系统调用序列,在同一 CPU 上重用该栈区域

  4. 伪造对象:在被回收的栈位置上写入伪造的rt_mutex_waiter,包含攻击者控制的lock指针等字段;

  5. 触发 UAF 读/写:内核在后续的 PI 操作中解引用pi_blocked_on,访问攻击者控制的数据。


四、红黑树擦除引发的受限写入

4.1 写入原语的本质

该漏洞提供的核心利用原语来自于rt_mutex_dequeue()的红黑树(rbtree)擦除操作

当内核在 PI 路径中处理伪造的rt_mutex_waiter时,会调用:

c

rt_mutex_dequeue(lock, waiter);

在红黑树的rb_erase()实现中,当被删除节点是根节点且只有一个子节点时,该子节点会直接替换根指针——即执行一次*(u64 *)target = W0_BASE类型的写入。

4.2 约束条件

该写入原语受到多重约束:

目标地址控制:写入的目标地址由伪造waiterlock字段控制。具体地,攻击者设置waiter->lock = target - offsetof(struct rt_mutex, waiters),使得红黑树根指针的写入发生在target地址。

写入值约束:写入的值W0_BASE红黑树子节点的指针——即攻击者控制的伪造waiter结构体的地址或其派生值。这意味着:

  • 写入值并非完全任意,而是指向攻击者可控内存区域的指针

  • 但通过精心布局伪造的waiter在内存中的位置,可以将W0_BASE调整为期望的指针值

目标地址约束:目标地址target必须指向一个可写的内核内存区域,且该区域附近必须存在一个有效的rt_mutex红黑树根结构。实践中这意味着:

  • 目标地址前后的内存布局必须满足自旋锁(spinlock)状态检测——内核在操作前会检查lock->wait_lock的状态;

  • 目标地址不能是只读内存(如__read_mostly段的部分区域)。

4.3 从受限写入到原语升级

通过多次触发该写入原语(每次精心调整伪造waiter的布局),攻击者可以将受限的单次指针写入升级为:

  1. 任意地址读:通过将目标指向某个函数指针,然后触发该函数调用,读取其值;

  2. 受限的任意地址写:通过链式写入,逐步将W0_BASE调整为期望值。

实际利用中,该写入原语被称为"Write-1-only" 或 "child-node PI write"


五、绕过与劫持

5.1 KASLR 绕过:Prefetch 侧信道泄漏

在获得受限写入原语后,攻击者首先需要绕过 KASLR(内核地址空间布局随机化)以获取内核基址和物理映射区基址。

利用prefetch 指令的侧信道(TLB 时序)实现 KASLR 泄漏。核心原理:

  • 物理地址线性映射区起始地址前的虚拟地址并不存在到物理页面的映射

  • prefetch指令在访问有效映射和无效映射时的执行时间存在可测量的差异

  • 通过暴力扫描虚拟地址空间,攻击者可以确定内核映像基址physmap 基址

具体攻击流程:

  1. 对候选虚拟地址范围执行prefetchnta/prefetcht2指令;

  2. 通过rdtsc精确测量执行时间;

  3. 利用时序差异确定有效映射的边界,从而推导出 KASLR 偏移。

该技术即使在有 KPTI 保护的系统中仍然有效。

5.2 定位 CPU 入口区(CEA)

在获得 KASLR 基址后,攻击者定位per-CPU Entry Area (CEA)。CEA 包含了每个 CPU 的异常栈、SYSCALL 入口等重要数据结构。

定位方法利用 CEA 在init_cea_offsets()中建立的固定偏移关系:

text

cea_base = kaslr_base + CPU_ENTRY_AREA_BASE_OFFSET

通过 CEA,攻击者可以获取:

  • 当前 CPU 的cpu_tss_rw(任务状态段);

  • entry_SYSCALL_64入口地址;

  • 其他关键 per-CPU 数据结构。

5.3 函数表劫持:覆盖 inet6_protos[IPPROTO_UDP]

获得任意写入能力后,攻击者选择劫持inet6_protos[]函数表

inet6_protos是一个全局数组,存储了各 IPv6 协议的inet6_protocol结构体指针:

c

const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS] __read_mostly;

攻击者通过受限写入原语,将inet6_protos[IPPROTO_UDP](索引 17)覆盖为用户空间构造的伪造inet6_protocol结构体

伪造的inet6_protocol结构体包含:

c

struct inet6_protocol { int (*handler)(struct sk_buff *skb); // ... int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info); // ... };

攻击者将handlererr_handler指向精心构造的ROP 链起始地址(用户空间 mmap 的地址或内核可写区域)。

5.4 触发控制流劫持:回环 IPv6 UDP

劫持完成后,攻击者通过回环(loopback)IPv6 UDP 流量触发控制流劫持:

  1. 创建 IPv6 UDP socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);

  2. ::1(IPv6 回环地址)发送 UDP 数据包;

  3. 内核协议栈在接收路径上调用inet6_protos[IPPROTO_UDP]->handler()

  4. 控制流跳转到攻击者伪造的 handler 地址 →开始执行 ROP 链

该方法的优势:

  • 无需网络权限:回环流量完全在内核内部;

  • 高度可靠:UDP 是无状态协议,触发路径简单;

  • 可重复触发:发送任意 UDP 包即可再次触发。


六、DirtyMode 提权阶段

6.1 缩短 ROP 链的策略

完整的 ROP 链可以执行复杂的 kernel 任意读写,但 ROP 链越长,栈空间约束和 gadget 可用性越成问题。为缩短 ROP 链,攻击者采用"单次写入"提权策略

6.2 翻转 core_pattern 权限位

攻击目标锁定在coredump_sysctls表中的core_pattern.mode字段

core_pattern是内核的 coredump 处理器配置,当进程崩溃时,内核会以root 权限执行core_pattern中指定的程序。

通过 ROP 链执行一次精确的 64 位写入,翻转core_pattern.mode的权限位:

text

*(u64 *)&coredump_sysctls.core_pattern.mode |= S_IWUSR | S_IXUSR

这使得原本只读的core_pattern变得可写

6.3 获取 Root 权限

  1. 写入恶意 core_pattern

    bash

    echo "|/tmp/rootme" > /proc/sys/kernel/core_pattern

    其中/tmp/rootme是一个由攻击者控制的 setuid-root 程序或脚本。

  2. 触发 core dump:攻击者使自己的一个子进程触发段错误(segmentation fault)。

  3. 内核以 root 权限执行恶意处理器:内核在生成 core dump 时,以root 身份执行/tmp/rootme

  4. 获取 root shell/tmp/rootme执行execve("/bin/sh", ...)或修改/etc/passwd,完成提权。

该方法的精妙之处在于:

  • 仅需一次内核写入(翻转 mode 位);

  • 后续操作完全在用户空间完成,无需维护复杂的 kernel ROP 状态;

  • 规避了复杂的 cred 结构体定位和修改(传统 LPE 的常见难点)。


七、修复与缓解

7.1 官方补丁

修复提交3bfdc63936dd("rtmutex: Use waiter::task instead of current in remove_waiter()")的核心改动:

diff

// kernel/locking/rtmutex.c static void remove_waiter(struct rt_mutex *lock, struct rt_mutex_waiter *waiter) { - struct task_struct *task = current; + struct task_struct *task = waiter->task; raw_spin_lock(&task->pi_lock); rt_mutex_dequeue(lock, waiter); - current->pi_blocked_on = NULL; + task->pi_blocked_on = NULL; raw_spin_unlock(&task->pi_lock); // ... }

同时在rt_mutex_start_proxy_lock()中增加了条件判断:

diff

- if (unlikely(ret)) + if (ret && rt_mutex_has_waiters(lock)) remove_waiter(lock, waiter);

7.2 补丁完整性分析

该补丁修复了根本问题,但存在一个值得注意的隐患:

NPD(NULL Pointer Dereference)风险:在remove_waiter()中,waiter->task理论上可能为 NULL(例如在某种竞态条件下 waiter 已被部分清理)。虽然当前代码路径下这种情况不会发生,但补丁未添加显式的 NULL 检查,未来若引入新的调用路径,可能引入新的漏洞。

补充修复:社区后续可能考虑增加:

c

if (WARN_ON_ONCE(!waiter->task)) return;

7.3 缓解措施的有效性分析

RANDOMIZE_KSTACK_OFFSET:该选项在每次系统调用时随机化内核栈的起始偏移,使得攻击者难以精确预测pi_blocked_on悬垂指针指向的栈地址。然而,攻击者可通过多次尝试(概率性攻击)或侧信道来克服——该缓解措施增加利用难度但不消除漏洞

STATIC_USERMODE_HELPER:该选项限制内核只能执行预定义的静态用户态辅助程序,可阻断core_pattern执行任意用户程序。但:

  • 大多数发行版默认未启用该选项;

  • 即使启用,攻击者仍可通过其他方法提权(如直接修改 cred);

  • 仅阻断提权的最后一步,不解决 UAF 本身。

防御建议

  1. 立即打补丁:升级至 Linux ≥ 6.1.175 / 6.6.140 / 6.12.86 / 6.18.27 / 7.0.4;

  2. 多租户环境优先:云服务器、容器平台、CI runner 应最先修复

  3. 考虑启用RANDOMIZE_KSTACK_OFFSET作为纵深防御;

  4. 监控异常:关注futex系统调用的异常模式及内核 panic 日志。


结语

GhostLock (CVE-2026-43499) 是一个教科书级别的栈 UAF 漏洞,其利用链展示了从单一代码缺陷完整 root 提权的完整路径:

text

remove_waiter() 错误使用 current ↓ pi_blocked_on 悬垂指针(栈 UAF) ↓ PR_SET_MM_MAP 栈 Spray(回收 + 伪造) ↓ rt_mutex_dequeue() 受限写入原语 ↓ Prefetch 侧信道(KASLR 绕过) ↓ inet6_protos 函数表劫持(CFI 绕过) ↓ core_pattern mode 翻转(单次写入提权) ↓ Root Shell

该漏洞自 2011 年潜伏至 2026 年,横跨十五年,提醒我们:最危险的漏洞往往不是最新的,而是最古老的——那些在复杂代码路径中沉睡多年、被无数人 review 却始终未被发现的缺陷。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/31 6:32:44

Mac窗口置顶终极指南:Topit让你的工作效率提升300%

Mac窗口置顶终极指南&#xff1a;Topit让你的工作效率提升300% 【免费下载链接】Topit Pin any window to the top of your screen / 在Mac上将你的任何窗口强制置顶 项目地址: https://gitcode.com/gh_mirrors/to/Topit 还在为Mac上频繁切换窗口而烦恼吗&#xff1f;To…

作者头像 李华
网站建设 2026/7/31 6:32:42

程序开发中的路径选择:绝对路径与相对路径的工程实践指南

1. 路径选择&#xff1a;一个看似简单却贯穿开发始终的“小”问题如果你写过代码&#xff0c;就一定和路径打过交道。无论是读取一个配置文件、加载一张图片&#xff0c;还是导入一个模块&#xff0c;你都得告诉程序&#xff1a;“东西在哪&#xff1f;” 这个问题&#xff0c;…

作者头像 李华
网站建设 2026/7/31 6:26:31

TCP四次挥手详解:从原理到实践,解决CLOSE_WAIT与TIME_WAIT问题

1. TCP四次挥手&#xff1a;告别不是结束&#xff0c;而是资源释放的艺术在网络世界里&#xff0c;每一次可靠的通信都始于一次热情的握手&#xff0c;终于一次体面的告别。这个“告别”的仪式&#xff0c;就是TCP协议中的“四次挥手”。对于任何与网络打交道的开发者、运维工程…

作者头像 李华
网站建设 2026/7/31 6:23:34

6.并发编程

Python并发编程的三种方式&#xff1a;多线程thread【threading模块】、多进程process【multiprocessing模块】、多协程coroutine【asyncio模块】。一个进程可以有多个线程&#xff0c;一个线程可以有多个协程。其中线程、协程适用IO密集型场景&#xff0c;进程适用多CPU计算场…

作者头像 李华
网站建设 2026/7/31 6:19:28

关键路径算法详解:从AOE网到时间余量,轻松掌握项目管理核心

1. 从“盖房子”到“关键路径”&#xff1a;一个项目经理的日常困境如果你做过项目&#xff0c;哪怕只是组织一次家庭聚餐&#xff0c;你肯定遇到过这种抓狂时刻&#xff1a;明明每个环节都有人在推进&#xff0c;但总感觉进度卡在某个地方&#xff0c;整个项目像被按了暂停键。…

作者头像 李华
网站建设 2026/7/31 6:18:16

Footprint Tool 3基础操作指南:从数据导入到结果分析的完整流程

1. 先搞清楚 Footprint Tool 3 到底解决什么问题 Footprint Tool 3 这类工具&#xff0c;从名字就能看出是用于计算、分析或管理某种“足迹”的专业软件。在实际项目中&#xff0c;足迹分析可能涉及碳足迹、环境足迹、资源消耗足迹、甚至项目进度足迹等多个维度。这个 UNIT 2 的…

作者头像 李华