编程 Copy Fail 漏洞深度拆解:732字节脚本如何让任意Linux用户秒变root(CVE-2026-31431内核提权原理解析)

2026-08-01 15:26:23 +0800 CST views 6

从内核深处拆解 Copy Fail:CVE-2026-31431 为什么是 2026 年最值得吃透的漏洞

前言:一个 732 字节的漏洞如何撬动整个 Linux 生态

2026 年 4 月 29 日,韩国安全公司 Theori 披露了一个让整个 Linux 社区倒吸一口凉气的漏洞:CVE-2026-31431,代号 Copy Fail

一个 732 字节的 Python 脚本,可以让任意普通 Linux 用户在几秒内拿到 root shell。成功率不是 90%,不是 99%,是 100%。不需要竞态条件,不需要复杂的堆风水,不需要找到任何 ROP gadget,不需要绕过 KASLR/ASLR,不需要内核信息泄漏。

这个漏洞的特殊之处不在于"又能提权"——Linux 内核提权漏洞年年有。这个漏洞的特殊之处在于:它攻击的是内核加密子系统里一条看似无害的优化路径,根植于 2017 年的一行代码,潜伏了近 9 年才被发现。

本文不打算写成又一篇"升级内核吧"的通告。我想从系统程序员 / SRE / 安全工程师的视角,带你真正理解这个漏洞在底层是怎么工作的:内核的 authencesn 加密模板为什么要做 in-place 优化?splice() 的零拷贝语义为什么在这里成了攻击面?page cache 的共享特性如何被利用来篡改 setuid 程序?以及,作为工程师,我们怎么从代码层理解、检测和防御它。


一、背景:三个 Linux 核心子系统如何被串成攻击链

1.1 AF_ALG:内核加密能力的用户态入口

Linux 内核从 2.5.45 起引入了 Kernel Crypto APIinclude/crypto/),为整个内核提供统一的密码学原语。设计上分为三层:

应用层 / 用户态库(OpenSSL, GnuTLS, NSS)
    ↓  系统调用(socket/bind/send/recv)
AF_ALG Socket 接口(net/ipc/algif_aead.c)
    ↓  算法查找(crypto_alloc_xx)
通用加密算法接口(crypto_ahash, crypto_aead)
    ↓  平台相关指令(AES-NI, ARM CE, 软件实现)
具体算法实现(aes, sha256, gcm...)

AF_ALG 是内核向用户态暴露加密能力的 socket 接口(地址族编号 38)。用户态程序可以像使用普通 socket 一样,调用 bind/accept/send/recv 来访问内核加密服务。这套接口的设计初衷是让 TLS 库能把加解密 offload 给内核的硬件加速器,同时保持代码的可移植性。

import socket

# 创建 AF_ALG socket——就像创建一个 TCP socket 一样简单
s = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)

# bind 时指定算法,内核会在 crypto 子系统中查找对应实例
# authencesn 不是具体算法,是一个"算法构造器模板"
s.bind(('aead', 'authencesn(hmac(sha256),cbc(aes))'))

# 设置密钥
s.setockopt(socket.SOL_ALG, socket.ALG_SET_KEY, key)

# 发送数据加解密
s.sendmsg([plaintext], [])

关键点:以上操作完全在用户态完成,不需要 root 权限。 在大多数 Linux 发行版上,非特权用户默认就能创建 AF_ALG socket。只有在管理员通过 SELinux/AppArmor/seccomp 显式限制后,普通用户才无法使用。

1.2 authencesn:一条为协议加密而生的模板链

authencesn 全称 "Authenticated Encryption with Associated Data using ESN",是内核 crypto API 中一个算法模板(template),而不是一个独立算法。它的核心用途是 IPsec、WireGuard、DTLS 等网络协议的加密层——这些协议需要同时满足三个条件:

  1. 加密:数据内容保密(CBC-AES 对称加密)
  2. 认证:确保数据没有被篡改(HMAC-SHA 完整性校验)
  3. 防重放:通过扩展序列号(ESN)机制处理 64 位序列号

它的内部构造如下:

明文
    ↓
  CBC-AES 加密(对称加密,数据保密)
    ↓
  HMAC-SHA256 认证(对 密文 + AAD 生成标签)
    ↓
密文 || Authentication Tag(一次性输出)

/proc/crypto 里你看到的是动态组合形式:

name         : authenc(hmac(sha256),cbc(aes))
driver       : authenc(hmac(sha256-generic,cbc-aes-generic))
module       : authencesn

