Detaillierter Proof-of-Concept und technische Analyse für CVE-2026-2441, eine Chrome CSS Use-After-Free-Sicherheitslücke, die über präparierte HTML-Seiten Remote-Code-Ausführung im sandboxed Renderer ermöglicht.
CVSS 8.8 (Hoch) | Aktiv in freier Wildbahn ausgenutzt | Renderer-RCE (Sandboxed)
Eine Use-After-Free-Sicherheitslücke in Google Chromes Blink-CSS-Engine, die es einem entfernten Angreifer ermöglicht, über eine präparierte HTML-Seite beliebigen Code innerhalb der Browser-Sandbox auszuführen.
| Feld | Wert |
|---|---|
| CVE | CVE-2026-2441 |
| CVSS | 8.8 (Hoch) |
| Typ | Use-After-Free (CWE-416) |
| Komponente | Blink CSS — CSSFontFeatureValuesMap |
| Quelldatei | third_party/blink/renderer/core/css/css_font_feature_values_map.cc |
| Fix-Commit | 63f3cb4864c64c677cd60c76c8cb49d37d08319c |
| Melder | Shaheen Fazim (2026-02-11) |
| Patch-Datum | 2026-02-13 |
| In freier Wildbahn | Ja — Google hat aktive Ausnutzung bestätigt |
| Plattform | Verwundbar | Behebung |
|---|---|---|
| Windows / macOS (Stabil) | < 145.0.7632.75 | >= 145.0.7632.75 |
| Linux (Stabil) | < 144.0.7559.75 | >= 144.0.7559.75 |
| Windows / macOS (Erweitertes Stable) | < 144.0.7559.177 | >= 144.0.7559.177 |
| Chromium-basierte Browser (Edge, Brave, Opera, Vivaldi) | Herstellerhinweis prüfen | Unterschiedlich |
FontFeatureValuesMapIterationSource speicherte einen rohen Zeiger (const FontFeatureAliases* aliases_) auf die interne FontFeatureAliases-HashMap. Wenn die Map während der Iteration durch set() oder delete() mutiert wird, wird die HashMap neu gehasht – neuer Speicher wird allokiert und der alte freigegeben. Der rohe Zeiger wird hängend, und der nächste Aufruf von FetchNextItem() liest aus freigegebenem Speicher.
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
Der Fix ersetzt den rohen Zeiger durch eine Tiefenkopie der HashMap. Selbst wenn die ursprüngliche Map neu hasht, arbeitet der Iterator auf seiner eigenen Kopie, wodurch der hängende Zeiger verhindert wird.
poc.html in einer verwundbaren Chrome-Version (< 145.0.7632.75)| Chrome-Version | Erwartetes Verhalten |
|---|---|
| < 145.0.7632.75 (ungepatcht) | Renderer-Absturz — STATUS_ACCESS_VIOLATION (Windows) oder SIGSEGV (Linux/macOS). Chrome zeigt Fehler „Seite kann nicht geöffnet werden“. |
| >= 145.0.7632.75 (gepatcht) | Kein Absturz — PoC läuft vollständig durch, alle Einträge werden normal gelesen. |
Der PoC ist so organisiert, dass die Ausnutzungskette in einer klaren und reproduzierbaren Reihenfolge gezeigt wird. Der erste Teil erstellt das verwundbare Blink/CSS-Objekt, der zweite Teil löst die Iterator-Invalidierung aus, und der letzte Teil simuliert die Post-Exploitation-Effekte in einer sicheren akademischen Umgebung.
Wichtiger Hinweis: Der UAF-Auslöser wird über echte, vom Browser bereitgestellte CSS/JavaScript-APIs implementiert. Der Heap-Leak und das Exfiltrations-Dashboard sind absichtlich kontrolliert/simuliert, um die Veröffentlichung eines einsatzbereiten Chromium-Exploits zu vermeiden.
Das Payload definiert zunächst eine CSS-@font-feature-values-Regel:
@font-feature-values VulnFont {
@styleset {
a0: 1; a1: 2; a2: 3; a3: 4;
a4: 5; a5: 6; a6: 7; a7: 8;
}
}
Diese Regel veranlasst Blink, eine interne CSSFontFeatureValuesMap zu erstellen. In der verwundbaren Implementierung ist die Iteration über diese Map unsicher, da der Iterator einen rohen Zeiger auf den internen FontFeatureAliases-Speicher behält.
Das JavaScript-Payload ruft später die Map aus dem Stylesheet ab:
const sheet = document.getElementById("uaf-style").sheet;
const rule = sheet.cssRules[0];
const map = rule && rule.styleset;
An diesem Punkt hat die vom Angreifer kontrollierte Seite ein JavaScript-Handle zu einem Browserobjekt, dessen interne C++-Implementierung anfällig für Iterator-Invalidierung ist.
Der Auslöser wird nicht sofort ausgeführt. Der PoC wartet 800 ms, bevor die verwundbare Sequenz läuft:
setTimeout(triggerUAF, 800);
Diese Verzögerung dient der Demo-Stabilität. Sie ermöglicht, dass die Seite und das gefälschte Bankverifikationsformular gerendert werden, bevor der Speicherkorruptionsauslöser läuft. In einem echten Drive-by-Szenario könnte derselbe Auslöser auch automatisch gestartet werden, sobald die bösartige Seite geladen wird.
Die Kern-UAF-Primitive ist die folgende Schleife:
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++;
}
Die Sicherheitslücke wird durch die Reihenfolge der Operationen ausgelöst:
1. map.entries() erstellt einen Iterator über CSSFontFeatureValuesMap.
2. In der verwundbaren Blink-Implementierung referenziert der Iterator den internen Map-Speicher.
3. it.next() liest den nächsten Eintrag über diesen Iterator.
4. map.delete(key) mutiert dieselbe Map, während der Iterator noch aktiv ist.
5. map.set(...) fügt einen neuen Eintrag ein und kann die zugrundeliegende HashMap zwingen, neu zu hashen.
6. Durch das Neu-Hashen kann der alte Speicher freigegeben oder verschoben werden.
7. Der Iterator kann immer noch auf den alten Speicher verweisen.
8. Der nächste Iterator-Zugriff kann daher zu einem Use-After-Free werden.
Die ursprüngliche aggressive Strategie verwendete eine größere Heap-Spray-ähnliche Schleife, die beispielsweise nach jeder Löschung hunderte von Elementen, wie 512 neue Einträge, einfügte. Das erzeugt einen stärkeren Heap-Druck und macht eine Neubelegung/Wiederverwendung wahrscheinlicher.
Für die Live-Demo wurde dies auf nur 4 Mutationsschritte reduziert:
while (step < 4) {
// Iterator-Lesen + Löschen + Setzen
}
Der Grund ist praktischer und pädagogischer Natur: Das 512-Elemente-Spray ließ den Renderer oft sofort abstürzen. Ein Absturz ist nützlich, um die Verfügbarkeitsauswirkung zu demonstrieren, verhindert jedoch, dass der Rest der Demonstration den simulierten Datendiebstahl und das Angreifer-Dashboard zeigt. Die reduzierte Version demonstriert immer noch die verwundbare Iterator-Invalidierungslogik, während der Browser stabil genug für die Live-Präsentation bleibt.
Ein echter einsatzbereiter UAF-Exploit würde normalerweise eine Speicheroffenlegungsprimitive erfordern, um Heap- oder V8-Zeiger preiszugeben und ASLR zu umgehen. Die Demo implementiert kein echtes beliebiges Speicherlesen. Stattdessen generiert sie eine Heap-ähnliche Adresse aus einem vordefinierten statischen Bereich:
const base = 0x55a000000000 + Math.floor(Math.random() * 0x200000);
heapLeak = {
raw: "0x" + base.toString(16).toUpperCase(),
base: "0x" + (base & ~0xfff).toString(16).toUpperCase()
};
Dieser Wert ist ein simulierter Heap-Leak: