Blog
VM EscapeCVE-2026-64561Linux KernelSicurezza Virtualizzazione

Zapscape CVE-2026-64561: il secondo VM escape da KVM in cinque settimane

Zapscape è un use-after-free nella shadow MMU di KVM che trasforma il root dentro una guest in root sull'host. È il terzo escape nello stesso sottosistema da maggio: patchare un CVE non chiude la classe.

Zero Hunt Research··10 min di lettura

Il 6 agosto 2026 il ricercatore Hyunwoo Kim ha pubblicato Zapscape — CVE-2026-64561, un use-after-free nella shadow MMU di KVM/x86 che permette a codice con privilegi kernel dentro una macchina virtuale di scrivere in memoria dell'host già liberata e prendere il controllo dell'hypervisor. Il proof-of-concept è diventato pubblico lo stesso giorno. Se virtualizzate su Linux — e se avete qualcosa su Linux, quasi certamente virtualizzate — la parte interessante non è questo singolo bug: è che Zapscape è il terzo use-after-free nel codice di shadow paging di KVM divulgato da fine maggio, stesso sottosistema, stesso impatto, e la patch del precedente non copre questo.

Cosa rompe davvero Zapscape

La shadow MMU di KVM mantiene le page table lato host che traducono gli indirizzi di memoria della guest. Quando l'hypervisor esaurisce le shadow page le recupera: le zappa, percorrendo ricorsivamente la gerarchia e rimuovendo le entry figlie.

Il difetto è un errore di ordinamento in quel percorso. KVM verificava se la root MMU corrente fosse stale prima di rendere disponibili nuove pagine. Il reclaim poteva quindi invalidare proprio quella root, ma KVM proseguiva comunque lungo il fault path. Le shadow page figlie ereditavano uno stato non valido lasciando link di lista pendenti, e chi controlla il timing ottiene una scrittura in memoria che il kernel ha già liberato. La scheda NVD esprime senza giri di parole l'invariante violato: le pagine invalide non dovrebbero mai comparire nella lista delle MMU page attive.

La patch upstream sposta il controllo sulla root stale dopo la verifica di disponibilità delle pagine e fa ripartire il fault (RET_PF_RETRY) quando il reclaim ha invalidato una root. È entrata il 21 luglio 2026, sedici giorni prima della divulgazione pubblica.

La finestra vulnerabile va dal commit f95eec9bed76 (8 luglio 2020) fino a quella correzione: da Linux 5.9 in avanti, circa sei anni di kernel in produzione. Il punteggio preliminare di Red Hat è CVSS 7.0, vettore AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H. Non ancorate la risposta a quel 7.0: "complessità alta, locale, privilegi bassi" è esattamente l'aspetto che un escape da hypervisor assume in CVSS, e CVSS non modella il raggio d'azione di perdere l'host sotto trenta tenant.

Le precondizioni decidono se il problema è vostro

Non è un bug che scatta su qualunque macchina KVM, e la distinzione conta più del punteggio:

  • Servono privilegi kernel nella guest (L1). L'attaccante deve già essere root dentro una VM. Su un host multi-tenant non è un ostacolo: è il prodotto che gli avete venduto.
  • La virtualizzazione annidata deve essere esposta alla guest. Niente nested virt, niente percorso raggiungibile.
  • Su Intel servono entrambe le lunghezze di page walk EPT, 4 e 5, esposte a L1. Su AMD (SVM/NPT) il write-up dell'autore descrive una superficie più ampia, e il PoC pubblicato prende di mira proprio AMD.

Poi c'è la precondizione che coglie impreparato chi pensa di essere fuori perimetro. Sulle distribuzioni in cui /dev/kvm è scrivibile da tutti — modo 0666, il default in famiglia Red Hat — un utente locale non privilegiato può sfruttare il bug senza avere alcuna VM preesistente. Si crea la propria guest, dentro la quale è root per definizione, e usa l'escape come privilege escalation locale fino a root sull'host. La copertura di Phoronix segnala esplicitamente questo doppio impatto. Un build server, un runner di CI, una postazione di sviluppo condivisa, una workstation con /dev/kvm permissivo: nessuna di queste compare come "hypervisor" nell'inventario asset, e tutte rientrano nel perimetro.

Tre escape nello stesso sottosistema in dieci settimane

Zapscape non è un caso isolato. Messe in fila, le divulgazioni sulla shadow MMU di questo trimestre:

CVE Nome Meccanismo Fix / divulgazione
CVE-2026-46113 La shadow MMU calcola i GFN da sp->gfn più l'indice SPTE; il calcolo salta se le page table guest cambiano tra due VM entry, lasciando rmap stale dopo la free di una kvm_mmu_page. CVSS 8.8, scope changed. Pubblicato 28 maggio 2026
CVE-2026-53359 Januscape Confusione di frame number / ruolo della shadow page: l'host associa una shadow PTE al guest frame sbagliato. Primo escape KVM dimostrato sia su Intel sia su AMD, presentato come zero-day al kvmCTF di Google. Difetto risalente al 2010. Upstream 16 giugno, stable 4 luglio 2026
CVE-2026-64561 Zapscape Use-after-free nel percorso di zap ricorsivo; controllo sulla root stale eseguito prima del reclaim. Upstream 21 luglio, pubblico 6 agosto 2026

