Sploitus

Exploit for cve_2026_31431

kitploit · 2026-08-28

Exploit Code

MARKDOWN150 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-ROOTSECDEV-CVE_2026_31431
# Toolkit CVE-2026-31431 ("Copy Fail")

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

## Autorizzazione

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.

## Riepilogo della vulnerabilitĂ 

`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

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.

## Avvio rapido

root@kitploit:~
    
    
    # 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
    

## Uso del detector

root@kitploit:~
    
    
    python3 test_cve_2026_31431.py
    

Cosa fa:

  1. Verifica che `AF_ALG` e l'algoritmo `authencesn(hmac(sha256),cbc(aes))` siano raggiungibili da un processo senza privilegi.
  2. Crea un file sentinella da 4 KiB in una directory temporanea e popola la page cache.
  3. Invia 8 byte di AAD inline tramite `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.
  4. Chiama `recv()` per pilotare la decrittazione. Il controllo di autenticazione fallisce con `EBADMSG`; la scratch write scatta comunque.
  5. Rilegge il file (page cache, non disco) e cerca il marcatore.



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.

## Uso dell'LPE

root@kitploit:~
    
    
    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:

  1. Cerca la riga dell'UID dell'utente corrente in `/etc/passwd` e trova l'offset in byte del campo UID di 4 caratteri.
  2. Esegue una `write4` su quell'offset, sostituendo l'UID con `0000`.
  3. Chiama `pwd.getpwnam(user)` per confermare che libc ora riporta UID 0.
  4. Con `--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.



### Requisiti

  * L'utente corrente ha un UID a 4 cifre (1000–9999). Gli UID da 1 a 3 cifre richiedono scritture multiple — estendi `write4` di conseguenza.
  * Nessun demone di cache NSS (`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.
  * La pagina di `/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.



### Annullamento

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:

root@kitploit:~
    
    
    # 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.

## Come funziona `write4`

root@kitploit:~
    
    
    sendmsg([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()`.

## Mitigazione

Finché il kernel con la patch non arriva sulla tua distribuzione:

root@kitploit:~
    
    
    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.

## Riferimenti

  * Analisi della divulgazione di Xint: https://xint.io/blog/copy-fail-linux-distributions
  * CVE-2026-31431