Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-64468 — 非特权概念验证及针对 Linux 内核 Binder 释放后使用漏洞(CVE-2026-64468)的 x86_64 本地权限提升,附带 KASAN 实验环境及易受攻击/已修复版本的差异对比。 | Kitploit
工具/GitHubGitHub/aramosf/cve-2026-64468
权限提升漏洞利用框架漏洞分析漏洞利用学习与教育二进制利用
GitHubaramosf/cve-2026-64468

CVE-2026-64468

非特权概念验证及针对 Linux 内核 Binder 释放后使用漏洞(CVE-2026-64468)的 x86_64 本地权限提升,附带 KASAN 实验环境及易受攻击/已修复版本的差异对比。

查看仓库
115天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2026-64468 — Linux 内核 Binder binder_free_transaction() 进程生命周期释放后使用

实时 QEMU/KVM 运行:未打补丁的易受攻击内核报告释放后使用,已修复内核则不会

本仓库包含针对 Linux 内核 Binder 释放后使用漏洞的内容,该漏洞已由上游提交 f223d27a546c1e1f48d38fd67760e78f068fe8c4 修复:

  • binder_chain_64468.c — 一个无特权的概念验证程序,可触发该漏洞并让内核自行证明,附带 KASAN 实验室和易受攻击/已修复的差分测试(lab/、run.sh、verify.sh)。
  • exploit.c — 一个针对 x86_64 的自包含本地权限提升程序。使用 gcc -O2 -pthread -o exploit exploit.c 编译,以普通用户身份运行,最终获得 root shell。
  • demo/ — 一个实验室,在未打补丁的内核上启动真实的 Debian 13 用户空间,因此该漏洞利用程序可由目标自身的 gcc 编译,并在其随后接管的那台机器上运行。

所有内容均以普通用户身份(uid/gid 1000,无能力,无命名空间)针对未打任何补丁的纯上游内核运行。

警告

此代码有意竞态内核对象生命周期,然后劫持内核控制流。竞态失败会破坏内核堆状态,并可能导致机器崩溃或挂起。请仅在您拥有的隔离、一次性虚拟机中运行。切勿在宿主机上运行。

状态与范围

漏洞

binder_free_transaction() 在 t->lock 下从事务中读取目标进程,释放该锁,然后获取目标进程的内部锁:```c spin_lock(&t->lock); target_proc = t->to_proc; spin_unlock(&t->lock);

root@kitploit:~
if (target_proc) {
	binder_inner_proc_lock(target_proc);   /* use after free */
root@kitploit:~
没有任何东西能让 `target_proc` 在该间隙中保持存活。一个正在被并行销毁的进程可能在此期间到达 `binder_proc_dec_tmpref() -> kfree()`,因此锁会在已释放的内存上被获取。上游修复方案在仍持有 `t->lock` 的同时固定 `t->to_thread`,这会使所属进程保持存活,直到内部锁被使用并释放。

该漏洞由 **Alice Ryhl** 报告,并由 **Carlos Llamas** 修复,两人均来自 Google。[原始报告](https://lore.kernel.org/all/[email protected]/) 附带了相关的 KASAN 跟踪信息。

### 到达易受攻击的访问点

唯一能够到达*外部* `to_proc` 的调用者是 `binder_send_failed_reply()`,并且它仅在 `t->from` 为 `NULL` 时才遍历到 `t->from_parent`:```c
	target_thread = binder_get_txn_from_and_acq_inner(t);
	if (target_thread) { ...; binder_free_transaction(t); return; }
	next = t->from_parent;
	binder_free_transaction(t);
	t = next;

from_parent 恰好在一处被赋值,而该赋值在几行之前受到 binder 的坏事务栈检查的保护:一个线程只有在栈顶是它正在接收的事务时,才能发送同步事务。因此,对于该链的每一环,子进程的发送方和父进程的接收方都是同一个线程。

