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

Omisión de la protección billion laughs de libexpat, corregida en 2.10.0 (CVE-2026-115506)

libexpat hasta 2.9.0 detecta demasiado tarde algunas variantes de billion laughs; 2.10.0 corrige esto y tres fallas más del analizador.

TL;DR

  • libexpat 2.10.0, publicada el 2 de noviembre de 2026, corrige cuatro fallas de seguridad. La mayor es CVE-2026-115506, una variante de billion laughs que la protección existente detecta demasiado tarde. Las otras tres son un desbordamiento de entero con signo en storeAtts (CVE-2026-118370), una denegación de servicio mediante XML_ParserReset (CVE-2026-113351) y una desreferencia de NULL con entrada vacía de subanalizador (CVE-2026-117610).
  • Todas las versiones hasta 2.9.0, la versión anterior, están afectadas. Los registros CVE puntúan CVE-2026-115506 8.7 High, CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N, y las otras tres 8.2 High.
  • No se conoce explotación y ninguno de los cuatro CVE figura en el catálogo KEV de CISA al 2 de noviembre de 2026. El generador público de payloads de Hanno Böck produjo las variantes que revelaron el hueco de detección.
  • Actualice a 2.10.0, o espere el backport de su distribución. Quien incluya una copia de Expat debe actualizar el árbol incluido, como pide el mantenedor.

¿Estoy afectado?

Deben cumplirse todas estas condiciones:

  • Ejecuta libexpat 2.9.0 o anterior, o software que la incluye. El módulo xml.parsers.expat de Python enlaza Expat, y el repositorio de CPython incluye su propia copia en Modules/expat.
  • Su software analiza documentos XML controlados por el atacante, o usa una biblioteca que lo hace.
  • Cada falla fija la barrera de forma distinta: CVE-2026-115506 solo necesita un documento de tamaño moderado, mientras que CVE-2026-118370 necesita un documento de varios gigabytes cuya etiqueta inicial lleve más de 2^30 atributos.

El mantenedor describe Expat como «packaged everywhere» y la sitúa entre los dos analizadores XML libres en C más usados. Los paquetes de las distribuciones la llevan como biblioteca en tiempo de ejecución, y las pilas Python, Perl y Apache se apoyan en ella. El rastreador de seguridad de Debian ya lista el paquete expat como vulnerable en todas las suites compatibles.

Cronología de la versión de seguridad libexpat 2.10.0: primer CVE publicado el 20 de octubre, correcciones fusionadas del 23 al 29 de octubre, y versión publicada el 2 de noviembre de 2026.
Figura 1: Pasaron 13 días desde el primer CVE publicado hasta la versión que corrige los cuatro. Fuente: registros CVE, peticiones de extracción de libexpat y la versión 2.10.0, comprobado el 2 de noviembre de 2026.

Rama Versiones afectadas Versión corregida
libexpat estable 2.7.2 hasta 2.9.0 (CVE-2026-113351); 1.95.2 hasta 2.9.0 (las otras tres) 2.10.0 (2026-11-02)

Compruebe la versión que enlaza. Debian y Ubuntu la entregan como libexpat1; el intérprete de Python informa de la versión de la copia que enlaza:

dpkg -l libexpat1
python3 -c "import xml.parsers.expat as e; print(e.EXPAT_VERSION)"

Una salida como expat_2.9.0 indica la versión afectada. Al 2 de noviembre de 2026, el rastreador de seguridad de Debian marca el paquete expat vulnerable y sin corregir en bookworm, trixie, forky y sid.

¿Cómo lo corrijo?

Actualice a libexpat 2.10.0.

  1. Si instala desde su distribución, espere su actualización de seguridad y aplíquela al publicarse; el aviso del mantenedor señala que la versión deja las cuatro correcciones disponibles para backport.
  2. Si compila desde código fuente o incluye una copia, actualice el árbol incluido al tag R_2_10_0 y recompile cada consumidor. El aviso del mantenedor pide a los empaquetadores y a quienes incluyen Expat que pasen a 2.10.0.
  3. Si mantiene un backport, la petición de extracción #1439 lleva el complemento que el mantenedor marca como esencial al retroportar la corrección de billion laughs.