Lo stesso ricercatore ha pubblicato anche ITScape (CVE-2026-46316) contro KVM/arm64. E la genealogia è molto più lunga: CVE-2019-7221 (use-after-free nel preemption timer VMX di KVM) e CVE-2021-22543 (gestione di VM_IO|VM_PFNMAP che lasciava liberare pagine ancora accessibili al VMM) sono la stessa forma di difetto sullo stesso confine di fiducia.

Un'osservazione interna, che offriamo come dato nostro e non come statistica di settore: interrogando la Knowledge RAG di Zero Hunt sui precedenti di questa classe prima di scrivere, non è tornato un singolo match ma l'intera catena — 2019, 2021, maggio 2026 e la coppia attuale. Il corpus riconosce lo shadow paging come punto debole ricorrente, non come sequenza di coincidenze. È codice legacy, sensibile alle prestazioni, pieno di regole di lifetime imposte per convenzione e non dal sistema di tipi. Questa è una classe di bug, e le classi ritornano.

Auditor: L'escape da KVM risulta rimediato dall'8 luglio. Platform engineer: Corretto. Abbiamo portato i kernel stable di luglio su tutta la flotta per Januscape. Auditor: Questo rilievo è datato 6 agosto e parla dello stesso sottosistema, da guest a host, root sull'hypervisor. Platform engineer: È un CVE diverso. Il kernel di Januscape non contiene il fix di Zapscape. Auditor: Quindi il controllo che avete evidenziato il mese scorso era vero, e nel frattempo il confine è rimasto aperto.

Hanno ragione entrambi, ed è esattamente questo il problema. "Abbiamo patchato il VM escape" è un'affermazione su un CVE; l'auditor sta chiedendo di un confine. Le due cose divergono nel momento in cui la classe produce l'istanza successiva — e qui ne ha prodotte due in cinque settimane.

Remediation

1. Sono esposto?

Tre verifiche: versione del kernel, esposizione della virtualizzazione annidata, permessi di /dev/kvm.

# Kernel nell'intervallo vulnerabile? (da 5.9 fino al fix del 21 luglio 2026)
uname -r

# La nested virtualization è attiva?
cat /sys/module/kvm_intel/parameters/nested 2>/dev/null   # Y/1 = esposta
cat /sys/module/kvm_amd/parameters/nested   2>/dev/null   # Y/1 = esposta

# Chi raggiunge l'interfaccia? 0666 = ogni utente locale è in perimetro
stat -c '%a %U:%G' /dev/kvm
getent group kvm

Siete esposti al percorso di escape se il kernel non è patchato e la nested virt è attiva. Siete esposti anche al percorso di privilege escalation locale se /dev/kvm è 0666, indipendentemente dal fatto che facciate girare guest.

2. Patch — versioni corrette esatte

Le release stable pubblicate alla divulgazione sono 6.6.148, 6.12.101, 6.18.42, 7.1.6 e 7.2-rc5. Notate cosa non c'è: l'intervallo vulnerabile parte da 5.9, quindi i rami mantenuti più vecchi (5.10, 5.15, 6.1) rientrano ma non avevano una stable enumerata al momento della divulgazione — verificate il backport sul CVE tracker della vostra distribuzione invece di darlo per acquisito. Validate per versione, non per "abbiamo lanciato gli aggiornamenti": un pacchetto kernel installato ma non avviato è un host non patchato.

Se il 4 luglio avete preso i kernel stable di Januscape (7.1.3, 6.18.38, 6.12.95, 6.6.144, 6.1.177, 5.15.211, 5.10.260), non siete coperti per Zapscape: sono precedenti al fix del 21 luglio.

3. Non potete riavviare adesso? Controlli compensativi

Entrambi efficaci, entrambi a basso costo:

# Disattivare la nested virtualization (nessuna guest attiva sul modulo)
echo 'options kvm_intel nested=0' > /etc/modprobe.d/kvm-nested.conf
echo 'options kvm_amd nested=0'  >> /etc/modprobe.d/kvm-nested.conf

# Chiudere il percorso utente locale: /dev/kvm ristretto al gruppo kvm
printf 'KERNEL=="kvm", GROUP="kvm", MODE="0660"\n' > /etc/udev/rules.d/65-kvm.rules
udevadm control --reload && udevadm trigger --name-match=kvm

Disattivare la nested virt elimina del tutto la precondizione dell'escape. Portare /dev/kvm a 0660 elimina la strada dell'utente non privilegiato sulle macchine che hypervisor non sono — dove, nella maggior parte dei parchi macchine, l'accesso universale non serviva a nessuno. Il live patching (kpatch, kernelcare, ksplice) è la terza opzione dove il vostro fornitore distribuisce questo fix: verificate che il CVE specifico sia incluso prima di registrarlo come rimediato.

4. Caccia alla compromissione

