
Exploit POC per CVE_2026_31431
Detector e proof-of-concept di LPE per il bug di scratch-write nella page cache di Linux algif_aead / authencesn divulgato il 29 aprile 2026.
Analisi della divulgazione: https://xint.io/blog/copy-fail-linux-distributions
Utilizzare solo su host di tua proprietà o su cui hai un esplicito incarico di valutazione. L'LPE modifica lo stato in memoria (page cache), ma la tecnica è una vera escalation di privilegi: eseguirla su sistemi senza autorizzazione è illegale nella maggior parte delle giurisdizioni.
algif_aead esegue le operazioni AEAD in-place (req->src == req->dst). Quando i dati sorgente vengono forniti tramite splice() da un file regolare, la scatterlist di destinazione contiene riferimenti alle pagine della page cache del file — cioè il kernel vi scriverà dentro. L'algoritmo authencesn(hmac(sha256), cbc(aes)) esegue quindi una scratch write di 4 byte del campo seqno_lo dell'AAD (byte 4–7 dell'AAD fornito via sendmsg) in quella destinazione, corrompendo la copia del file presente nella page cache.
Poiché il file su disco non viene mai modificato, non esiste alcuna firma su disco; la corruzione è osservabile solo dai lettori che condividono la page cache. /etc/passwd e /usr/bin/su sono entrambi leggibili da tutti, quindi un utente locale senza privilegi può corrompere la vista che il kernel in esecuzione ha di entrambi.
Interessati: i kernel che includono il commit 72548b093ee3 (AEAD in-place, 2017) senza il revert a monte. La divulgazione ha confermato Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3 e SUSE 16, ma la primitiva sottostante è precedente a questo intervallo.
| File | Scopo |
|---|---|
test_cve_2026_31431.py | Detector non distruttivo. Opera su un file sentinella in una directory temporanea; non tocca mai i binari di sistema. |
exploit_cve_2026_31431.py | LPE. Imposta l'UID dell'utente corrente a 0 nella page cache di /etc/passwd, poi invoca su per una shell di root. |
Entrambi gli script sono in Python 3.10+ puro e usano solo la stdlib.
# 1. Detect
python3 test_cve_2026_31431.py
# exit 0 = not vulnerable, 2 = vulnerable, 1 = test error
# 2. Exploit (interactive — su will prompt for your own password)
python3 exploit_cve_2026_31431.py --shell
python3 test_cve_2026_31431.py
Cosa fa:
AF_ALG e l'algoritmo authencesn(hmac(sha256),cbc(aes)) siano raggiungibili da un processo senza privilegi.sendmsg+cmsg con seqno_lo impostato al marcatore PWND, poi esegue os.splice() di 32 byte della pagina della page cache del file sentinella nel socket AF_ALG dell'operazione.recv() per pilotare la decrittazione. Il controllo di autenticazione fallisce con EBADMSG; la scratch write scatta comunque.Classi di output:
Precondition not met — AF_ALG o authencesn non disponibili. Uscita 0.VULNERABLE to CVE-2026-31431 — il marcatore PWND è finito nella pagina trasferita con splice. Uscita 2.Page cache MODIFIED via in-place AEAD splice path — la pagina è stata scritta ma il marcatore non è finito nella posizione prevista. Trattare come vulnerabile. Uscita 2.Page cache intact — patch applicata. Uscita 0.Il detector non tocca mai /usr/bin/su, /etc/passwd o qualsiasi altro file al di fuori della directory temporanea che crea, e quel file viene rimosso all'uscita.
python3 exploit_cve_2026_31431.py # patch only, print next steps
python3 exploit_cve_2026_31431.py --shell # patch and exec `su <user>`
Cosa fa:
/etc/passwd e trova l'offset in byte del campo UID di 4 caratteri.write4 su quell'offset, sostituendo l'UID con 0000.pwd.getpwnam(user) per confermare che libc ora riporta UID 0.--shell, esegue execvp("su", ["su", user]). Inserisci la tua password. PAM valida tramite /etc/shadow (non toccato), poi setuid(getpwnam(user).pw_uid) si attesta a 0.write4 di conseguenza.nscd, sssd, systemd-userdbd) sta mascherando le letture di /etc/passwd. Se getpwnam restituisce ancora l'UID reale dopo la patch, riavvia o aggira la cache, oppure scegli un utente diverso./etc/passwd deve rimanere in cache tra la patch e l'esecuzione di su. In pratica è affidabile su qualsiasi sistema con pressione di memoria normale.Il file /etc/passwd su disco non viene modificato.
Dry-run (exploit_cve_2026_31431.py senza --shell) rimuove automaticamente la pagina corrotta all'uscita tramite POSIX_FADV_DONTNEED, quindi le risoluzioni UID→nome tornano subito alla normalità.
Dopo --shell, la pagina rimane corrotta finché non la pulisci. Finché è corrotta, qualsiasi operazione che risolve l'UID 1000 → nome (es. ls, file manager, controlli di proprietà scp/sftp) fallirà o mostrerà ID numerici. Per pulire:
# unprivileged - request page-cache eviction for /etc/passwd:
python3 -c "import os; fd=os.open('/etc/passwd', os.O_RDONLY); \
os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED); os.close(fd)"
# from the root shell:
echo 3 > /proc/sys/vm/drop_caches
Anche un riavvio la pulisce.
write4sendmsg([8-byte AAD], cmsg=[ALG_SET_OP=DECRYPT, ALG_SET_IV, ALG_SET_AEAD_ASSOCLEN=8],
flags=MSG_MORE)
splice(target_fd, pipe_w, 32, offset_src=file_offset)
splice(pipe_r, op_fd, 32)
recv(op_fd) # EBADMSG; scratch write has already landed
I 4 byte dalle posizioni 4–7 dell'AAD (seqno_lo) vengono scritti da authencesn nella scatterlist di destinazione, che in questo percorso è la pagina della page cache da cui abbiamo fatto lo splice di target_fd. L'offset di arrivo all'interno della pagina corrisponde a offset_src che abbiamo passato a splice().
Finché il kernel con la patch non arriva sulla tua distribuzione:
sudo tee /etc/modprobe.d/disable-algif-aead.conf <<<'install algif_aead /bin/false'
sudo rmmod algif_aead 2>/dev/null
Dopo l'applicazione, test_cve_2026_31431.py dovrebbe riportare Precondition not met e uscire con 0.
La correzione a monte riporta le operazioni AEAD da in-place a out-of-place, mantenendo le pagine della page cache fuori dalle scatterlist scrivibili.