这带来一个尖锐的后果。binder_thread_release() 在持有 proc->inner_lock 的情况下遍历正在消亡线程的栈,并写入以下两者:``` iteration j : [holds child->lock ] child->from = NULL ; unlock child->lock spin_lock(parent->lock) iteration j+1 : [holds parent->lock] parent->to_proc = NULL

root@kitploit:~
in **同一遍历的相邻迭代中**,每次迭代都持有各自的
`t->lock`。遍历者只有在释放了 `child->lock` 之后,才能得知 `child->from == NULL`,并且需要 `parent->lock` 来获取自身的快照。因此,整个机会窗口就位于该次遍历的 `spin_unlock(&child->lock)` 与
`spin_lock(&parent->lock)` 之间——仅相隔几条指令。释放遍历持有自旋锁,在该处无法被抢占;只有中断才能延迟它。

这正是该竞态条件极为狭窄的原因,也是概念验证和利用程序都依赖概率的原因。

### 概念验证构建了什么

`binder_chain_64468.c` 构建了能够触达易受攻击访问的最短链,从而使遍历者在该窗口内的工作量尽可能小——仅两次 `t->lock` 获取和一次 `kfree()`,不涉及外部 `inner_proc_lock`、无 `wake_up`,也无回复投递:```
  B thread i   --e2 (sync, code 0x4442414b)-->  P thread Y_i
  P thread Y_i --e1 (sync, code 0x54414c4c)-->  B thread i   (nested target)

  B thread i   stack: [ e2 outgoing , e1 incoming (top) ]
  P thread Y_i stack: [ e2 incoming , e1 outgoing (top) ]

P 随后关闭其 binder fd,因此 binder_deferred_release() 会释放 Y_1..Y_K 并最终释放 binder_proc,而每个 B 线程 同时发出 BINDER_THREAD_EXIT:``` binder_thread_release(B_i) nulls e1->to_proc (own proc) and e2->from binder_send_failed_reply(e1) e1->from == NULL once Y_i was released -> binder_free_transaction(e1) target_proc already NULL, kfree(e1) -> binder_free_transaction(e2) target_proc == P <-- vulnerable access

root@kitploit:~
第三个进程是活页夹上下文管理器,仅用于分发另外两个进程所需的句柄。`exploit.c` 完全复用了这一构造。

### 两个独立条件

一份 KASAN 报告需要同时满足以下两点:

* **易受攻击的访问** —— 遍历者必须在上方所述的狭窄窗口内获取 `parent->lock`,从而快照到一个仍然存活的 `to_proc`;以及
* **窗口内的释放落地** —— 遍历者随后必须在释放 `t->lock` 与获取受害者内部锁之间失去其 CPU,并保持离线状态,直到延迟释放完成并释放 `binder_proc`。

第一个条件有其自身的判定依据,无需依赖 KASAN:活页夹会打印```
binder: binder_free_proc: Unexpected outstanding_txns -1

每当这种情况发生时,因为遍历器和 binder_thread_release() 随后都会对同一事务的同一个计数器进行递减。请注意,这本身并非漏洞/修复之间的差异——修复阻止的是释放,而不是第二次递减——因此它在两个内核上都会出现。这里使用它只是为了展示易受攻击的代码路径被执行。

从释放后使用到 root

概念验证止步于对已释放内存的 4 字节递减。要将其转化为 uid 0 需要四样东西,而它们都不来自漏洞本身:该漏洞不会泄露任何信息。

1. 共享缓存

struct binder_proc 为 648 字节,通过普通的 GFP_KERNEL kzalloc 分配——而非 __GFP_ACCOUNT。因此它会落入 kmalloc-1k,与该大小的所有其他未记账分配放在一起,并且不会被隔离在 kmalloc-cg-* 之后。仅此一点就使得该对象可以被回收。

2. 跟得上的回收泵

kfree() 发生的时刻无法从用户空间观察到,遍历器再次触碰该对象的时刻同样无法观察到,因此没有任何可供计时对齐的时机。因此喷射以泵的形式运行:它持续分配并释放 kmalloc-1k 对象,在运行延迟释放的那个 CPU 上执行,只要遍历器在运行就持续进行。

