galoryber/CVE-2026-31431-cleaned
GitHub: galoryber/CVE-2026-31431-cleaned
对 CVE-2026-31431 Linux 内核 splice() 本地提权漏洞的完整注释分析,详尽拆解了从 socket 构造到内核内存破坏再到 setuid 二进制文件劫持的完整利用链。
Stars: 1 | Forks: 0
# copyFail.py — CVE 漏洞利用分析报告
## 概述
`copyFail.py` 是一个概念验证PoC 漏洞利用程序,演示了 Linux 内核中的一个**本地权限提升**漏洞。通过滥用 `splice()` 系统调用并结合精心构造的 socket 选项,该漏洞利用程序获得了破坏任意内核内存的能力。它利用这种能力用自定义的 ELF 可执行文件覆盖磁盘上的任何 setuid 二进制文件,然后运行被篡改的二进制文件以获取提升的权限。
**攻击类型:** 本地权限提升 (LPE)
**影响:** 获取完整的 root shell 访问权限(当针对 setuid-root 二进制文件时)
**复杂度:** 低(单脚本,无外部依赖)
**用法:**
```
./copyFail_cleaned.py [target_binary] # default: /usr/bin/su
```
原始 PoC 硬编码了 `/usr/bin/su`,但底层的内核漏洞适用于**任何文件**——攻击者可以覆盖他们选择的任何 setuid 二进制文件。清理后的版本接受一个可选的命令行参数来指定目标,使这种行为更加明确。
## 漏洞是什么
Linux 内核提供了一个 `splice()` 系统调用,它完全在内核内部在两个文件描述符之间复制数据——而无需先将数据复制到用户空间。这被称为“零拷贝 I/O”,用于 Web 服务器和反向代理等高吞吐量应用程序以提高性能。
该漏洞的产生是因为当在配置了不受支持的协议级 socket 选项的 PACKET socket 上使用 `splice()` 时,内核没有正确验证某些 socket 结构的内部状态。通过精心构造这些选项,攻击者可以破坏内核指针,然后使用 `splice()` 将内核内存写入重定向到任意位置。
简单来说:内核在内部移动数据时使用了一个捷径,而这个漏洞利用程序欺骗该捷径将数据写入错误的位置——具体来说,是写入系统二进制文件的内存副本中。
## 漏洞利用工作原理(高层视角)
该漏洞利用遵循以下清晰的步骤序列:
### 1. 设置格式错误的 Socket
该漏洞利用创建了一个原始的 PACKET socket(`AF_PACKET` / `SOCK_RAW`),并将其绑定到故意格式错误的地址。然后它使用 `SOL_PNIO`(协议级别 279)调用 `setsockopt()`,这是一个 Linux 无法识别的 Solaris 级别常量。
**为什么这很重要:** 当内核在 `setsockopt()` 中遇到不受支持的协议级别时,它会落入一个未正确验证数据的通用处理程序中。这使得内核内存处于不一致且可被利用的状态。
### 2. 使用 sendmsg() 破坏内核指针
在通过 `accept()` 建立 socket 连接后,该漏洞利用使用 `sendmsg()` 发送精心构造的辅助(控制)消息。这些消息具有故意不匹配的长度头——有些声称比实际短,有些声称比实际长。
**为什么这很重要:** 内核的控制消息解析器使用这些长度进行指针算术运算。不匹配的长度会导致解析器在预期边界之外进行读取或写入,从而破坏相邻的内核结构——特别是 `splice()` 稍后将遵循的指针。
### 3. 重定向 splice() 以覆盖任意内存
该漏洞利用创建了一个管道并调用了两次 `splice()`:
- 首先,它将数据从 `/usr/bin/su` 拼接到管道中。
- 然后,它将数据从管道拼接到受损的 socket 中。
**为什么这很重要:** `splice()` 调用会遵循在步骤 2 中植入的被破坏的内核指针,导致它将数据写入攻击者选择的内核内存地址,而不是 socket 缓冲区。这为该漏洞利用提供了一个**任意内核内存写入**原语。
### 4. 针对 Shellcode 的每个数据块重复操作
该漏洞利用的 payload 是一个 160 字节的 ELF 可执行文件(见下文)。每次调用漏洞利用函数都会精确写入 4 个字节。因此,主循环运行 40 次(160 / 4 = 40),每次都会:
- 创建一个新的 socket
- 破坏内核状态
- 写入 4 个字节的 shellcode
- 清理
在所有 40 次迭代之后,`/usr/bin/su` 的前 160 个字节已被覆盖。
### 5. 执行被篡改的二进制文件
最后,该漏洞利用运行 `os.system("su")`。内核从其页面缓存中加载修改后的 `/usr/bin/su`(现在包含攻击者的 shellcode,而不是真正的 `su` 二进制文件),然后执行该 shellcode。
## Shellcode 载荷
嵌入的 payload 解压后是一个 **160 字节的 x86-64 ELF 可执行文件**,包含以下 shellcode:
```
; Attempt syscall 105 (execveat) — may not be available on older kernels
xor eax, eax
xor edi, edi
mov al, 0x69 ; syscall 105
syscall
; Fallback: syscall 59 (execve) — the reliable path
lea rdi, [rip+0xf] ; RDI = pointer to "/bin/sh"
xor esi, esi ; RSI = NULL (envp)
push 0x3b ; syscall 59 number
pop eax
cdq ; RDX = NULL (argv)
syscall
; Exit cleanly
xor edi, edi
push 0x3c ; syscall 60 (exit)
pop eax
syscall
; Data section: "/bin/sh\0\0\0"
```
**它的作用:** 生成一个不带参数的 `/bin/sh`,继承运行 `su` 的进程的权限。如果漏洞利用以 root 身份运行(或具有授予 `su` root 访问权限的 capabilities),则生成的 shell 就是 root shell。
**它不会做什么:**
- 没有反向 shell 或网络回调
- 没有持久化机制(cron、systemd、SSH 密钥等)
- 没有凭证窃取或数据渗出
- 没有进程隐藏或反取证
这是一个直接的一次性权限提升,与研究 PoC 一致。
## 技术细节
### 使用的主要 Linux 常量
| 常量 | 值 | 在漏洞利用中的用途 |
|----------|-------|-------------------|
| `AF_PACKET` | 17 | 原始数据包 socket 族 |
| `SOCK_RAW` | 3 | 原始 socket 类型 |
| `SOL_PNIO` | 279 | 不受支持的协议级别(Solaris) |
| `MSG_DONTWAIT` | 0x400 | 非阻塞 sendmsg 标志 |
### 文件结构
```
copyFail.py
├── hex_to_bytes() — hex string decoder
├── exploit_splice() — core exploit (socket setup + corruption + splice)
│ ├── Phase 1: Create PACKET socket, bind, setsockopt (SOL_PNIO)
│ ├── Phase 2: accept() connection
│ ├── Phase 3: sendmsg() with crafted ancillary messages
│ ├── Phase 4: pipe() + splice() to corrupt kernel memory
│ └── Phase 5: recv() attempt (solidifies corruption)
└── Main loop:
├── Open /usr/bin/su (read-only)
├── Decompress embedded payload (zlib → 160-byte ELF)
├── Loop: inject 4 bytes per iteration (40 iterations total)
└── Execute tampered su → root shell
```
### 为什么是只读的?
该漏洞利用以 `O_RDONLY`(只读)方式打开 `/usr/bin/su`。它不需要写访问权限,因为破坏是通过内核页面缓存发生的——这是内核在将更改刷新到磁盘之前使用的文件内存副本。基于 `splice()` 的写入完全绕过了正常的文件权限,直接进入内核内存。
## 检测与缓解
### 妥协指标
- `/usr/bin/su` 二进制文件被修改(将哈希值与包管理器中的进行比较)
- 非特权用户(非 root)异常创建 PACKET socket
- 带有未知协议级别的意外 `setsockopt()` 调用
- 在非正规文件描述符上调用 `splice()` 系统调用
### 缓解措施
- 在可用时应用针对此 CVE 的内核补丁
- 使用 `sysctl` 限制非特权用户创建 `AF_PACKET` socket
- 启用内核加固选项(CONFIG_FORTIFY_SOURCE、CONFIG_STACKPROTECTOR)
- 监控关键二进制文件的文件完整性(AIDE、OSSEC、Tripwire)
## 参考
- 原始 PoC:`copyFail.py`(已分发)
- 带注释的版本:`copyFail_cleaned.py`(同目录)
- 相关内核子系统:`net/packet/`、`fs/splice.c`、`net/core/sock.c`
*本报告仅供安全研究和防御分析之用。*
标签:0day挖掘, CVE, CVE-2026-31431, ELF文件分析, Linux内核漏洞, LPE, meg, PACKET套接字, PoC, SetUID, Socket选项, splice系统调用, Web报告查看器, 二进制文件分析, 任意内存覆盖, 信息安全, 内核安全, 内核态利用, 安全渗透, 提权, 数字签名, 数据展示, 暴力破解, 本地权限提升, 漏洞分析, 红队, 网络安全, 路径探测, 逆向工具, 隐私保护, 零拷贝