重要:内核中实际注册的是 authencesn(带 ESN 的增强版),对应模板 crypto/authencesn.c。ESN 主要用于 IPv6 IPsec 场景,防止序列号回绕。

1.3 2017 年的 in-place 优化:一切的起点

2017 年,内核 commit 9d1f0d6e 在 authencesn 模块中引入了一个性能优化:in-place 加密解密

传统加密(out-of-place)需要两个独立的缓冲区:

// 传统 out-of-place 加密
struct scatterlist src[1], dst[1];
sg_set_buf(src, plaintext, len);           // 读:明文缓冲区
sg_set_buf(dst, ciphertext_out, len);        // 写:密文输出缓冲区
crypto_aead_encrypt(req, src, dst, len);    // 数据从 src → dst,中间经过加密

In-place 加密(单缓冲区,原地覆盖):

// In-place 加密
struct scatterlist sg[1];
sg_set_buf(sg, buffer, len);                  // 同一块缓冲区,既是源也是目标
crypto_aead_encrypt(req, sg, sg, len);       // 内核理解:原地加密,不需要临时缓冲

性能收益在哪里?减少一次 memcpy() 意味着:

  • 减少 CPU 缓存抖动
  • 减少内存带宽占用
  • 在高吞吐网络栈(IPsec、TLS offload)中效果可观

但 in-place 路径引入了关键的内存别名(aliasing)问题:scatterlist 的读端(sg_src)和写端(sg_dst)指向同一块内存区域。当内核后续代码需要区分"读"和"写"的 scatterlist 时,处理逻辑变得复杂。

1.4 splice():零拷贝的双刃剑

splice() 是 Linux 2.6.17 引入的系统调用,用于在两个文件描述符之间零拷贝移动数据

#include <sys/splice.h>
ssize_t splice(int fd_in,  loff_t *off_in,
               int fd_out, loff_t *off_out,
               size_t len, unsigned int flags);

与普通 read/write 的本质区别

read(fd) → 内核复制 page cache 内容 → 用户缓冲区
write(fd) ← 用户缓冲区 → 内核复制 page cache

splice(fd_in → fd_out) → 内核转移 page 引用 → 无用户态复制

splice() 的核心实现依赖 pipe buffer 的引用语义:当数据从文件 splice 到管道时,管道 buffer 并不持有数据的副本,而是持有一个指向原始 page cache 页的引用计数指针。这使得数据在两个 fd 之间的移动不需要任何物理内存拷贝——只是"引用"的转移。

问题:这个引用语义在 Copy Fail 场景中成了攻击面——当一个进程修改了 pipe buffer 持有的 page cache 引用,其他引用同一 page 的进程也会看到变化。


二、攻击链路解剖:四步完成从普通用户到 root

2.1 攻击概览

┌──────────────────────────────────────────────────────────────────────┐
│                        攻击链路                                      │
│                                                                      │
│  [普通用户]  open('/usr/bin/su')   → page cache 持有文件内容        │
│       │                                                             │
│       │  splice() 进管道           → pipe buffer 持有 page 引用      │
│       │                                                             │
│       │  AF_ALG authencesn 解密    → scatterlist dst == page cache页│
│       │                                    ↓                        │
│       │                            解密输出写入 page cache!          │
│       │                                                             │
│       └→ execve('/usr/bin/su')     → 从已被篡改的 page cache 读取    │
│                                      跳转到 root shell               │
└──────────────────────────────────────────────────────────────────────┘

2.2 完整攻击流程

准备阶段:构造 page cache 陷阱

imp
复制全文 生成海报 Linux内核 CVE 安全 提权

推荐文章

api远程把word文件转换为pdf
2024-11-19 03:48:33 +0800 CST
前端开发中常用的设计模式
2024-11-19 07:38:07 +0800 CST
pin.gl是基于WebRTC的屏幕共享工具
2024-11-19 06:38:05 +0800 CST
地图标注管理系统
2024-11-19 09:14:52 +0800 CST
Rust 高性能 XML 读写库
2024-11-19 07:50:32 +0800 CST
CSS 媒体查询
2024-11-18 13:42:46 +0800 CST
15 个你应该了解的有用 CSS 属性
2024-11-18 15:24:50 +0800 CST
LangChain快速上手
2025-03-09 22:30:10 +0800 CST
使用 `nohup` 命令的概述及案例
2024-11-18 08:18:36 +0800 CST
mysql关于在使用中的解决方法
2024-11-18 10:18:16 +0800 CST
程序员茄子在线接单