为此使用 System V 消息。alloc_msg() 是一个普通的未记账 kmalloc,包含 48 字节头部加负载,因此一条 976 字节的消息就是一次 1024 字节的分配;配额是按队列而非按 uid 计算的;并且 msgrcv() 会同步释放。实测吞吐量:每秒约 198,000 次分配,零失败。

首先尝试了 add_key/user_key_payload,但它是个陷阱。其负载受按 uid 的字节配额(kernel.keys.maxbytes,默认 20000)限制,该配额只有在密钥垃圾收集器销毁密钥时才会释放,因此紧凑的分配/释放循环会在毫秒内耗尽它:实测 27,151 次成功分配,对应 2,121,009 次失败——98.7% 的喷射在静默地做无用功,看起来就像一场永远赢不到槽位的喷射。KEYCTL_INVALIDATE 使情况更糟(4,775 次成功),因为它会排队等待 GC 工作。

3. 被回收对象必须包含的内容

遍历器会触碰已释放 binder_proc 的四个字段(偏移量通过 pahole 在目标构建上测得):

当 outstanding_txns == 1 且 is_frozen == 1 时,递减达到零,遍历器调用 wake_up_interruptible_all(&proc->freeze_wait)。随后 __wake_up_common 计算 curr = head.next - 24 并调用 *(head.next - 8):一个从 head.next 所指向位置读取的函数指针,而该值由被回收的对象提供。

4. 来自侧信道的两个地址

该指针必须到达攻击者控制的内存,且位于内核地址空间,而该漏洞不泄露任何信息。这两个地址都来自预取计时——与 KASLD(Brendan Coles,MIT)相同的信道,本实现即源自该项目:

  • 内核文本。 对已映射内核地址的预取会在页表遍历中解析并退出,其速度可测量地快于对未映射地址的预取,即使该访问在架构上永远不会可见。扫描文本范围的 2 MiB 槽位会显示镜像为一段快速槽位;其第一个槽位即 _text。

  • 直接映射。 启用 CONFIG_RANDOMIZE_MEMORY 后,直接映射以 1 GiB 为单位随机化,因此也必须定位它。与文本不同,它覆盖全部 RAM,因此它是映射槽位中最长的连续段。需要两项改进才能使其可用:

    • 在 KVM 下,单次预取区分映射与未映射仅相差约 4 个周期,这无法在噪声中存活,因此每次采样对一批 400 次预取计时(实测 311 对 523 周期——可区分);
    • 在争用激烈的主机上,单次扫描不可靠——单客户机时 10/10 正确,五台客户机同时运行时为 3/5——因此扫描运行五次,并要求多数一致。若无共识,漏洞利用会报告失败并停止,而不是对着未验证的地址开火。

    运行可靠起始的位置并非 page_offset_base 本身,而是内核可能以 1 GiB 页映射的第一个槽位——即覆盖物理 4 GiB 的那个。在其之下,PCI 空洞和固件保留区强制使用 2 MiB 页,其更长的遍历在此处无法与未映射区分。该槽位正是喷射所需的,因此使用它。

随后约 60% 的物理内存被填充为同一精心构造的 4 KiB 页的副本,因此无论布局最终如何,从该锚点起的固定偏移都由该构造页支撑。

攻击链```

freeze_wait.head.next -> entry1 (in the sprayed page)

entry1.func = mov 0x28(%rdi),%rdi ; mov 0x18(%rdi),%rax ; jmp *0x58(%rax) RDI <- entry1+0x28 = &cred RAX <- cred+0x18 jmp *(RAX+0x58) = commit_creds -> commit_creds(cred)

entry2.func = mov $-1,%rax ; ret __wake_up_common breaks out on ret < 0

root@kitploit:~
没有栈迁移,也没有 `iretq`:被劫持的线程正常地从它的 `ioctl()` 返回,然后只是以 root 身份回到用户空间。

### 伪造的 cred,以及那个本会浪费它的 bug

分发器 gadget 从 `cred+0x18` 获取它的 `RAX`,而 `cred+0x18` 是
`euid`/`egid`,所以链执行后 `euid` 立即变成内核指针的低半部分。
这只是外观问题。真正的问题在于 `prepare_creds()` ——
**每个后续的 `fork()` 和 `execve()` 都会调用它** —— 会解引用三个字段,且没有 NULL 检查:```c
	get_group_info(new->group_info);        /* refcount_inc(&gi->usage) */
	get_uid(new->user);                     /* refcount_inc(&u->__count) */
	new->ucounts = get_ucounts(new->ucounts);

伪造的凭据若将其置为 NULL,会得到 uid 0,然后在第一次 execve 时使机器崩溃——也就是说,该漏洞利用会报告成功,却立刻摧毁目标系统。因此,利用链将 user、ucounts 和 group_info 指向真实的内核全局变量 root_user、init_ucounts 和 init_groups,这些变量的地址来自同一个 _text 基址。设置好这些之后,提权线程在 init_user_ns 中持有 CAP_SETUID,于是它调用 setresuid(0,0,0),内核便会用一份干净、由内核分配的 root 凭据覆盖伪造的凭据。只有在这之后,才会执行其他任何操作。

权限通过一份 setuid-root 的漏洞利用二进制副本传递给父进程,该副本在 exec 启动 shell 之前会自行删除,因此 root shell 在主进程中于干净的终端上运行,且不会留下任何 setuid 残留。

受影响系统

仅凭 uname -r 并不足够。厂商内核通常会移植 binder 修复,而不会将版本号改为对应的主线版本;请检查源码或软件包变更日志。

该修复带有 Cc: stable 标签,因此 stable 分支和厂商分支都会收到移植补丁。

漏洞位于何处,以及何处可触达

这是两个不同的问题,而第二个问题才是决定影响范围的关键。

只要设置了 CONFIG_ANDROID_BINDER_IPC,易受攻击的代码就会被编译进去。这包括通用发行版——但在所有被调查的发行版中,该驱动都是默认不加载的模块,即使加载了,init_binder_device() 在注册 misc 设备时也未设置 miscdev.mode,因此 devtmpfs 会以 0600 root:root 权限创建 /dev/binder。在 Android 上,是 ueventd 将其权限改为 0666,这正是该漏洞在 Android 上至关重要、而在桌面系统上大多无关紧要的原因。

以下配置读取自各发行版自带的已发布内核软件包:

因此,桌面发行版上的实际暴露面是间接的:任何加载 binder 并将其开放给用户的组件——Waydroid、Anbox、Android 模拟器或容器运行时——都会在仍存在该漏洞的内核上重新引入与 Android 完全相同的可触达性。

各项加固选项对漏洞利用的代价

这些选项不影响漏洞本身;它们影响的是这个漏洞利用。

已验证目标

两个内核均为标准上游树。未打任何补丁。

第一个实验室中的 CONFIG_KASAN_GENERIC 是检测器,而非使能器:没有它,竞态条件完全相同。CONFIG_PREEMPT 是真正的先决条件,也是 Android 所启用的配置。

架构对漏洞而言并非影响因素——它是架构无关 C 代码中的生命周期错误。但对漏洞利用而言,架构影响极大:gadget、预取信道和直接映射布局都是 x86_64 特有的。

构建与运行

要求:clang、lld、make、cpio、gzip、qemu-system-x86_64、docker(仅用于组装 Debian rootfs)、Linux git 树的本地克隆,以及 gcc。

在已存在漏洞的系统上运行漏洞利用```sh

gcc -O2 -pthread -o exploit exploit.c ./exploit

root@kitploit:~
对于 `demo/` 中以外的内核,请先提取其偏移量:```sh
./mkoffsets.sh /path/to/vmlinux > offsets.h
gcc -O2 -pthread -DEXPLOIT_OFFSETS='"offsets.h"' -o exploit exploit.c

可调参数,全部为可选,均从环境变量中读取: CVE64468_SECONDS、CVE64468_THREADS、CVE64468_SPRAY_PERCENT、 CVE64468_CALL_OFFSET_MB、CVE64468_KASLR_ATTEMPTS、CVE64468_DELAY_MAX_US、 CVE64468_DELAY_STEP_US、CVE64468_STAGGER_US、CVE64468_VERBOSE、 CVE64468_SHELL。

内存安全实验室```sh

LINUX_GIT=/path/to/linux ./lab/build.sh # x86_64 LINUX_GIT=/path/to/linux TARGET_ARCH=arm64 ./lab/build.sh # arm64 ./run.sh vulnerable ./verify.sh 600 16

root@kitploit:~
该脚本会在上述提交处创建两个分离的工作树,如果任一工作树存在未提交的更改则拒绝运行,验证存在漏洞的树缺少修复而修复后的树包含该修复,构建两个内核,并将概念验证打包进initramfs。

### 漏洞利用实验室```sh
./demo/build-kernel.sh     # vulnerable kernel, KASAN off, hardening on
./demo/build-rootfs.sh     # Debian 13 userland + gcc, as an initramfs
./demo/run-demo.sh         # boot it; this is what the recording shows
./demo/verify.sh logs/     # unattended reliability run, one guest per trial

demo/run-demo.sh 启动客户机并将控制台交给 uid 1000,该用户使用客户机自带的 gcc 编译 exploit.c 并运行它。

内核镜像、工作树、根文件系统树和 initramfs 均为实验室产物,不纳入版本管理。

实际运行

内存安全:存在漏洞版本与修复版本对比

docs/example-output.txt 是真实存在漏洞内核的完整输出记录,包含 KASAN 报告;docs/patched-negative-output.txt 是修复后内核的对照组;docs/e2e-results.json 是机器可读的结果。

