首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 归档 标签 关于

GhostLock,一种堆栈 UAF,已在所有 Linux 发行版中存在 15 年

2026-07-11 1 阅读 约9分钟阅读 djfergus
分享:
字号:
需要一些不太技术性的东西吗?快速浏览一下我们的错误摘要。阅读 bug 摘要 → GhostLock (CVE-2026-43499) 是 VEGA 发现的一个 Linux 内核漏洞,自 2011 年以来每个主要发行版中都存在该漏洞。触发该 bug 不需要任何特殊的内核配置或权限。通过将其转化为 97% 稳定的权限提升和容器逃逸,Google 奖励了 92,337 美元的 kernelCTF。这篇文章涵盖了该漏洞的技术细节。漏洞摘要 GhostLock (CVE-2026-43499) 允许非特权本地攻击者: 仅通过常规线程系统调用即可获取指向内核堆栈内存的悬空内核指针。写入一个指向几乎任意地址的指针。劫持函数表以劫持控制流并最终获得 root 访问权限。 GhostLock 在 Linux 2.6.39 中引入,并在 Linux 7.1 中修复。它在 Linux 内核中已经存在超过 15 年了。每个没有补丁的 Linux 发行版都会受到影响,应该考虑升级到最新的 LTS 版本。您的浏览器不支持视频标签。漏洞分析 我想直接看到利用策略 概述 GhostLock 是在 8161239a8bcc 中随 rtmutex 返工引入的(“rtmutex:简化 PI 算法并使最高优先级任务获得锁定”),并且大约十五年没有受到影响,直到 3bfdc63936dd 中的 2026 年 4 月修复(“rtmutex:使用 waiter::task 而不是当前的删除_waiter()”)。受影响的范围是 v2.6.39-rc1 到 v7.1-rc1 , CONFIG_FUTEX_PI=y 是唯一的要求,不需要任何功能或用户命名空间。 kernel/locking/rtmutex.c 中的 remove_waiter() 清除 current->pi_blocked_on 。这在正常的慢速路径上是正确的,其中当前是拥有服务员的任务。代理路径错误。 rt_mutex_start_proxy_lock() 入队,并在错误时回滚,代表另一个任务的 rt_mutex_waiter,因此当前是重新排队者而不是等待者。等待者对象位于在 FUTEX_WAIT_REQUEUE_PI 中休眠的任务的堆栈上。然后,FUTEX_CMP_REQUEUE_PI 将该服务员代理到目标 PI futex。当 rtmutex 链遍历报告死锁时,回滚会使等待者从锁中出列,但会清除重新排队器上的 pi_blocked_on 。等待任务使 pi_blocked_on 保持指向其自己的堆栈帧,该堆栈帧在等待程序返回用户空间时弹出。任何后续 PI 链遍历该任务都遵循悬空指针。根本原因 这与许多其他生命周期错误具有相同的形式:被从未为其编写的调用者重用的函数。辅助函数remove_waiter()最初是为一种场景而编写的:线程自行阻塞,然后自行清理。所以它一直假设current(无论哪个线程正在运行)是它需要清理的服务员,并相应地清除current->pi_blocked_on。然而,Requeue-PI 打破了这个假设。通过 rt_mutex_start_proxy_lock() ,该帮助器现在用于代表不同的休眠线程进行清理。在该路径中, current 是发出 FUTEX_CMP_REQUEUE_PI 的线程,而不是实际的 waiter 。当 __rt_mutex_start_proxy_lock() 返回 -EDEADLK 时,它会通过误用的帮助程序remove_waiter() 进行回滚。 1 int __sched rt_mutex_start_proxy_lock ( struct rt_mutex_base * lock , 2 struct rt_mutex_waiter * waiter , 3 struct task_struct * task ) 4 { 5 int ret; 6 raw_spin_lock_irq(&lock->wait_lock); 7 ret = __rt_mutex_start_proxy_lock(锁、服务员、任务); 8 if(不太可能(ret)) 9remove_waiter(lock, waiter); // ret == -EDEADLK 10 raw_spin_unlock_irq (&lock->wait_lock); 11返回ret; 12 } remove_waiter() 然后清理错误的任务。 1 static void __sched remove_waiter ( struct rt_mutex_base * lock , 2 struct rt_mutex_waiter * waiter ) 3 { 4 ... 5 raw_spin_lock ( & current->pi_lock); 6 rt_mutex_dequeue(锁,等待者); 7 当前->pi_blocked_on = NULL ; // 应该是 waiter->task 8 raw_spin_unlock (&​​current->pi_lock); 9 ... 10 } waiter 是存在于睡眠线程自己的堆栈上的对象,而这里的 current 是请求重新排队的线程。该修复会锁定 waiter->task->pi_lock 并清除 waiter->task->pi_blocked_on。这个问题忽略了 lockdep,它只检查 pi_lock 是否被持有,而不检查是谁的。触发 -EDEADLK 路径。达到 -EDEADLK 回滚需要一个由三个 futex 字和三个线程构建的 PI 依赖循环。 f_pi_chain ,一个 PI futex,首先由等待线程锁定。 f_pi_target ,一个 PI futex,首先由所有者线程锁定。这是重新排队目标。 f_wait ,服务员用 FUTEX_WAIT_REQUEUE_PI 阻塞的普通 futex 。顺序是:等待者获取 f_pi_chain ,然后阻塞在 FUTEX_WAIT_REQUEUE_PI(f_wait -> f_pi_target) 中。它的 rt_mutex_waiter 现在位于其堆栈上。所有者获取 f_pi_target ,然后阻塞在服务员持有的 f_pi_chain 上。主线程调用 FUTEX_CMP_REQUEUE_PI(f_wait -> f_pi_target) 。重新排队尝试将服务员代理到 f_pi_target 上。 f_pi_terge 的所有者
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