Actualizar sigue siendo la solución principal. Expat permite desactivar las entidades generales al compilar con XML_GE, disponible desde 2.6.0. En ejecución, XML_SetDefaultHandler suprime la expansión de entidades generales definidas internamente en texto, pero su comportamiento y disponibilidad dependen de la aplicación o del binding. No son un interruptor universal en ejecución para aplicaciones desplegadas. Las aplicaciones que no necesitan DTD pueden rechazar documentos con DTD antes del análisis, rechazando también documentos legítimos. Consulte la documentación de la API de Expat.

¿Cuál es la causa raíz?

Las cuatro fallas están en expat/lib/xmlparse.c, el núcleo del analizador. Esta sección cita el código vulnerable en el tag 2.9.0.

CVE-2026-115506: la comprobación de amplificación se ejecuta después del trabajo de expansión. Expat cuenta bytes directos e indirectos por token mientras analiza, y rechaza cuando la razón supera un umbral. La función de comprobación es:

static XML_Bool
accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
                        const char *after, int source_line,
                        enum XML_Account account) {
  ...
  *additionTarget += bytesMore;
  const XmlBigCount countBytesOutput
      = rootParser->m_accounting.countBytesDirect
        + rootParser->m_accounting.countBytesIndirect;
  const float amplificationFactor
      = accountingGetCurrentAmplification(rootParser);
  const XML_Bool tolerated
      = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes)
        || (amplificationFactor
            <= rootParser->m_accounting.maximumAmplificationFactor);
  ...
  return tolerated;
}

La función se llama desde las rutas de procesamiento de tokens, lo que significa que Expat ya expandió el contenido de la entidad de un token para cuando se actualizan los contadores. El fuzzer lololol de Hanno Böck encontró combinaciones de parámetros en las que el analizador ejecuta decenas de segundos de expansión antes de que la razón acumulada cruce el umbral. La corrección, petición de extracción #1433, añade un campo expandedSize a cada entidad y precalcula hasta dónde se expandirá una entidad antes de empezar la expansión. El rechazo ocurre entonces de entrada, y la petición de extracción #1439 extiende el análisis a secciones CDATA, comentarios e instrucciones de proceso dentro de los valores de entidad.

CVE-2026-118370: dos contadores con signo en storeAtts se desbordan. En 2.9.0 la función declara:

static enum XML_Error
storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
          TAG_NAME *tagNamePtr, BINDING **bindingsPtr,
          enum XML_Account account) {
  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
  int attIndex = 0;
  XML_Char *uri;
  int nPrefixes = 0;

Ambos contadores int dan la vuelta en 2^31 atributos. Una etiqueta inicial manipulada con más de 2^30 atributos hace que la aritmética escriba fuera de límites y corrompa el montículo. La corrección, petición de extracción #1450, cambia ambos a size_t y añade comprobaciones explícitas INT_MAX donde los valores se almacenan en campos enteros del analizador.

CVE-2026-113351: XML_ParserReset nunca compensa el rastreador de asignaciones. Expat 2.7.2 añadió un rastreador de montículo que tolera asignaciones proporcionales al tamaño del documento. Reiniciar un analizador para reutilizarlo conserva la memoria agrupada, pero el total bytesAllocated del rastreador conserva las asignaciones del documento anterior. Un documento ordinario posterior sobre el mismo analizador puede entonces rechazarse con XML_ERROR_NO_MEMORY. La corrección, petición de extracción #1431, registra bytesAllocatedWhenLastReset y lo resta, de modo que cada documento parte de cero.

CVE-2026-117610: falta un caso en el switch. externalEntityInitProcessor2 en 2.9.0 maneja XML_TOK_BOM, XML_TOK_PARTIAL y otros, pero no tiene caso XML_TOK_NONE. Un primer análisis vacío y no final sobre un subanalizador de entidad externa continúa hacia externalEntityInitProcessor3, y un análisis posterior se bloquea. La petición de extracción #1444 añade el caso y devuelve XML_ERROR_NONE limpiamente.

¿Cómo se explota?

No se ha publicado un exploit completo para ninguna de las cuatro. Las entradas de ataque provienen de los registros CVE y del aviso del mantenedor.

  1. El atacante envía un documento XML de tamaño moderado con una DTD grande y expansiones de entidades anidadas. El fuzzer público calibra esos documentos para mantenerse bajo el umbral de detección el mayor tiempo posible, y el análisis consume decenas de segundos de CPU antes de que accountingDiffTolerated lo rechace (CVE-2026-115506).
  2. El atacante envía un documento de varios gigabytes cuya única etiqueta inicial lleva más de 2^30 atributos. Los contadores int de storeAtts dan la vuelta y el bucle de copia escribe fuera de sus búferes (CVE-2026-118370).
  3. El atacante analiza un documento grande en un analizador que la aplicación reutiliza mediante XML_ParserReset, y luego un segundo documento ordinario falla con XML_ERROR_NO_MEMORY hasta recrear el analizador (CVE-2026-113351).
  4. El atacante controla la fragmentación del flujo de una entidad externa y lo inicia con un fragmento vacío no final, bloqueando el proceso en el siguiente análisis (CVE-2026-117610).

El generador que produce los documentos de ataque del paso 1 es público: github.com/hannob/lololol.

¿Cómo fluye el ataque?

En una frase: un documento manipulado con un árbol de entidades profundo se expande más rápido de lo que la comprobación de amplificación nota, el analizador quema CPU antes de rechazar, o los contadores de atributos dan la vuelta hacia una escritura fuera de límites.

flowchart LR A["Documento XML controlado
por el atacante"] --> B["La expansión de entidades
añade bytes indirectos"] B --> C["La comprobación de amplificación se ejecuta
solo después de cada token"] C --> D["El trabajo de expansión ocurre
antes del rechazo"] D --> E["Decenas de segundos de CPU
por análisis"]

¿Qué puede hacer un atacante?

Los impactos documentados son denegación de servicio del proceso de análisis y, para CVE-2026-118370, escrituras fuera de límites en el montículo:

  • El atacante no necesita privilegios ni interacción más allá de entregar un documento a un sistema que lo analice. Los cuatro vectores CVSS v4.0 registran un vector de ataque de red.
  • CVE-2026-115506 y CVE-2026-113351 son fallas de denegación de servicio. Los registros CVE las describen así, y el aviso del mantenedor no evalúa ningún impacto de ejecución de código.
  • CVE-2026-118370 es corrupción de memoria con un requisito exigente: un documento de varios gigabytes con más de 2^30 atributos en una sola etiqueta inicial. El registro CVE se queda en «bloquear aplicaciones o corromper la memoria del montículo», y ninguna fuente demuestra ejecución de código.
  • CVE-2026-117610 bloquea el proceso cuando el atacante controla la fragmentación del flujo, requisito que el registro CVE señala.

¿Por qué funciona el parche?

Las cuatro peticiones de extracción cierran los cuatro mecanismos.

Para CVE-2026-115506, la petición #1433 precalcula el expandedSize de cada entidad antes de que la expansión comience, y la comprobación de amplificación consulta el tamaño proyectado en lugar de esperar a los contadores de bytes:

  /* Indirect accounting bytes charged when this entity is expanded: 1 per text
     character, plus each reference's "&name;"/"%name;" token length and its own
     expansion. ... */
  XmlBigCount expandedSize;

Para CVE-2026-118370, la petición #1450 cambia los dos contadores y acota los almacenamientos:

-  int attIndex = 0;
+  size_t attIndex = 0;
   XML_Char *uri;
-  int nPrefixes = 0;
+  size_t nPrefixes = 0;

Para CVE-2026-113351, la petición #1431 añade la marca de reinicio:

+  size_t bytesAllocatedWhenLastReset;

y XML_ParserReset ahora registra el total de asignaciones al reiniciar, de modo que la comprobación de tolerancia del siguiente documento resta lo que el anterior dejó.

Para CVE-2026-117610, la petición #1444 añade el caso que faltaba:

+  case XML_TOK_NONE:
+    if (! parser->m_parsingStatus.finalBuffer) {
+      *endPtr = start;
+    }
+    return XML_ERROR_NONE;

El mantenedor señala que la versión cierra 12 fallas de seguridad en total contra 20 hallazgos nuevos en el periodo, con 13 problemas de seguridad conocidos aún sin corregir al publicarse. Las cuatro correcciones son completas para los mecanismos que abordan; si quedan más variantes de billion laughs es exactamente lo que el fuzzer sigue probando.

¿Cómo compruebo si fui comprometido?

Ninguna de las cuatro fallas filtra datos por diseño, así que las comprobaciones se centran en anomalías de disponibilidad.

  1. Revise los registros de analizadores XML con entradas no confiables: XML_ERROR_NO_MEMORY puede justificar investigar CVE-2026-113351; el consumo prolongado de CPU durante el análisis y los errores XML_ERROR_AMPLIFICATION_LIMIT_BREACH pueden justificar investigar CVE-2026-115506. Estas observaciones no demuestran por sí solas una explotación.
  2. Busque peticiones que entreguen cargas XML anormalmente grandes a servicios que las analizan, incluido cualquier envío de varios gigabytes (CVE-2026-118370).
  3. Para CVE-2026-117610, revise los volcados de bloqueo en las rutas de subanalizador de entidad externa.

Un documento billion laughs que Expat rechazó no deja rastro más allá del error de rechazo, y un rechazo no significa que el servicio se degradara.

Cronología

  • 2026-10-20: Se publica CVE-2026-113351 para la denegación de servicio de XML_ParserReset, reportada por Pu Cheng.
  • 2026-10-22: Se publica CVE-2026-115506 para el hueco de detección de billion laughs, tras el hallazgo de variantes por el fuzzer de Hanno Böck.
  • 2026-10-23: Se fusiona la corrección de XML_ParserReset, petición de extracción #1431.
  • 2026-10-26: Se fusiona el complemento de billion laughs, petición de extracción #1439.
  • 2026-10-28: Se publican CVE-2026-117610 y CVE-2026-118370.
  • 2026-10-29: Se fusionan las últimas correcciones, peticiones de extracción #1444 y #1450.
  • 2026-11-02: Se publica libexpat 2.10.0 con las cuatro correcciones.

Preguntas frecuentes

¿Cuál de las cuatro debo priorizar?

CVE-2026-115506. Su entrada es un documento de tamaño moderado que cualquier punto de entrada XML acepta, y su costo son decenas de segundos de CPU por análisis. CVE-2026-118370 es corrupción de memoria, pero su requisito de varios gigabytes la hace mucho más difícil de entregar.

¿Afecta a las aplicaciones Python?

Afecta a cualquier proceso que enlaze un Expat afectado, incluido Python mediante xml.parsers.expat. El intérprete informa de la versión enlazada con EXPAT_VERSION. En las construcciones de Python enlazadas al paquete del sistema, actualizar el libexpat1 del sistema actualiza el analizador.

¿Se explotó alguna en el mundo real?

No se reporta explotación de ninguna de las cuatro, y ninguna figura en el catálogo KEV de CISA al 2 de noviembre de 2026. El fuzzer público demuestra el ataque, no un uso real.

¿Por qué un piso 2.7.2 en una falla y ninguno en las otras?

El rastreador de asignaciones que CVE-2026-113351 rompe se añadió en 2.7.2, así que esa falla no puede existir antes. Las otras tres preceden a sus protecciones o viven en rutas presentes antes de 2.0, de ahí el «hasta 2.9.0» de los registros.

¿Puedo simplemente desactivar la expansión de entidades?

Los controles de compilación y ejecución difieren: XML_GE controla las entidades generales al compilar, mientras XML_SetDefaultHandler cambia su expansión interna en texto. Compruebe qué expone la aplicación o el binding y pruebe la compatibilidad; actualizar a 2.10.0 sigue siendo la solución principal.

¿Quién encontró y corrigió estas fallas?

Hanno Böck encontró las variantes de billion laughs con su fuzzer, y Pu Cheng reportó la falla de reinicio. Shani Yosef aportó la idea clave de la corrección antes de la expansión, como acreditan el aviso de la versión y los registros CVE.

Encuentre dónde se ejecuta Expat en su entorno con Ostorlab

Los escaneos de dependencias de Ostorlab construyen el inventario de los paquetes que instalan sus servicios e imágenes de contenedor, para encontrar cada despliegue con un libexpat afectado. Ejecute un escaneo con Ostorlab para confirmar la actualización 2.10.0 o retroportada donde importa.

Referencias

  • Aviso de publicación del mantenedor de Expat, «Expat 2.10.0 released, fixes four vulnerabilities», publicado el 2 de noviembre de 2026, con la explicación del ataque, los créditos y la nota de retroportado para la petición de extracción #1439: https://blog.hartwork.org/posts/expat-2-10-0-released/ (comprobado el 2 de noviembre de 2026)
  • Anuncio de publicación en oss-security, «libexpat 2.10.0 fixes two vulnerabilities», publicado el 2 de noviembre de 2026, listando los cuatro CVE, las entradas del registro de cambios y las peticiones de extracción correctivas: https://www.openwall.com/lists/oss-security/2026/11/02/6 (comprobado el 2 de noviembre de 2026)
  • Página de la versión Expat 2.10.0 en GitHub, publicada el 2 de noviembre de 2026: https://github.com/libexpat/libexpat/releases/tag/R_2_10_0 (comprobado el 2 de noviembre de 2026)
  • Registro CVE-2026-115506, publicado el 22 de octubre de 2026, CVSS v4.0 8.7 High y CVSS v3.1 7.5 High: https://www.cve.org/CVERecord?id=CVE-2026-115506 (comprobado el 2 de noviembre de 2026)
  • Registro CVE-2026-113351, publicado el 20 de octubre de 2026, CVSS v4.0 8.2 High, CVSS v3.1 5.9 Medium: https://www.cve.org/CVERecord?id=CVE-2026-113351 (comprobado el 2 de noviembre de 2026)
  • Registro CVE-2026-117610, publicado el 28 de octubre de 2026, CVSS v4.0 8.2 High, CVSS v3.1 5.9 Medium: https://www.cve.org/CVERecord?id=CVE-2026-117610 (comprobado el 2 de noviembre de 2026)
  • Registro CVE-2026-118370, publicado el 28 de octubre de 2026, CVSS v4.0 8.2 High, CVSS v3.1 5.9 Medium: https://www.cve.org/CVERecord?id=CVE-2026-118370 (comprobado el 2 de noviembre de 2026)
  • Petición de extracción correctiva #1431 para CVE-2026-113351, fusionada el 23 de octubre de 2026: https://github.com/libexpat/libexpat/pull/1431 (comprobado el 2 de noviembre de 2026)
  • Petición de extracción correctiva #1433 para CVE-2026-115506, el tamaño de expansión precalculado, y su complemento #1439 para CDATA, comentarios e instrucciones de proceso: https://github.com/libexpat/libexpat/pull/1433 (comprobado el 2 de noviembre de 2026)
  • Petición de extracción correctiva #1444 para CVE-2026-117610, fusionada el 29 de octubre de 2026: https://github.com/libexpat/libexpat/pull/1444 (comprobado el 2 de noviembre de 2026)
  • Petición de extracción correctiva #1450 para CVE-2026-118370, fusionada el 29 de octubre de 2026: https://github.com/libexpat/libexpat/pull/1450 (comprobado el 2 de noviembre de 2026)
  • Generador de payloads de Hanno Böck para variantes de billion laughs, enlazado desde el registro de cambios de Expat: https://github.com/hannob/lololol (comprobado el 2 de noviembre de 2026)
  • Página principal del proyecto Expat, que describe Expat como empaquetada en todas partes y lista usuarios, bindings y wrappers: https://libexpat.github.io/ (comprobado el 2 de noviembre de 2026)
  • Código fuente vulnerable de expat/lib/xmlparse.c en el tag R_2_9_0, para el código citado: https://github.com/libexpat/libexpat/blob/R_2_9_0/expat/lib/xmlparse.c (comprobado el 2 de noviembre de 2026)
  • Rastreador de seguridad de Debian para CVE-2026-115506, que marca expat vulnerable y sin corregir en bookworm, trixie, forky y sid al 2 de noviembre de 2026: https://security-tracker.debian.org/tracker/CVE-2026-115506 (comprobado el 2 de noviembre de 2026)
  • Página de seguridad de Ubuntu para CVE-2026-115506, que lista el paquete fuente expat vulnerable y los paquetes que usan el expat del sistema: https://ubuntu.com/security/CVE-2026-115506 (comprobado el 2 de noviembre de 2026)
  • Fuente JSON del catálogo KEV de CISA, buscada para los cuatro CVE sin coincidencias al 2 de noviembre de 2026: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (comprobado el 2 de noviembre de 2026)