报告中的调用链与上游报告完全一致:``` BUG: KASAN: slab-use-after-free in queued_spin_lock_slowpath+0x62/0x6a0 Read of size 4 at addr ffff888100282270 by task cve64468-B/91 CPU: 6 UID: 1000 PID: 91 Comm: cve64468-B Not tainted 7.2.0-rc1+ #2 PREEMPT _raw_spin_lock+0x55/0x60 binder_free_transaction+0x80/0x1f0 binder_send_failed_reply+0x98/0x340 binder_thread_release+0x528/0x5e0 binder_ioctl+0x1ca/0xeb0 __x64_sys_ioctl+0x8c9/0xcf0

Allocated by task 93: binder_open+0xb5/0x7b0

Freed by task 9: kfree+0x113/0x310 binder_deferred_func+0x104b/0x1180 process_scheduled_works+0x69f/0xbb0

root@kitploit:~
`UID: 1000` 是概念验证自身无特权身份:受害者
`binder_proc` 由 `binder_open()` 分配,由 binder 延迟
工作队列释放,并在释放后被遍历器读取。

记录运行,2026-08-16,`./verify.sh 1200 16 3` — 每个变体三个并发 QEMU/KVM
客户机,各 10 个 vCPU,16 线程,每个变体 1200 秒,**两侧均无内核补丁**:

| | 易受攻击的 `114a116aaa5f` | 已修复的 `f223d27a546c` |
| --- | --- | --- |
| 尝试次数 | 158,384 | 158,471 |
| 遍历次数 | 2,534,144 | 2,535,536 |
| 设置失败 | 0 | 0 |
| 易受攻击的访问(`Unexpected outstanding_txns -1`) | 1,127 | 934 |
| **KASAN `slab-use-after-free`** | **8** | **0** |

这就是差异所在:相同的工作负载,尝试次数相差在
0.06% 以内,而释放后使用仅出现在未打补丁的易受攻击内核上。

易受攻击的访问出现在*两个*内核上,这是预期的 — 该修复
防止进程在窗口内被释放,而不是防止 `outstanding_txns` 的第二次
递减。这就是为什么该行仅用作廉价
预言机,而从不作为差异依据。

### 权限提升

![实时 QEMU/KVM 运行:Debian 13 客户机,无特权用户使用客户机自带的 gcc 编译 exploit.c,最终到达 root 提示符](https://assets.kitploit.com/production/public/readmes/54229/da1e333d7516671ffab64e10d89604ee2a8043de1c48306abc03aaeee88d0d62/205a1253ab216ae4b62a65f761fffb9f0329bcbf9e4feb38563647f68ad2c5e0-display-v1.webp)

`docs/lpe-output.txt` 是来自演示客户机的真实记录,而
`docs/lpe-demo.cast` 是上述动画渲染所依据的完整、未经编辑的 Asciinema 录制(`asciinema play docs/lpe-demo.cast` 可完整重放)。动画省略了该录制中漫长的竞争中间部分 — 客户机的串行控制台在整个约 24 分钟的竞争期间持续输出 binder 调试信息,若完整渲染将产生数十兆字节的滚动日志 — 保留了 Debian 前言和 root shell;exploit 自身的 `hit after 26661 attempts ... in 1437s` 行精确说明了被省略的内容。两者均为单次运行:未能在预算内获胜的客户机会关机,录制只是简单重复,而非编辑成一次胜利。

有关实测成功率,请参阅下方*可靠性*部分。

## 可靠性

该竞争在两方面都是概率性的,因此失败运行有时是预期行为,而非 exploit 故障。

### 内存安全

基于上述运行,在易受攻击内核上得出的比率:

| 数量 | 值 |
| --- | --- |
| 尝试速率 | 每个客户机约 44 次尝试/秒,三个合计约 132 次/秒 |
| 易受攻击的访问 | 每次尝试 7.1e-3 |
| 给定该访问后,释放落入窗口的概率 | 7.1e-3 |
| KASAN 报告 | 约每 20,000 次尝试 1 次,即按此速率大约每 2.5 分钟一次 |

### 权限提升

每个客户机都是一次独立试验:其自身的 KASLR、其自身的直接映射
随机化,而获胜的客户机会停止竞争。因此该数字是
**每次启动的成功率**,而非每次尝试的成功率。

记录活动,2026-08-16 23:32 UTC,`GUESTS=6 CPUS=8 MEMORY_MB=9216 ./demo/verify.sh`
— 六个并发 QEMU/KVM 客户机,未打补丁的 `114a116aaa5f`,Debian 13 用户空间,
每个预算 60 分钟,exploit 在客户机内编译,以 uid 1000 启动:

| | |
| --- | --- |
| 达到 uid 0 的客户机 | **6 个中的 2 个** |
| 到达 root 的时间 | 623 秒和 1,344 秒 |
| 获胜竞争中的尝试次数 | 13,839 和 31,125 |
| 每个未获胜客户机的尝试次数 | 整个 3,600 秒内约 95,000 次 |
| 整个活动中的总尝试次数 | 427,676(6.8 百万次栈遍历) |
| 观察到的易受攻击的访问(`Unexpected outstanding_txns -1`) | 256 |
| **内核崩溃、oops 或 panic** | **0**,在 9 个客户机小时内 |

该表中有两点值得解读。

**基本上每次获胜的竞争都变成了 root。** KASAN 实验室测得
给定易受攻击的访问后,释放落入窗口的概率为
7.1e-3。将其应用于此处观察到的 256 次访问,可预测整个活动中约有
~1.8 次释放后使用 — 而实际获得了 2 次 root。回收、
地址发现和链并非瓶颈;竞争才是。

**没有任何崩溃。** 在九个客户机小时内,没有客户机出现 oops,包括
四个从未获胜的客户机。要么链针对正确定位、喷洒的
页面触发,要么根本不触发 — 这正是阶段 2 中多数投票拒绝
的用途。

该拒绝在实践中确实会触发。在已负载的主机上同时启动六个客户机,
产生了一个在阶段 2 放弃的客户机,并显示```
[*] direct map not found; refusing to fire at an unverified address

并退出竞态。这正是预期行为:当时间通道无法达成共识时,浪费一次启动是正确的结局,远比在从未确认过的地址上触发链式利用要好得多。

docs/lpe-results.json 携带机器可读形式,包括所用确切内核和 initramfs 的 SHA-256。

为什么需要多个客户机,以及为什么需要超订宿主机

并发运行客户机不仅仅是为了并行性。在争用激烈的宿主机上,KVM 会取消调度客户机 vCPU,而这正是第二个条件所需的延迟:walker 必须在释放 t->lock 与获取受害者内部锁之间失去其 CPU。在空闲宿主机上的单个客户机,其易受攻击访问率经测量约为三个并发客户机的二十分之一。

