Neutron, nuestro motor de IA, obtuvo un 97.1% en el benchmark CyberGym de UC Berkeley. Más información

Análisis a fondo

Análisis a fondo

Escape del sandbox de Wasmtime por compilación incorrecta (CVE-2026-115889)

CVE-2026-115889 es un escape crítico del sandbox de Wasmtime: Cranelift empaqueta índices en campos de 24 bits que un módulo manipulado desborda. Corregido en 36.0.19 y 43.0.0.

Resumen

  • Wasmtime compila WebAssembly con Cranelift, su propio backend compilador. En la representación intermedia del compilador, cada registro de valor empaquetaba un índice de instrucción, bloque o valor en 24 bits de una palabra de 64 bits, con una protección que solo provoca un panic en compilaciones de depuración.
  • Una función Wasm que lleve uno de esos índices de instrucción, bloque o valor por encima de 2^24 - 1, aproximadamente 16.7 millones, desborda los campos de 24 bits en compilaciones de producción. Puede ser una función realmente enorme o una diseñada para inflar el recuento. El valor pasa a referenciar la instrucción, bloque o valor incorrecto. El compilador genera código máquina a partir de esos datos corruptos. El resultado puede ser confusión de tipos y, con ello, un escape del sandbox.
  • Bytecode Alliance califica el fallo como crítico, CVSS v3.1 9.9, con el backend predeterminado Cranelift afectado en cualquier arquitectura. La rama principal está corregida desde Wasmtime 43.0.0 (marzo de 2026); la rama de mantenimiento 36.x recibió la adaptación en 36.0.19, publicada el 19 de octubre de 2026, el mismo día que el aviso.
  • Actualice a 36.0.19 o posterior en la rama 36.x. En la principal, 43.0.0 solo corrige CVE-2026-115889; para cerrar los tres fallos descritos, ejecute 48.0.7 o posterior, o 49.0.4 o posterior en la rama 49. No se conoce explotación en ataques reales a fecha del 24 de octubre de 2026.
  • Dos escapes complementarios del sandbox comparten la ventana de octubre. CVE-2026-115893 permite que un br_table con más de unos 256.000 destinos salte a un desplazamiento elegido por el atacante en aarch64; está corregido en 36.0.19, 48.0.7 y 49.0.4. GHSA-m9f7-9w4p-gv38 permite a un invitado mover cualquier directorio del host dentro de su sandbox mediante el renombrado de un enlace simbólico en macOS y FreeBSD; está corregido en 36.0.18, 48.0.6 y 49.0.3.

¿Estoy afectado?

Está afectado si se cumplen todas estas condiciones:

  • Ejecuta Wasmtime, o un producto que lo incorpora, con el backend compilador predeterminado Cranelift.
  • Su versión es anterior a 36.0.19 en la rama de mantenimiento 36.x, o está entre 37.0.0 y 42.x en la principal.
  • Los módulos Wasm invitados no son completamente confiables: proceden de inquilinos, usuarios u otras partes cuyo código confina en lugar de confiar en él.

La declaración de impacto del aviso es explícita: «Un motor Wasmtime con configuración predeterminada (que utilice el backend Cranelift) en cualquier arquitectura/plataforma podría sufrir un escape del sandbox desde un módulo Wasm especialmente manipulado». Los motores que solo ejecutan módulos propios y confiables no están expuestos a este error.

La ventana de avisos de octubre de 2026 incluye otros dos escapes del sandbox con intervalos afectados distintos, por lo que la tabla enumera cada fallo por separado:

Fallo Versiones afectadas Versión corregida
CVE-2026-115889, cualquier arquitectura 36.x: anterior a 36.0.19; rama principal: 37.0.0 – 42.x 36.0.19 y 43.0.0 (rama principal, marzo de 2026)
CVE-2026-115893, solo aarch64 36.x: anterior a 36.0.19; rama principal: 37.0.0 – 48.0.6 y 49.0.0 – 49.0.3 36.0.19, 48.0.7 y 49.0.4 (todas publicadas el 19 de octubre de 2026)
GHSA-m9f7-9w4p-gv38, wasmtime-wasi en macOS y FreeBSD 36.x: 36.0.17 y anteriores; rama principal: 37.0.0 – 48.0.5 y 49.0.0 – 49.0.2 36.0.18, 48.0.6 y 49.0.3 (todas publicadas el 14 de octubre de 2026)

El aviso de aarch64 indica que CVE-2026-115893 afecta a toda versión de Wasmtime compatible con aarch64, desde su compatibilidad inicial de 2020 anterior a v1.0. El aviso del sistema de archivos necesita un invitado con acceso de escritura a un directorio dentro de su sandbox y un host macOS o FreeBSD; los hosts Linux y Windows no están afectados porque su renameat no sigue un enlace simbólico con una barra final.

Compruebe qué ejecuta:

# CLI version
wasmtime --version

# Rust dependency tree
cargo tree -i wasmtime -e features | head
cargo tree --depth 1 | grep wasmtime

# Docker image check
docker run --rm <your-image> wasmtime --version

Wasmtime suele estar integrado en otros productos en lugar de ejecutarse como CLI: compruebe la versión en el árbol de dependencias de su plataforma, porque las plataformas sin servidor y de plugins suelen fijarla.

¿Cómo lo corrijo?

Actualice Wasmtime. Para cerrar los tres fallos a la vez, las versiones mínimas corregidas son 36.0.19 en la rama 36.x, 48.0.7 en la 48 y 49.0.4 en la 49:

# Rust, 36.x maintenance line
cargo update -p wasmtime --precise 36.0.19
# Rust, 48 line
cargo update -p wasmtime --precise 48.0.7
# Rust, 49 line
cargo update -p wasmtime --precise 49.0.4

# CLI install script (installs the latest release)
curl https://wasmtime.dev/install.sh -sSf | bash

La versión 43.0.0 solo corrige CVE-2026-115889. Las versiones 43.0.0 a 48.0.6 y 49.0.0 a 49.0.3 siguen conteniendo el escape de br_table en aarch64. Las versiones 43.0.0 a 48.0.5 y 49.0.0 a 49.0.2 siguen conteniendo el escape del sistema de archivos.

El aviso no enumera ninguna medida provisional. Rechazar cuerpos de función inusualmente grandes antes de compilar reduce la entrada que puede alcanzar el desbordamiento, pero no es una corrección. El aviso distingue una función diseñada para utilizar muchos valores SSA de un cuerpo muy grande, por lo que un límite de tamaño en bytes no es una barrera fiable. Considere la actualización del motor como la corrección y cualquier límite como una medida temporal.

Escanee sus repositorios Rust e imágenes de contenedor con Ostorlab para señalar todas las compilaciones que sigan fijando una versión de wasmtime anterior a 36.0.19, 48.0.7 o 49.0.4.

¿Cuál es la causa raíz?

La representación intermedia de Cranelift registra cada valor SSA en un registro ValueDataPacked. Para mantener el registro en 64 bits, el compilador empaquetaba cuatro campos en una palabra: una etiqueta de 2 bits, un tipo de 14 bits y dos campos de 24 bits llamados x e y.

El campo y contiene el índice de la instrucción que define el valor, el índice del bloque o el índice del valor original en un alias. Estos crecen con el tamaño de la función. Para resultados de instrucciones y parámetros de bloque, x solo contiene el número de salida o parámetro, que sigue siendo pequeño. Para una unión, un nodo creado por el optimizador, ambos campos contienen índices de valores. La disposición, del código afectado:

// cranelift/codegen/src/ir/dfg.rs (before the fix)
//        | tag:2 |  type:14        |    x:24       | y:24          |
//
// Inst       00     ty               inst output     inst index
// Param      01     ty               blockparam num  block index
// Alias      10     ty               0               value index
// Union      11     ty               first value     second value

La función auxiliar de empaquetado protegía el ancho del campo solo con una aserción de depuración, una comprobación que las compilaciones de producción eliminan:

// cranelift/codegen/src/ir/dfg.rs (before the fix)
fn encode_narrow_field(x: u32, bits: u8) -> u32 {
    let max = (1 << bits) - 1;
    if x == 0xffff_ffff {
        max
    } else {
        debug_assert!(
            x < max,
            "{x} does not fit into {bits} bits (must be less than {max} to \
             allow for a 0xffffffff sentinel)"
        );
        x
    }
}

Sin la aserción, encode_narrow_field devuelve el índice sin cambios. El código de empaquetado lo desplaza a su posición y lo combina con OR en la palabra sin aplicar una máscara, de modo que los bits posteriores al 24 invaden el campo vecino. Al leer el registro, cada campo se enmascara a 24 bits. Un índice de instrucción de 2^24 en y se lee como 0, una instrucción completamente distinta, y también corrompe x.

En una frase: una estructura del compilador almacenaba índices de entidades en campos demasiado estrechos, y la única comprobación de desbordamiento era una que las compilaciones de producción eliminan. Así, una función con suficientes valores corrompía la correspondencia entre valores y definiciones del compilador antes de generar código.

La consecuencia es una compilación incorrecta influida por el atacante. Un Value que declara una instrucción definidora incorrecta hereda el tipo de resultado y la semántica de esa instrucción. El compilador emite entonces código máquina donde la salida de una operación se consume como otro tipo: confusión de tipos en código compilado dentro del invitado, que rompe el sandbox que Wasmtime debe imponer.

¿Cómo se explota?

El atacante proporciona un módulo Wasm con una función que lleva los índices de instrucción, bloque o valor de Cranelift por encima de 2^24 - 1. En una compilación de producción, los índices desbordan silenciosamente y el código mal compilado, con confusión de tipos, se ejecuta con los privilegios del host. No existe prueba de concepto pública ni se conoce explotación en ataques reales a fecha del 24 de octubre de 2026. La ruta, según el aviso y el parche:

  1. El atacante puede proporcionar un módulo Wasm al motor: un inquilino que despliega código, un usuario que carga un plugin o cualquier autor de código invitado. Por eso, el vector CVSS v3.1 registra privilegios requeridos bajos.
  2. El módulo contiene una función que lleva un índice de instrucción, bloque o valor por encima de 2^24 menos 1, unos 16.7 millones. El aviso describe dos formas: un cuerpo de función Wasm muy grande o uno especialmente diseñado para utilizar muchos valores SSA.
  3. Durante la traducción a IR de Cranelift y la optimización, los índices superan los campos de 24 bits. En una compilación de producción no se produce ningún panic; los registros empaquetados pasan silenciosamente a apuntar a otros índices.
  4. El optimizador y el backend leen la correspondencia corrupta. Un valor se resuelve a una definición incorrecta, con el tipo incorrecto, y el código máquina generado realiza operaciones que el verificador nunca vio: por ejemplo, bits enteros reinterpretados como referencia o como un valor de otro tipo.
  5. Dentro del invitado, el atacante utiliza el código con confusión de tipos para leer o escribir fuera de los límites de memoria lineal de Wasm o acceder a mecanismos del host que el sandbox debería mantener inaccesibles: un escape del sandbox, ejecutado con los privilegios del proceso host.

Todo el ataque consiste en un módulo compilado. No requiere uso indebido de API del host, configuración incorrecta del motor ni una segunda vulnerabilidad.

¿Cómo pasa un módulo manipulado del desbordamiento al escape?

flowchart LR A["El atacante proporciona una función Wasm
con índices superiores a 2^24 - 1"] --> B["Cranelift empaqueta índices en
campos de 24 bits de un u64"] B --> C["Compilación de producción: no hay
control de depuración, desbordamiento silencioso"] C --> D["Los registros de valores apuntan
a instrucciones o bloques incorrectos"] D --> E["El backend emite código con
tipos de valor corruptos"] E --> F["La confusión de tipos del invitado
rompe el sandbox Wasm"]

La comprobación fallida está en C: la única protección contra el desbordamiento no existe en compilaciones de producción.

¿Qué puede hacer un atacante?

Escapar del sandbox WebAssembly y ejecutar código con los privilegios del proceso host. El modelo de seguridad de Wasmtime considera no confiable el código invitado: un escape significa que el invitado accede directamente a memoria, API y capacidades del proceso host. El vector CVSS v3.1 AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H puntúa confidencialidad, integridad y disponibilidad como altas con cambio de alcance, coherente con un invitado que compromete su host.

Las condiciones que necesita el ataque:

  • Wasmtime con el backend predeterminado Cranelift, cualquier arquitectura y una compilación de producción.
  • Una versión afectada: anterior a 36.0.19 en la rama 36.x, o de 37.0.0 a 42.x.
  • El motor debe compilar realmente el código del atacante, por lo que los motores que solo precompilan módulos confiables o utilizan otro backend quedan fuera de la ruta demostrada.

¿Cuáles son los otros dos escapes del sandbox?

La misma ventana de avisos de octubre incluye dos escapes más de Wasmtime, publicados el 19 y el 14 de octubre de 2026, ambos con versiones corregidas distintas de las de CVE-2026-115889.

CVE-2026-115893: ¿cómo escapa del sandbox un br_table grande en aarch64?

En el backend aarch64, un módulo Wasm con más de unos 256.000 destinos en una instrucción br_table se compila como un salto condicional con destino incorrecto. El salto puede llegar a código arbitrario en un desplazamiento controlado por el módulo, lo que según el aviso provoca escapes arbitrarios del sandbox.

Está afectada toda versión de Wasmtime compatible con aarch64, desde su compatibilidad inicial de 2020 anterior a v1.0. La corrección se publicó en 36.0.19, 48.0.7 y 49.0.4 el 19 de octubre de 2026. Las versiones de la rama principal 43.0.0 a 48.0.6 corrigen CVE-2026-115889 pero aún contienen este fallo.

GHSA-m9f7-9w4p-gv38: ¿cómo escapa del sandbox de archivos el renombrado de un enlace simbólico?

En el crate wasmtime-wasi, un invitado podía mover cualquier directorio del host accesible para el proceso dentro de su propio sandbox y después leerlo y escribir en él.

Al renombrar, wasmtime-wasi elimina la barra final de la ruta de origen para resolver su directorio padre. Después vuelve a añadirla antes de llamar a renameat. El invitado crea un enlace simbólico relativo que apunta fuera de su sandbox, por ejemplo symlink("../outside", "up"), y renombra "up/" hacia dentro del sandbox. El kernel del host sigue el enlace y mueve el directorio externo al interior. La corrección anterior GHSA-vqjp-4c8c-hfgg cambió cómo se abren las rutas, no cómo se renombran.

Los hosts macOS y FreeBSD están afectados. Linux y Windows no, porque su renameat no sigue un enlace simbólico con barra final. El invitado necesita acceso de escritura a un directorio dentro de su sandbox. La corrección se publicó en 36.0.18, 48.0.6 y 49.0.3 el 14 de octubre de 2026. No se conoce ninguna medida provisional y no se ha asignado CVE.

Ninguno de los dos avisos complementarios comunica explotación en ataques reales.

¿Por qué funciona la corrección?

La corrección elimina los campos estrechos: x e y pasan a ser índices u32 completos, de modo que ningún tamaño de función puede hacer desbordar un índice hacia otra definición. En la rama principal, la pull request #12613 (integrada el 18 de febrero de 2026 y publicada en Wasmtime 43.0.0) sustituyó el u64 empaquetado por:

// cranelift/codegen/src/ir/dfg.rs (after the fix)
-struct ValueDataPacked(u64);
+#[repr(Rust, packed)]
+struct ValueDataPacked {
+    x: u32,
+    y: u32,
+    flags_and_type: u16,
 }

El comentario de disposición cambia de x:24 | y:24 a x:32 | y:32: ambos campos contienen ahora todo su intervalo u32, y las funciones encode_narrow_field y decode_narrow_field se eliminan porque ya no hay nada que estrechar. El registro crece de 8 a 10 bytes. Aun así, la PR midió una mejora de velocidad de compilación del 3 al 5 por ciento en sus benchmarks.

La adaptación a 36.0.19, pull request #14818, lleva el mismo cambio a la rama de mantenimiento, junto con correcciones de otros dos avisos de la misma versión: un problema de longitudes de contenido contradictorias en wasi-http (GHSA-r8xh-cgf7-392j) y el destino de salto br_table de aarch64 (CVE-2026-115893, GHSA-mghf-wjfp-p5xm).

La corrección es completa para esta clase de desbordamiento en esta estructura: los valores ya no pasan por ningún estrechamiento, por lo que ningún tamaño de función puede corromper la correspondencia. El aviso añade que Cranelift usa ahora un índice u32 completo y comprueba cuándo se supera ese límite; incluso el máximo u32 queda protegido en lugar de fallar silenciosamente.

¿Cómo compruebo si he sufrido una intrusión?

No se han publicado indicadores de compromiso para este aviso. Los escapes del sandbox por compilación incorrecta no tienen firma ni IOC publicados y no se conoce explotación. Si ejecuta un motor afectado con invitados no confiables y quiere auditarlo:

  • Revise qué módulos se compilaron y quién los proporcionó, si su plataforma registra hashes de módulos o historial de cargas por inquilino. Un módulo manipulado para este error contiene una función con un número de valores SSA inusualmente alto respecto a su tamaño en bytes.
  • Considere cualquier anomalía del invitado, lecturas fuera de límites que se manifiesten como caídas del host o comportamientos imposibles para el verificador como motivo para investigar el proceso host, no solo el invitado.
  • Si sospecha un compromiso, trate el proceso host como completamente comprometido: rote todos sus secretos y reconstruya desde un estado conocido como válido, porque el código invitado que escapa se ejecuta con privilegios del host.

Cronología

  • 18 de febrero de 2026: la pull request #12613, «Cranelift: actualizar ValueDataPacked para admitir todo el intervalo de Value», se integra en la rama principal.
  • 20 de marzo de 2026: Wasmtime 43.0.0 publica la codificación de ancho completo.
  • 14 de octubre de 2026: Bytecode Alliance publica GHSA-m9f7-9w4p-gv38, el escape del sandbox de archivos wasmtime-wasi mediante renombrados de enlaces simbólicos con barra final en macOS y FreeBSD, y las versiones 36.0.18, 48.0.6 y 49.0.3 con la corrección.
  • 19 de octubre de 2026: se reconoce al informante jaydns; Bytecode Alliance publica GHSA-897f-g5gm-fmxm y el aviso complementario GHSA-mghf-wjfp-p5xm (CVE-2026-115893, escape de br_table en aarch64), y publica 36.0.19 con ambas adaptaciones, además de 48.0.7 y 49.0.4 con la corrección de br_table.
  • 23 de octubre de 2026: se publica CVE-2026-115889.
  • 24 de octubre de 2026: este artículo. No se conoce explotación en ataques reales de ninguno de los tres fallos; ninguno figura en el catálogo CISA KEV.
  • 26 de octubre de 2026: actualización con los dos avisos complementarios, GHSA-mghf-wjfp-p5xm (CVE-2026-115893) y GHSA-m9f7-9w4p-gv38.

Preguntas frecuentes

¿Por qué la rama principal recibió la corrección meses antes del aviso?

El cambio de febrero a campos u32 completos eliminó un límite del tamaño del cuerpo de función. Fue una mejora de corrección funcional, no un parche de seguridad publicado como tal. El impacto de seguridad de la codificación anterior se comunicó después, y el aviso de octubre recoge ambos hechos: v43 y posteriores no son susceptibles, y 36.0.19 adapta el cambio a la rama de mantenimiento.

¿Necesito 16.7 millones de instrucciones en una función para estar en riesgo?

No. El umbral afecta a los índices empaquetados en el registro de valor: índices de instrucciones, bloques y valores, unos 16.7 millones de cada uno. Los índices de valor crecen con el número de valores SSA, y el aviso indica que una función puede estar «especialmente diseñada para utilizar muchos valores SSA». Por tanto, un límite de tamaño en bytes no es una barrera fiable. Actualice.

¿Están afectadas las compilaciones de depuración?

No. En esas compilaciones se activa debug_assert! y la compilación aborta en lugar de corromper la correspondencia. Los motores de producción utilizan compilaciones de producción, por eso el aviso se refiere a su comportamiento.

¿Están afectados el backend Winch o la ruta de módulos precompilados?

El aviso limita el fallo al backend Cranelift, el predeterminado. Los motores que ejecutan módulos precompilados omiten el paso de compilación donde ocurre la corrupción, pero deben actualizar igualmente porque el aviso no descarta otras rutas de backend o precompilación.

¿Qué alcance de despliegue tiene Wasmtime?

El crate wasmtime registra 41.373.089 descargas históricas y 11.809.615 en los últimos 90 días en crates.io, y es el motor de plataformas sin servidor y de plugins que confinan código de terceros: exactamente el límite que un atacante cruza con este error.

¿Afecta a plataformas basadas en wasmtime que establecen límites de módulos?

Sí, si el motor integrado es una versión afectada. Los límites reducen la entrada, pero no forman parte de la corrección; actualice el motor en lugar de confiar en ellos.

¿Actualizar a 43.0.0 o posterior resuelve los tres fallos?

No. En la rama principal, CVE-2026-115889 está corregido desde 43.0.0. El escape del sistema de archivos se corrige primero en 48.0.6 (49.0.3 en la rama 49), y el escape de br_table en aarch64 primero en 48.0.7 (49.0.4 en la rama 49). En la rama de mantenimiento 36.x, 36.0.19 resuelve los tres.

Referencias

  • Aviso de Bytecode Alliance GHSA-897f-g5gm-fmxm: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-897f-g5gm-fmxm (consultado el 24 de octubre de 2026)
  • Aviso de Bytecode Alliance GHSA-mghf-wjfp-p5xm, CVE-2026-115893, escape del sandbox de br_table en aarch64, crítico, CVSS v3.1 9.9, corregido en 36.0.19, 48.0.7 y 49.0.4: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-mghf-wjfp-p5xm (consultado el 26 de octubre de 2026)
  • Aviso de Bytecode Alliance GHSA-m9f7-9w4p-gv38, escape del sandbox de archivos wasmtime-wasi mediante renombrados de enlaces simbólicos con barra final en macOS y FreeBSD, alto, sin CVE, corregido en 36.0.18, 48.0.6 y 49.0.3: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-m9f7-9w4p-gv38 (consultado el 26 de octubre de 2026)
  • Registro CVE-2026-115889 (CVSS v3.1 9.9, CWE-197 y CWE-843, intervalos afectados): https://cveawg.mitre.org/api/cve/CVE-2026-115889 (consultado el 24 de octubre de 2026)
  • Corrección de la rama principal, pull request #12613, integrada el 18 de febrero de 2026: https://github.com/bytecodealliance/wasmtime/pull/12613 (consultado el 24 de octubre de 2026)
  • Aviso de Bytecode Alliance GHSA-r8xh-cgf7-392j, longitudes de contenido contradictorias de wasi-http, corregido en la misma adaptación 36.0.19: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-r8xh-cgf7-392j (consultado el 27 de octubre de 2026)
  • Adaptación a 36.x, pull request #14818: https://github.com/bytecodealliance/wasmtime/pull/14818 (consultado el 24 de octubre de 2026)
  • Versión Wasmtime 36.0.19 (publicada el 19 de octubre de 2026): https://github.com/bytecodealliance/wasmtime/releases/tag/v36.0.19 (consultado el 24 de octubre de 2026)
  • Versión Wasmtime 43.0.0 (publicada el 20 de marzo de 2026): https://github.com/bytecodealliance/wasmtime/releases/tag/v43.0.0 (consultado el 24 de octubre de 2026)
  • dfg.rs vulnerable de la rama 36.x (codificación ValueDataPacked): https://github.com/bytecodealliance/wasmtime/blob/v36.0.18/cranelift/codegen/src/ir/dfg.rs (consultado el 24 de octubre de 2026)
  • Recuentos de descargas de wasmtime en crates.io (41.373.089 históricas, 11.809.615 en los últimos 90 días): https://crates.io/api/v1/crates/wasmtime (consultado el 24 de octubre de 2026)