Il segnale di rete qui è quasi nullo: l'attacco avviene interamente dentro una singola macchina fisica, quindi è una caccia sulla telemetria host. Mappatura MITRE ATT&CK: T1611 (Escape to Host), T1068 (Exploitation for Privilege Escalation) e, dopo l'escape, T1014 (Rootkit), T1562.001 (Impair Defenses) e T1005 (Data from Local System — le immagini disco e i segreti dei co-tenant sono il vero bottino).

# Il tentativo fallito è rumoroso: oops KVM/MMU, GPF, report KASAN
journalctl -k --since "2026-05-01" | grep -iE 'kvm|mmu|BUG:|general protection|KASAN|Oops'

# Un kernel tainted che nessuno sa spiegare è un rilievo
cat /proc/sys/kernel/tainted

# Chi apre /dev/kvm, e guest che all'improvviso richiedono nested virt
fuser -v /dev/kvm 2>&1
grep -rlE '<cpu.*(vmx|svm)' /etc/libvirt/qemu/ 2>/dev/null

Segnali da escalare: oops ripetuti della MMU di KVM su un host con guest non fidate; una guest la cui configurazione ha acquisito la nested virtualization senza ticket di modifica; /dev/kvm aperto da un service account o da un job di build che non ha motivo di creare VM; e qualunque modulo kernel nuovo caricato sull'host in prossimità di questi eventi.

5. Bonifica e verifica

Se trovate prove di un escape riuscito, il kernel dell'host ha eseguito codice controllato dall'attaccante: l'asset compromesso è l'host, non la guest. Ricostruitelo. Non migrate le guest su un host pulito dando per scontato che il problema le abbia seguite, e considerate divulgati i segreti dell'host. Nell'ordine:

  1. Evacuate i carichi su un host patchato, isolando la guest sospetta invece di riavviarla.
  2. Ricostruite l'host da supporti noti e integri, poi patchate prima di rimetterlo nel pool.
  3. Ruotate tutto ciò che l'host custodiva: credenziali di gestione dell'hypervisor, chiavi di storage e della rete di backup, materiale TLS dell'host, qualunque token presente in immagini disco di co-tenant leggibili dall'attaccante.
  4. Verificate dopo la patch, per assenza e per versione:
uname -r                                          # deve essere >= la release corretta
cat /sys/module/kvm_*/parameters/nested           # atteso N/0 dove disattivato
stat -c '%a' /dev/kvm                             # atteso 660

Dove si colloca Zero Hunt

La domanda operativa da cui parte questo articolo non è "quale CVE devo patchare", ma "un punto d'appoggio dentro una delle mie guest arriva al mio host — oggi, sul kernel su cui sono effettivamente avviato, con le impostazioni di nested virt che ho davvero?". Quella domanda non sopravvive a una valutazione trimestrale, perché la risposta è cambiata due volte nelle ultime cinque settimane.

Lo swarm di 10 agenti di Zero Hunt è costruito per rispondere in continuo a questa classe di domande, non per fare lookup di CVE. Gli agenti Post-Exploit e Pivot non si fermano a "risulta installata una versione vulnerabile del pacchetto": stabiliscono un punto d'appoggio e verificano fin dove arriva, che è la differenza tra uno scanner che dichiara un'esposizione e la prova che un confine ha tenuto o no. Gli exploit sono generati per singolo target da un LLM locale invece di essere prelevati da un database pubblico, quindi una sessione di validazione riflette il vostro kernel, la vostra configurazione dell'hypervisor e i vostri permessi su /dev/kvm, non le assunzioni di un PoC generico. Ogni skill passa dall'AI Gym — 142+ skill auto-evolutive, testate contro Vulhub, NYU CTF Bench e Vulhub-Bench — prima di avvicinarsi a un ambiente di produzione, e le campagne change-triggered rieseguono la validazione quando la configurazione di un host si muove, non tre mesi dopo.

C'è un punto autoreferenziale che vale la pena dire apertamente, visto che l'articolo parla di confini di isolamento che cedono. Zero Hunt esegue ogni exploit dentro un container Docker effimero con hardening gVisor opzionale, su un host appliance irrobustito, interamente on-premise: nessuna callback cloud, nessuna API LLM esterna, air-gap supportato. Una piattaforma di validazione che esegue codice offensivo deve rispondere alla stessa domanda che pone a voi, ed eseguirlo sull'infrastruttura condivisa di qualcun altro non è una risposta.

Il versante compliance discende dalla stessa evidenza. Livello di patch del kernel, esposizione della nested virt e permessi sui device sono controlli simultanei di ISO 27001, NIS2 e CIS; Zero Hunt mappa ogni finding e ogni remediation su tutti i 32 framework e firma il record in ECDSA al momento della scrittura. È ciò che trasforma lo scambio riportato sopra da discussione a cronologia: quali host erano su quale kernel in quale data, e quando il confine è stato davvero chiuso.

Se il vostro parco macchine include KVM multi-tenant, o semplicemente molti server Linux con /dev/kvm permissivo, scriveteci — oppure guardate come la piattaforma valida la sfruttabilità invece di dedurla.