严重程度:CVSS 7.8 (High)
漏洞别名:Copy Fail
影响范围:2017 年至今几乎所有主流 Linux 发行版
公开披露:2026-04-29
内核补丁:commit a664bf3d603d


一、交互式漏洞利用流程演示

以下是一个 7 步交互式可视化,展示从普通用户到 root 的完整攻击链。

1 / 7

二、漏洞技术原理

根本原因

2017 年,algif_aead.c 引入了 in-place 优化(commit 72548b093ee3):

1
2
3
4
5
6
7
// 优化前(安全):src 和 dst 分离
req->src = TX_SGL;   // 含 Page Cache 页(只读路径)
req->dst = RX_SGL;   // 用户缓冲区(可写)

// 优化后(有漏洞):src = dst
req->src = req->dst = 合并的 SGL;
// Page Cache 页通过 sg_chain() 链入可写的 dst!

authencesn 的越界写

1
2
3
// 第3步:写 seqno_lo 到 dst[assoclen + cryptlen]
// 这个位置正好是 Tag 区域 = 现在是 Page Cache 页!
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1);

攻击者控制三个维度:

控制项 方法
哪个文件 任何普通用户可读的 setuid 文件
哪个偏移 通过 assoclen + splice offset 精确定位
写入什么值 AAD[4:8](seqno_lo)完全由攻击者构造

三、修复方法

数据来源:Ubuntu 官方、CERT-EU、CloudLinux、Tenable(2026-04-30)

3.1 确认是否受影响

1
2
3
4
5
6
7
# 查看内核版本
uname -r

# 确认模块状态(built-in 或 loadable)
modinfo algif_aead | grep filename
# 输出 "(builtin)"  → RHEL 系,需用 grubby 方案
# 输出文件路径     → Debian 系,rmmod 有效

3.2 立即临时缓解

方案 A:禁用模块(Ubuntu / Debian 系)

1
2
3
echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/disable-algif.conf
sudo rmmod algif_aead 2>/dev/null || true
sudo update-initramfs -u

验证:

1
2
sudo modprobe algif_aead   # 应该报错
lsof | grep AF_ALG         # 应该无输出

方案 B:grubby 内核参数(RHEL / AlmaLinux / Rocky 系)

⚠️ RHEL 系 algif_aead 为内置模块,modprobe.d 无效,必须用此方案。

1
2
3
4
sudo grubby --update-kernel=ALL --args="initcall_blacklist=algif_aead_init"
sudo reboot
# 重启后验证
sudo grubby --info=ALL | grep initcall_blacklist

方案 C:eBPF / BPF LSM 热拦截 AF_ALG socket(Rocky Linux 8.10 上实测)

适用于 BPF LSM 已启用的 RHEL / Rocky / Alma 系内核。该方案不杀进程,而是在 socket_create 阶段直接返回 EPERM,阻止非 root 用户创建 AF_ALG socket。攻击链第一步失败,后续 bind()splice()recv() 都不会发生。

确认当前内核支持 BPF LSM:

1
2
3
4
cat /sys/kernel/security/lsm 2>/dev/null || true

grep -E 'CONFIG_BPF_LSM|CONFIG_BPF_SYSCALL|CONFIG_BPF_JIT|CONFIG_DEBUG_INFO_BTF' \
  /boot/config-$(uname -r) 2>/dev/null || true

如果输出中包含:

1
2
3
4
capability,yama,selinux,bpf
CONFIG_BPF_LSM=y
CONFIG_BPF_SYSCALL=y
CONFIG_DEBUG_INFO_BTF=y

则可以使用 BPF LSM 方案。

安装编译依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 如果当前 repo 没有 PowerTools,可先补充 Rocky 8 PowerTools repo
cat > /etc/yum.repos.d/Rocky-PowerTools-Aliyun.repo <<'EOF'
[PowerTools]
name=Rocky Linux $releasever - PowerTools - Aliyun
baseurl=https://mirrors.aliyun.com/rockylinux/$releasever/PowerTools/$basearch/os/
enabled=1
gpgcheck=1
countme=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
EOF

dnf clean all
dnf makecache

dnf install -y clang llvm gcc make bpftool libbpf libbpf-devel \
  elfutils-libelf-devel zlib-devel kernel-headers

生成 vmlinux.h

1
2
3
mkdir -p /root/block-afalg
cd /root/block-afalg
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

