Saltar al contenido
CVE-2026-121577 ·
Media · 3 de noviembre de 2026

Separadores de cabecera sin escapar en whatstk construyen una expresión regular que realiza un retroceso exponencial

S0
Secur0 CNA
CVE-2026-121577

Descripción

Complejidad ineficiente de expresión regular (CWE-1333) en generate_regex() (whatstk/whatsapp/parser.py) en whatstk desde 0.2.0 y anterior a 0.9.0. Quien aporta un export de chat puede hacer que el proceso que lo analiza consuma una cantidad ilimitada de CPU con un fichero de unos pocos cientos de bytes.

La función construye la expresión regular de la cabecera sustituyendo cada código %x por su grupo de captura con nombre, y deja sin escapar todos los demás caracteres del formato de cabecera dentro del patrón. Un separador de fecha que sea un metacarácter de expresión regular altera por tanto el patrón en lugar de coincidir consigo mismo. Un separador + cuantifica el grupo de captura que lo precede, dando (?P<day>\d{1,2})+(?P<month>\d{1,2})+(?P<year>\d{2,4}), y una secuencia de dígitos que no puede completar la cabecera hace que el motor pruebe todas las formas de particionar esa secuencia.

La detección automática de cabecera está activada por defecto y deduce el formato del texto del fichero, por lo que el separador proviene del propio export y la función llamante no tiene que pasar ningún formato de cabecera. Un chat cuyas fechas sean 11+11+2011 se detecta como %d+%m+%y, %H:%M - %name: y produce el patrón cuantificado por sí solo.

La misma falta de escapado rompe otros separadores sin más. (, ) y \ provocan re.PatternError, *, | y ? provocan TypeError, y ^ y $ provocan KeyError. Ninguno se captura, de modo que la excepción aflora en el programa llamante.

Tipo de vulnerabilidad (CWE)

  • CWE-1333: Complejidad ineficiente de expresión regular

Versiones afectadas

whatstk desde 0.2.0 y anterior a 0.9.0. La sustitución sin escapar de generate_regex() se remonta a 0.1.10, pero la detección automática de cabecera llega en 0.2.0 y está activada por defecto desde esa versión, que es el punto a partir del cual el patrón puede quedar dirigido por un fichero de chat y no por un formato elegido por la función llamante. Estado por defecto: no afectado.

Medido en la 0.8.1 de PyPI con argumentos por defecto y un fichero con tres líneas corrientes más una secuencia larga de dígitos: 16 dígitos en un fichero de 115 bytes tardaron 0.011s, 20 dígitos 0.063s, 24 dígitos 0.486s, 28 dígitos 4.912s, y 32 dígitos en un fichero de 131 bytes tardaron 40.811s, en torno a diez veces más por cada cuatro dígitos. Las mismas entradas en 0.9.0 tardan 0.010s o menos, incluso con 48 dígitos, y se analizan correctamente. Los diez separadores metacarácter probados se analizan con normalidad en 0.9.0.

Puntuación (CVSS 4.0)

Media (6.7)

CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

El vector de ataque es Local porque la librería lee un fichero de export en la máquina que la ejecuta. Los privilegios requeridos son Ninguno, ya que quien aporta el fichero no necesita ningún acceso a esa máquina. Los requisitos de ataque son Ninguno porque el atacante escribe el export completo, separador incluido, y no hace falta que coincida nada. La interacción del usuario es Activa porque el análisis tiene que invocarse sobre el fichero. La confidencialidad y la integridad son Ninguna: no se lee ni se altera nada. La disponibilidad del sistema vulnerable es Alta porque el análisis no llega a completarse, y al ritmo de crecimiento medido una secuencia de dígitos algo más larga en ese mismo fichero pequeño cuesta horas en lugar de segundos, mientras que las variantes con metacarácter terminan el programa llamante con una excepción no capturada. Las métricas del sistema subsiguiente son Ninguna, ya que lo que se detiene es el programa que realiza el análisis.

Un servicio que acepte exports subidos y los analice con argumentos por defecto es el caso en el que esto más importa, y allí el vector sería de Red y no Local.

Solución

Actualizar a whatstk 0.9.0 o posterior.

En una versión afectada, pasar un hformat explícito que no contenga metacaracteres de expresión regular evita que el patrón quede alterado, pero no sirve cuando el formato se detecta del fichero, que es el comportamiento por defecto.

Parche

Publicado en 0.9.0, mediante la pull request #181 y el commit de publicación 01d66d8. generate_regex() divide ahora el formato de cabecera por los códigos %x, pasa todo lo que hay entre ellos por re.escape y vuelve a unir las partes, de modo que un separador que sea un metacarácter coincide consigo mismo. El mismo cambio corrige un error de coincidencia que no figuraba en el reporte: el . tampoco se escapaba, así que un formato %d.%m.%y coincidía con 06X08X2016 en la 0.8.1.

Créditos

  • m.gongora.carriedo - descubridor
  • Xoán M. Otero Jorge - analista
  • Darío Rivas - analista
  • Secur0 CNA - coordinador
  • Lucas Rodés-Guirao - desarrollador de la corrección

Origen del descubrimiento: Externo

Registro oficial: CVE-2026-121577

Registros relacionados

  • CVE-2026-121564 - pérdida silenciosa de mensajes al fusionar exports solapados en la misma librería