不过存在上限。在 32 线程宿主机上运行 8 个各含 8 个 vCPU 的客户机,每个客户机喷洒约 5 GiB 的直接映射内存时,宿主机进入交换状态,8 个客户机中有 3 个完全没有任何进展。6 个是随附的默认值。

可选的客户机内抢占辅助线程经测量会使尝试率增加约三倍,但并未提高命中率,因此未使用。

有一个环境因素被证明比预期更重要:binder 自身的调试输出。当 binder.debug_mask 处于默认值时,驱动在竞态期间会发出大量限速的 pr_info 流量,而由此产生的 printk 和 console-lock 压力会延长第二个条件所需的精确抢占窗口。使用 binder.debug_mask=0 将其静音以获得更整洁的控制台——这是录制时显而易见该做的事——在测试中可测量地降低了命中率:客户机运行远超两个获胜者的尝试次数却未命中。因此 demo/run-demo.sh 将 binder 调试保留在默认值,而安静的控制台是显式选择加入的。这是实验室的属性,而非利用本身的属性——但它很好地说明了此竞态在多大程度上依赖于系统范围的时序抖动,而非利用本身能控制的任何东西。

安全性

  • 两个客户机都是可丢弃的 initramfs 镜像。不会向客户机或宿主机磁盘写入任何内容。
  • 概念验证仅打开 /dev/binder、发送 binder 事务并退出 binder 线程。它不安装任何内容,也不留下任何痕迹。
  • 该利用确实会写入一个文件:自身的 setuid-root 副本,用于将特权从获胜线程移交给父进程。它在 exec shell 之前取消链接该副本,因此运行后不会有任何 setuid 内容存留。
  • 竞态失败可能导致客户机 panic 或挂起;内存安全实验室以 panic=1 oops=panic 启动,因此运行会终止而非在损坏状态下继续。务必始终从干净启动重新开始。

致谢

  • 利用作者:A. Ramos <[email protected]>(Twitter:@aramosf)
  • 漏洞发现与报告:Alice Ryhl,Google
  • 上游修复:Carlos Llamas,Google
  • Prefetch KASLR 侧信道:衍生自 KASLD,版权所有 (c) 2019 Brendan Coles,MIT 许可。直接映射变体为本项目新工作。

公开利用搜索

于 2026-08-16 使用 SearchSploit(本地 Exploit-DB 副本)及针对 CVE-2026-64468、binder_free_transaction 和 f223d27a546c 的网络搜索进行了检查。未找到针对此 CVE 的公开利用或概念验证;SearchSploit 仅返回无关的、较旧的 Android binder 条目。这是时间点检查,而非永久保证。

下载工具
状态声明
已确认该漏洞真实存在,可从无特权进程触达,且上游修复已将其消除。
已演示对濒临消亡的 binder_proc 的易受攻击解引用在未打补丁的内核上自然且反复发生,而在已修复内核上从未发生。
已演示完整的释放后使用,由 KASAN 在未打补丁的内核上报告。
已演示使用攻击者控制的字节回收已释放的 binder_proc、内核控制流劫持,以及在 x86_64 上从无特权用户提权至 uid 0。
未声明针对特定厂商或 Android 设备的任何结果。仅测试了下列上游内核,均在 x86_64 上。
未声明随附的漏洞利用程序无需修改即可针对发行版内核运行。请参阅受影响系统:它需要针对每个内核的偏移量,而且在所调查的每个通用发行版上,binder 设备首先就无法被无特权用户触达。
偏移量字段遍历器执行的操作
108int outstanding_txns对其递减
113bool is_frozen读取它
120wait_queue_head_t freeze_wait若 outstanding_txns == 0 && is_frozen 则遍历它
624spinlock_t inner_lock获取并释放它
状态提交备注
引入谱系a370003cc301由上游 Fixes: 标签命名
已验证存在漏洞114a116aaa5f修复的直接父提交;携带相邻的 CVE-2026-64469 修复,因此该组合可单独隔离 CVE-2026-64468
已修正的主线版本f223d27a546c正在测试的修复
发行版内核ANDROID_BINDER_IPC设备SLAB_BUCKETSRANDOM_KMALLOC_CACHES非特权用户可触达?
Debian 13 (trixie)6.12.101mANDROID_BINDER_DEVICES="binder",BINDERFS 关闭y关闭否——模块未加载;/dev/binder 权限为 0600
Debian 12 (bookworm)6.1.0mANDROID_BINDER_DEVICES="binder",BINDERFS 关闭不适用(6.11 之前)不适用(6.6 之前)否——同上
Ubuntu 24.04 LTS6.8.0mBINDERFS=m,ANDROID_BINDER_DEVICES=""不适用(6.11 之前)y否——需要 root 权限执行 mount -t binder
Ubuntu 22.04 LTS5.15.0mBINDERFS=m,ANDROID_BINDER_DEVICES=""不适用不适用否——同上
Android(AOSP / 厂商)6.1、6.6、6.12 GKIy/dev/binder、/dev/hwbinder、/dev/vndbinder——是——该驱动是平台 IPC,且对全局可访问
选项此处的影响
CONFIG_SLAB_BUCKETS(6.11+)对该内存回收利用是致命的。 它将 msg_msg 隔离到独立的 kmalloc 桶中,因此泵送分配永远无法落入 binder_proc 的槽位。Debian 13 启用了它。需要另找一种未被计入的 1 KiB 分配。内核 6.6(即相关 Android 设备所运行的版本)完全早于该选项。
CONFIG_RANDOM_KMALLOC_CACHES(6.6+)按调用点将 kmalloc-1k 拆分为多个缓存,因此泵送分配必须命中同一个缓存;这对内存回收利用是一笔 1/16 的额外开销,而非一堵墙。Ubuntu 启用了它,Debian 未启用。
页表隔离(未使用 nopti)对地址发现是致命的。 在 PTI 激活时,预取无法看到内核文本,漏洞利用会检测到这一点并停止。上述每个发行版都编译了 PTI,但由 CPU 决定其是否激活:在不受 Meltdown 影响的硬件上它是关闭的,而这些结果正是在此类硬件上测得的。
CONFIG_SLAB_FREELIST_RANDOM、..._HARDENED在实验室中已启用,与发行版发布时一致。无可测量的影响:泵送分配并不预测空闲链表顺序,它只是分配大量对象。
KASLR(RANDOMIZE_BASE、RANDOMIZE_MEMORY)已启用。被预取阶段所击败;未使用 nokaslr。
每内核偏移量利用链需要 commit_creds、三个与凭据相关的全局变量以及两个 gadget,均以 _text 为基准的偏移量。mkoffsets.sh 从目标 vmlinux 中提取它们;缺少这些偏移量,漏洞利用就会在错误的地址上触发。这是任何内核漏洞利用的按构建属性,而非一种防御手段。
内存安全实验室(lab/)漏洞利用实验室(demo/)
内核版本7.2.0-rc1+7.2.0-rc1+
存在漏洞的提交114a116aaa5f0295376cdf12da743c5bce3b20ce相同
已修复的提交f223d27a546c1e1f48d38fd67760e78f068fe8c4—(漏洞利用仅在存在漏洞的内核上测量)
架构x86_64(KVM)和 arm64(TCG)x86_64(KVM)
编译器Ubuntu clang 21.1.8 / LLD 21.1.8相同
KASAN开启——它是检测器关闭——它会改变 slab 布局,并使任何内存回收利用失去代表性
Slab 加固—SLAB_FREELIST_RANDOM、SLAB_FREELIST_HARDENED 开启;SLAB_BUCKETS、RANDOM_KMALLOC_CACHES 关闭
KASLR—RANDOMIZE_BASE、RANDOMIZE_MEMORY 开启
用户空间最小 initramfsDebian GNU/Linux 13 (trixie),使用发行版自带的 gcc
起始身份uid 1000、gid 1000、无能力、无命名空间相同
启动命令行console=ttyS0 rdinit=/init panic=1 oops=panic kasan_multi_shot=1console=ttyS0 loglevel=4 rdinit=/init——无 nopti、无 nokaslr、无 mitigations=off