编写 BPF LSM 程序:

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
cat > block_afalg.bpf.c <<'EOF'
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

char LICENSE[] SEC("license") = "GPL";

#define AF_ALG 38
#define EPERM 1

SEC("lsm/socket_create")
int BPF_PROG(block_afalg_socket_create,
             int family,
             int type,
             int protocol,
             int kern,
             int ret)
{
    __u64 uid_gid;
    __u32 uid;
    char comm[16];

    if (ret != 0)
        return ret;

    uid_gid = bpf_get_current_uid_gid();
    uid = (__u32)uid_gid;

    if (family == AF_ALG && uid != 0) {
        bpf_get_current_comm(&comm, sizeof(comm));
        bpf_printk("deny AF_ALG socket: uid=%d comm=%s\n", uid, comm);
        return -EPERM;
    }

    return 0;
}
EOF

编写用户态 loader:

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
cat > block_afalg_loader.c <<'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>

static volatile sig_atomic_t exiting = 0;

static void sig_handler(int sig)
{
    exiting = 1;
}

int main(int argc, char **argv)
{
    struct bpf_object *obj = NULL;
    struct bpf_program *prog;
    struct bpf_link *links[32];
    int link_count = 0;
    int err;
    struct rlimit rlim = {
        .rlim_cur = RLIM_INFINITY,
        .rlim_max = RLIM_INFINITY,
    };
    const char *obj_path = "/usr/local/libexec/block_afalg.bpf.o";

    if (argc > 1)
        obj_path = argv[1];

    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);
    setrlimit(RLIMIT_MEMLOCK, &rlim);

    obj = bpf_object__open_file(obj_path, NULL);
    if (!obj) {
        fprintf(stderr, "failed to open BPF object: %s\n", obj_path);
        return 1;
    }

    err = bpf_object__load(obj);
    if (err) {
        fprintf(stderr, "failed to load BPF object: %d\n", err);
        bpf_object__close(obj);
        return 1;
    }

    bpf_object__for_each_program(prog, obj) {
        struct bpf_link *link = bpf_program__attach(prog);
        if (!link) {
            fprintf(stderr, "failed to attach BPF program\n");
            bpf_object__close(obj);
            return 1;
        }
        if (link_count < 32)
            links[link_count++] = link;
    }

    printf("block-afalg BPF LSM loaded: non-root AF_ALG socket returns EPERM.\n");

    while (!exiting)
        sleep(1);

    while (link_count > 0)
        bpf_link__destroy(links[--link_count]);

    bpf_object__close(obj);
    return 0;
}
EOF

编译并安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
clang -O2 -g -target bpf -D__TARGET_ARCH_x86 \
  -I/usr/include \
  -c block_afalg.bpf.c \
  -o block_afalg.bpf.o

gcc -O2 -g block_afalg_loader.c \
  -I/usr/include \
  -L/usr/lib64 \
  -o block-afalg \
  -lbpf -lelf -lz

mkdir -p /usr/local/libexec
install -m 0600 block_afalg.bpf.o /usr/local/libexec/block_afalg.bpf.o
install -m 0750 block-afalg /usr/local/sbin/block-afalg

手工加载测试:

1
/usr/local/sbin/block-afalg /usr/local/libexec/block_afalg.bpf.o

另开普通用户终端验证:

1
2
3
4
5
6
python3 - <<'PY'
import socket
print("before")
s = socket.socket(38, socket.SOCK_SEQPACKET, 0)
print("after", s)
PY

预期结果:

1
2
before
PermissionError: [Errno 1] Operation not permitted

root 用户仍然允许创建 AF_ALG socket:

1
2
3
4
5
6
sudo python3 - <<'PY'
import socket
print("before")
s = socket.socket(38, socket.SOCK_SEQPACKET, 0)
print("after", s)
PY

普通 TCP socket 不受影响:

1
2
3
4
5
python3 - <<'PY'
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
print("AF_INET ok")
PY

配置为 systemd 服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cat > /etc/systemd/system/block-afalg.service <<'EOF'
[Unit]
Description=Block non-root AF_ALG socket creation using BPF LSM
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/local/sbin/block-afalg /usr/local/libexec/block_afalg.bpf.o
Restart=always
RestartSec=2
User=root

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now block-afalg.service
systemctl status block-afalg.service

查看 BPF 拦截日志:

1
cat /sys/kernel/debug/tracing/trace_pipe

回滚:

1
2
3
4
5
6
systemctl stop block-afalg.service
systemctl disable block-afalg.service
rm -f /etc/systemd/system/block-afalg.service
rm -f /usr/local/sbin/block-afalg
rm -f /usr/local/libexec/block_afalg.bpf.o
systemctl daemon-reload

该方案的本质是:

1
2
3
4
5
非 root 用户调用 socket(AF_ALG, ...)
  → BPF LSM socket_create hook
  → family == AF_ALG && uid != 0
  → return -EPERM
  → socket fd 不创建

3.3 安装修复补丁

Ubuntu / Debian

1
2
3
sudo apt update && sudo apt upgrade -y linux-image-$(uname -r)
sudo reboot
uname -r

RHEL / AlmaLinux 8

1
2
3
sudo dnf clean all && sudo dnf update kernel* -y && sudo reboot
sudo dnf config-manager --disable almalinux-testing
sudo dnf remove $(dnf repoquery --installonly --latest-limit=-1 -q)

RHEL / AlmaLinux 9

1
2
3
4
sudo dnf install -y https://repo.almalinux.org/almalinux/9/extras/x86_64/os/Packages/almalinux-release-testing-9-1.el9.noarch.rpm
sudo dnf update kernel -y && sudo reboot
sudo dnf config-manager --disable almalinux-testing
sudo dnf remove $(dnf repoquery --installonly --latest-limit=-1 -q)

RHEL / AlmaLinux 10

1
2
3
4
sudo dnf install -y https://repo.almalinux.org/almalinux/10/extras/x86_64/os/Packages/almalinux-release-testing-10-1.el10.x86_64.rpm
sudo dnf update kernel -y && sudo reboot
sudo dnf config-manager --disable almalinux-testing
sudo dnf remove $(dnf repoquery --installonly --latest-limit=-1 -q)

Amazon Linux 2023

1
sudo yum update kernel -y && sudo reboot

SUSE / openSUSE

1
sudo zypper update kernel-default -y && sudo reboot

3.4 打完补丁后撤销临时缓解

1
2
3
4
5
6
7
# Debian 系
sudo rm /etc/modprobe.d/disable-algif.conf && sudo update-initramfs -u

# RHEL 系
sudo grubby --update-kernel=ALL --remove-args="initcall_blacklist=algif_aead_init"
sudo systemctl disable block-afalg.service
sudo reboot

3.5 Kubernetes / 容器环境

⚠️ 修复必须在宿主机上操作,不能只在容器内。

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
# patch-copy-fail.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: patch-copy-fail-cve
  namespace: default
spec:
  selector:
    matchLabels:
      app: patch-copy-fail-cve
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: patch-copy-fail-cve
    spec:
      hostPID: true
      priorityClassName: system-node-critical
      volumes:
      - name: root-mount
        hostPath:
          path: /
          type: Directory
      initContainers:
      - name: patch-copy-fail-cve
        image: busybox:1.36.1
        command: ["/bin/sh", "-c"]
        args:
        - |
          tee /host/etc/modprobe.d/disable-algif-aead.conf <<<'install algif_aead /bin/false'
          chroot /host rmmod algif_aead 2>/dev/null || true
          chroot /host update-initramfs -u
        securityContext:
          privileged: true
          runAsUser: 0
        volumeMounts:
        - name: root-mount
          mountPath: /host
      containers:
      - name: pause
        image: registry.k8s.io/pause:3.10.1
1
kubectl apply -f patch-copy-fail.yaml

四、各方案对比

方案 需要重启 效果 适用场景
rmmod algif_aead 立即生效,重启失效 Debian 系,模块可卸载
modprobe.d 禁用 需更新 initramfs 持久有效 Debian 系
grubby 内核参数 需要 持久有效 RHEL 系(built-in 内核)
eBPF / BPF LSM 拦截 AF_ALG 即时拒绝非 root 创建 AF_ALG socket BPF LSM 可用的 RHEL / Rocky / Alma 系
升级内核(推荐) 需要 根本修复 所有发行版
KernelCare livepatch 不需要 根本修复 订阅用户

五、影响范围说明

禁用 algif_aead 不影响:dm-crypt/LUKS、kTLS、IPsec/XFRM、OpenSSL/GnuTLS/NSS(默认配置)、SSH。

检查当前是否有程序依赖:

1
2
lsof | grep AF_ALG
# 无输出 → 可安全禁用

六、参考资料