
# Riscrittura educativa del PoC Copy Fail (CVE-2026-31431) — LPE del kernel Linux tramite crypto in-place algif_aead + scrittura page-cache con splice()
Riscrittura a scopo educativo del proof-of-concept di Theori/Xint per CVE-2026-31431,
una vulnerabilità di escalation dei privilegi locale nell'interfaccia socket crittografica
algif_aead del kernel Linux. Colpisce tutti i kernel mainline dalla 4.14 (luglio 2017)
fino alla 6.18.21, e il ramo stabile 6.19 fino alla 6.19.11.
Solo per ricerca educativa e test di sicurezza autorizzati. Non eseguire su sistemi di cui non sei proprietario o per cui non hai esplicita autorizzazione scritta per il test. Eseguirlo su un host vulnerabile concede a qualsiasi utente locale non privilegiato accesso root immediato. Gli autori non si assumono alcuna responsabilità per un uso improprio.
Il kernel Linux espone un'API crittografica interna allo spazio utente dal
kernel 3.2 tramite la famiglia di socket AF_ALG (<linux/if_alg.h>). Lo spazio utente
apre un socket SOCK_SEQPACKET, lo associa a un template di algoritmo (es.
"aead" / "authencesn(hmac(sha256),cbc(aes))"), e invia dati per
crittografia o decrittografia.
Nel 2017 è stata integrata un'ottimizzazione delle prestazioni che consente agli algoritmi AEAD di crittografare o decrittografare i dati in-place quando il kernel rileva che la sorgente e la destinazione condividono le stesse pagine sottostanti. Questo evita una copia di memoria ridondante a ogni operazione.
Il template authencesn (che aggiunge il supporto Extended Sequence Number alla
crittografia autenticata) contiene una scrittura temporanea durante la decrittografia: scrive
i byte ESN in un offset specifico all'interno del buffer dati dell'operazione prima che venga eseguito
il controllo di autenticazione. Quando le pagine sorgente dell'operazione sono state ottenute dalla page cache
di un file regolare tramite splice(), e l'ottimizzazione in-place è attiva, questa scrittura
temporanea va direttamente in quelle pagine in cache.
Poiché la page cache del kernel è un mapping condiviso — qualsiasi processo che apre lo stesso file vede le stesse pagine fisiche — un utente non privilegiato può:
/usr/bin/su) con O_RDONLY.AF_ALG.authencesn; la scrittura temporanea ESN corrompe la
pagina a un offset controllato con byte controllati dall'attaccante.Gli scanner di integrità off-host (AIDE, Tripwire, IMA/EVM) che confrontano i checksum dei file su disco sono completamente ciechi a questo attacco. La scrittura scompare dopo un'evizione della page cache o un riavvio, senza lasciare tracce forensi su disco.
Prendendo di mira /usr/bin/su (un binario setuid root) e sovrascrivendo un ramo
che protegge il controllo di autenticazione PAM, l'attaccante ottiene una shell root
senza password.
Il commit di ottimizzazione che ha introdotto il bug è arrivato nel ciclo di sviluppo
della 4.14 (luglio 2017). Qualsiasi kernel compilato con CONFIG_CRYPTO_USER_API_AEAD=y
e CONFIG_CRYPTO_AUTHENC=y (entrambe impostazioni predefinite comuni) negli intervalli
sopra indicati è vulnerabile.
Controlla il security tracker della tua distribuzione per lo stato esatto dell'avviso.
os.splice è stato aggiunto in Python 3.10)/usr/bin/su, leggibile da tutti
poiché è setuid)# Mostra cosa verrebbe scritto senza toccare il kernel (sicuro):
python3 copy_fail_exploit.py --dry-run
# Corregge la page cache di /usr/bin/su (richiede kernel vulnerabile):
# ATTENZIONE: richiede kernel vulnerabile -- usare solo su sistemi di tua proprietà
python3 copy_fail_exploit.py
# Corregge e passa immediatamente a root:
python3 copy_fail_exploit.py --spawn-shell
# Usa un binario di destinazione e un payload personalizzati:
python3 copy_fail_exploit.py --target /usr/bin/sudo --payload-file ./custom_patch.bin
# Output dettagliato (stampa ogni scrittura da 4 byte):
python3 copy_fail_exploit.py --verbose --dry-run
Per impostazione predefinita --spawn-shell è disattivato. Dopo la correzione, lo script stampa:
[+] Patch applicata alla page cache di '/usr/bin/su' (disco invariato).
Esegui `su` per l'escalation, oppure riesegui con --spawn-shell.
Per ripristinare: echo 3 | sudo tee /proc/sys/vm/drop_caches
Devi accettare esplicitamente l'avvio della shell con --spawn-shell. Questa è una scelta
intenzionale: ti obbliga a capire cosa ha fatto l'exploit prima di eseguirne il risultato.
Impostazione della primitiva.
L'exploit apre un socket AF_ALG e lo associa al template
authencesn(hmac(sha256),cbc(aes)) con una chiave fittizia tutta a zero. Poi
configura la dimensione del tag di autenticazione a 4 byte tramite ALG_SET_AEAD_AUTHSIZE.
accept() sul socket di controllo produce un socket di operazione che può inviare
singole richieste di decrittografia.
Attivazione della scrittura nella page cache.
Per ogni blocco da 4 byte del payload della patch, l'exploit chiama sendmsg() sul
socket di operazione con il flag MSG_MORE impostato, passando 8 byte di dati associati
(AAD): 4 byte di riempimento seguiti dai 4 byte del payload. MSG_MORE dice al
kernel di tenere in sospeso l'operazione finché non arrivano altri dati. Poi crea una pipe
ed esegue due chiamate splice() — file_fd → pipe → op_sock — donando le pagine
della page cache del file stesso come input per l'operazione di decrittografia. Quando
viene infine chiamata recv(), il kernel esegue la decrittografia authencesn. L'
ottimizzazione in-place scatta (sorgente e destinazione sono le stesse pagine), e la
scrittura temporanea ESN copia i byte del nostro payload nella pagina in cache all'
offset di destinazione. recv() restituisce EBADMSG perché il tag di autenticazione
non può essere verificato — questo è previsto e innocuo. La scrittura è già avvenuta.
Correzione del binario.
Il payload incorporato (preso verbatim dal PoC di Theori) sovrascrive un'istruzione di ramo
in /usr/bin/su da una build stock di util-linux, trasformando un salto condizionale
che impone l'autenticazione PAM in un fall-through incondizionato. Dopo la correzione,
eseguire su senza password riesce e apre una shell root. Il binario su disco è intatto;
eliminare le voci della page cache (echo 3 > /proc/sys/vm/drop_caches) annulla la modifica
istantaneamente.
Per un'analisi tecnica più approfondita vedi i riferimenti sotto, in particolare il post del blog di Xint e il repository del PoC di Theori.
- rule: AF_ALG socket opened by unprivileged process
desc: >
CVE-2026-31431 — un processo non privilegiato ha aperto un socket AF_ALG (famiglia 38),
che è il primo passo dell'exploit Copy Fail.
condition: >
evt.type = socket and
evt.arg.domain = 38 and
not user.uid = 0
output: >
AF_ALG socket opened (user=%user.name uid=%user.uid
pid=%proc.pid comm=%proc.name)
priority: WARNING
tags: [host, network, privilege_escalation, CVE-2026-31431]
rule CopyFail_CVE_2026_31431 {
meta:
description = "Rileva lo script exploit Copy Fail in memoria o su disco"
cve = "CVE-2026-31431"
strings:
$template = "authencesn(hmac(sha256),cbc(aes))" ascii
$splice = "os.splice" ascii
$aflag = "AF_ALG" ascii
condition:
all of them
}
Aggiungi a /etc/audit/rules.d/cve-2026-31431.rules:
-a always,exit -F arch=b64 -S socket -F a0=38 -k cve_2026_31431_afalg
Poi controlla i log con:
ausearch -k cve_2026_31431_afalg --interpret
1. Applica la patch del kernel (consigliato) Aggiorna al kernel 6.18.22, 6.19.12, 7.0-rc7 o successivo, oppure applica l' aggiornamento di sicurezza della tua distribuzione.
2. Disabilita CONFIG_CRYPTO_USER_API_AEAD in fase di compilazione Se compili il tuo kernel, imposta:
# CONFIG_CRYPTO_USER_API_AEAD is not set
Questo rimuove l'intera interfaccia AEAD di AF_ALG ed elimina la superficie
d'attacco. La maggior parte delle distribuzioni embedded/hardened non ne ha bisogno.
3. seccomp — nega la famiglia di socket AF_ALG
Aggiungi un filtro seccomp ai processi sensibili (o a livello di sistema tramite una policy LSM)
che rifiuti socket(AF_ALG, ...):
// Nega socket() quando domain == AF_ALG (38)
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 38, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EACCES),
4. Riduci i privilegi della page cache tramite hardening del kernel
vm.unprivileged_userfaultfd=0 e tunable simili riducono la superficie d'attacco
delle interfacce di memoria del kernel accessibili all'utente, anche se non bloccano
direttamente questa CVE.
L'exploit modifica solo la page cache in memoria; il binario su disco non viene mai toccato. Per ripristinare il contenuto originale del file in memoria, elimina la page cache del kernel:
echo 3 | sudo tee /proc/sys/vm/drop_caches
Questo espelle tutte le pagine pulite dalla cache. La lettura successiva di /usr/bin/su
(o di qualsiasi binario corretto) ricaricherà i byte originali dal disco.
Il riavvio cancella anche completamente la page cache.
MIT — vedi LICENSE.
MIT è stata scelta rispetto a una licenza personalizzata "solo uso educativo" perché non esiste
una licenza di questo tipo riconosciuta come identificatore OSI/SPDX. MIT è permissiva, legalmente
ben compresa e compatibile con i repository di divulgazione responsabile. L'
intento educativo è espresso nel docstring del modulo, nella sezione Disclaimer di questo README
e nell'output di --help — non in una clausola di licenza personalizzata legalmente dubbia.
| Ramo | Prima versione vulnerabile | Ultima versione vulnerabile | Corretta in |
|---|
| mainline | 4.14 | 6.18.21 | 6.18.22 |
| 6.19 stable | 6.19.0 | 6.19.11 | 6.19.12 |
| 7.0-rc | 7.0-rc1 | 7.0-rc6 | 7.0-rc7 |
| Distribuzione | Kernel fornito (circa) | Corretta? |
|---|
| Debian 12 (Bookworm) | 6.1.x | Sì (backport) |
| Debian 13 (Trixie) | 6.12.x / 6.14.x | Sì (backport) |
| Ubuntu 24.04 LTS | 6.8.x | Sì (USN-7xxx) |
| Ubuntu 24.10 | 6.11.x | Sì |
| RHEL 9.x | 5.14.x (rebased) | Sì (RHSA) |
| Alpine 3.20 | 6.6.x | Sì (backport) |
| Arch Linux | rolling (≥ 6.18.22 ora) | Sì |
| Kali Linux 2026.1 | 6.18.12 | Vulnerabile |
| Kali Linux 2026.2+ | 6.18.22+ | Corretta |
| Linux Mint 22 | 6.8.x (base Ubuntu) | Sì (USN Ubuntu) |