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

Checkmk RCE: tres fallos críticos en un solo parche (CVE-2026-121374)

Tres fallos de Checkmk permiten ejecutar código como usuario del sitio desde reglas, un sitio remoto malicioso o un host comprometido; corregidos en 2.5.0p19.

Resumen

  • Checkmk 2.5.0p19 y 2.4.0p40 corrigen tres fallos críticos que terminan en ejecución de código como el usuario del sitio. El proveedor publicó ambos parches el 11 de noviembre de 2026.
  • Todo sitio anterior a 2.5.0p19 en la rama 2.5, o a 2.4.0p40 en la rama 2.4, está afectado. La versión 2.3.0 está al final de su vida y no recibe corrección.
  • Los tres tienen una puntuación CVSS v4.0 9.4 Crítica. No se conoce prueba de concepto pública ni explotación en entornos reales, y ninguno está en el catálogo KEV de CISA a 11 de noviembre de 2026.
  • Actualice a 2.5.0p19 o 2.4.0p40. Si no puede, restrinja la edición de reglas y no conecte el sitio central a sitios remotos no confiables; consulte "¿Cómo lo corrijo?" para conocer los límites.

¿Estoy afectado?

El requisito de versión se aplica a todas las vulnerabilidades; las demás condiciones se aplican por separado a los CVE indicados:

  • Ejecuta un sitio Checkmk en 2.5.0 anterior a 2.5.0p19, o en 2.4.0 anterior a 2.4.0p40. La versión 2.3.0 está afectada y al final de su vida.
  • Para CVE-2026-121374, al menos un usuario puede editar reglas en al menos una carpeta. Por defecto, esto incluye el rol "Normal monitoring user" cuando tiene permisos sobre una carpeta.
  • Para CVE-2026-118965, su sitio central sincroniza las etiquetas de host descubiertas desde sitios remotos que no considera totalmente confiables.
  • Para CVE-2026-120495, ejecuta la edición Pro, Ultimate, Cloud o Ultimate MT con la monitorización sintética Robotmk, y un host monitorizado puede enviar datos de agente manipulados.

El primer fallo no requiere Kubernetes. Un usuario que puede crear una regla puede añadir una regla del agente especial de Kubernetes aunque el sitio no monitorice ningún clúster. El tercer fallo necesita el descubrimiento automático de servicios para el host, o que un administrador de monitorización confirme los servicios Robotmk a mano.

Rama Versiones afectadas Versión corregida
2.5 2.5.0 a 2.5.0p18 2.5.0p19, publicada el 2026-11-11
2.4 2.4.0 a 2.4.0p39 2.4.0p40, publicada el 2026-11-11
2.3 todas las versiones 2.3.0 Ninguna; al final de su vida
3.0 3.0.0b1 Aún no publicada; incluye las correcciones

Compruebe la versión en cada sitio, incluidos los sitios remotos:

omd version

Dentro de un sitio, el núcleo de monitorización informa la misma versión:

sudo -u <site> cmk --version

La interfaz web la muestra en Help > About. El comando omd sites lista los nombres de los sitios cuando no está seguro de cuáles existen.

¿Cómo lo corrijo?

Instale el paquete 2.5.0p19 o 2.4.0p40 correspondiente a su edición y distribución, y actualice cada sitio. Las páginas werk del proveedor indican que la versión es compatible y que no requiere interacción manual después de la actualización.

  1. Instale el paquete correspondiente a su edición, por ejemplo check-mk-raw-2.5.0p19, check-mk-enterprise-2.5.0p19 o el paquete 2.4.0p40 equivalente.
  2. Actualice cada sitio, incluidos los sitios remotos: omd update <site>
  3. En un dispositivo Checkmk, suba la imagen 2.5.0p19 o 2.4.0p40 en la interfaz del dispositivo y ejecute la actualización allí.
  4. Confirme el resultado: omd version
  5. Repita en cada sitio distribuido. El fallo de las etiquetas de host se corrige en el sitio central; los fallos de Kubernetes y Robotmk se corrigen en el sitio que ejecuta esas comprobaciones.

Si no puede actualizar de inmediato, reduzca la exposición con estas medidas. Cada una limita una ruta de ataque y ninguna sustituye al parche.

  1. Quite el permiso "Edit rules" de todos los roles no administrativos y revise a qué carpetas llegan esos roles. Un administrador todavía puede explotar CVE-2026-121374, así que esta medida solo elimina la ruta por defecto de los usuarios de monitorización ordinarios.
  2. No conecte un sitio central a sitios remotos que no controle. CVE-2026-118965 no tiene un interruptor de configuración; la corrección es la actualización.
  3. Trate los hosts monitorizados como no confiables para CVE-2026-120495. Restrinja quién puede añadir servicios y desactive el descubrimiento automático de servicios para los hosts que no administra. Un host que puede enviar datos de agente todavía alcanza el fallo cuando un administrador añade sus servicios.
  4. Migre cualquier sitio 2.3.0 a 2.4.0p40 o 2.5.0p19. La rama 2.3.0 está al final de su vida y no tiene versión corregida.

¿Cuál es la causa raíz?

Los tres fallos comparten un patrón. Un valor controlado por una parte con menos privilegios llega a una línea de comandos o a una ruta de archivo sin confinamiento. Los privilegios del usuario del sitio convierten entonces la escritura en ejecución de código. Cada punto de entrada es distinto, así que cada corrección también lo es.

El primer fallo está en el agente especial de Kubernetes. La opción de regla "Cluster resource aggregation > Exclude nodes based on their role" recoge nombres de roles. La llamada del lado del servidor pasa esos nombres al agente como argumentos separados después de una sola opción:

match params.cluster_resource_aggregation:
    case ("cluster_aggregation_exclude_node_roles", list(excluded_roles)):
        args.append("--cluster-aggregation-exclude-node-roles")
        args.extend(excluded_roles)

El agente analiza sus argumentos con argparse. El analizador vulnerable declaraba la opción con nargs="+", así que consumía todos los tokens siguientes que no parecían una opción:

group.add_argument(
    "--cluster-aggregation-exclude-node-roles",
    nargs="+",
    default=["control-plane", "infra"],
    dest="roles",
)

Un valor de rol que empieza por --profile no es un valor para argparse. Es una nueva opción, y el siguiente valor de rol se convierte en su argumento. La opción --profile escribe datos de perfilado en un archivo y, en el mismo directorio, un lanzador Python ejecutable cuyo código contiene la ruta del perfil:

f'stats = pstats.Stats("{self._profile_file}")\n'

Una ruta que contiene una comilla doble y sintaxis de Python cambia por tanto el código del lanzador. Según la evaluación del werk, un editor de reglas puede escribir un script Python ejecutable con contenido controlado y alcanzar la ejecución de código como el usuario del sitio.

El segundo fallo está en la sincronización de etiquetas de host. El sitio central pide a cada sitio remoto las etiquetas de host descubiertas de sus hosts y después escribe el contenido devuelto en disco. El código vulnerable unía el nombre de archivo proporcionado por el sitio remoto al directorio de etiquetas sin ninguna comprobación:

def save_updated_host_label_files(updated_host_labels: list[UpdatedHostLabelsEntry]) -> None:
    for file_name, mtime, content in updated_host_labels:
        file_path = cmk.utils.paths.discovered_host_labels_dir / file_name
        store.save_text_to_file(file_path, content)
        os.utime(file_path, (mtime, mtime))

Un nombre de archivo que contiene ../ sale del directorio, y un nombre terminado en otro sufijo escribe un archivo que el sitio importa o ejecuta después. Un sitio remoto comprometido escribe por tanto archivos arbitrarios en el sitio central.

El tercer fallo está en el servicio de pruebas Robotmk. El servicio toma un nombre de caso de prueba de los datos del agente y lo usa para crear la carpeta del registro HTML. La ruta final no se validaba, así que un nombre de caso manipulado sale del directorio de registros. El contenido del registro también lo define el usuario, lo que convierte la escritura en ejecución de código según la evaluación del proveedor.

¿Cómo se explota?

Cada fallo necesita una posición de partida distinta. Ninguno requiere una vulnerabilidad en el host monitorizado, salvo el tercero, que necesita un host capaz de enviar datos de agente.

Para CVE-2026-121374:

  1. Un usuario que puede editar reglas crea o modifica una regla del agente especial de Kubernetes.
  2. En "Exclude nodes based on their role", el usuario introduce --profile como rol y una ruta como valor siguiente.
  3. Cuando se ejecuta el agente especial, argparse lee --profile como una opción y la ruta como su argumento.
  4. El agente escribe los datos de perfilado y el script lanzador en la ruta elegida. El lanzador contiene la ruta dentro de una cadena de Python.
  5. Una ruta manipulada con una comilla y código Python cambia el código del lanzador. El proveedor considera el script ejecutable resultante como ejecución de código como el usuario del sitio.

Para CVE-2026-118965:

  1. Un atacante controla un sitio remoto en una instalación distribuida, o lo compromete.
  2. El sitio remoto responde a la petición de etiquetas del sitio central con un nombre de archivo que contiene ../.
  3. La sincronización del sitio central escribe el contenido proporcionado en la ruta resuelta.
  4. El atacante elige una ruta que el usuario del sitio importa o ejecuta, lo que da ejecución de código en el sitio central.

Para CVE-2026-120495:

  1. Un atacante puede añadir datos de agente para un host monitorizado, por ejemplo mediante un plug-in de agente o un archivo spool malicioso. El werk indica que esto suele requerir root en el host monitorizado.
  2. Los datos manipulados definen un nombre de caso de prueba Robotmk que sale del directorio de registros HTML.
  3. El sitio escribe un registro HTML con contenido controlado por el atacante en la ruta resuelta.
  4. El proveedor califica el resultado como ejecución de código como el usuario del sitio.

¿Cómo se desarrolla el ataque?

Tres entradas distintas llegan al mismo resultado: un archivo escrito como el usuario del sitio Checkmk, que el proveedor considera ejecución de código.

flowchart LR A["Un editor de reglas define
un rol de nodo excluido"] --> B["El rol se lee como
una opción --profile"] C["Un sitio remoto malicioso
devuelve un nombre de archivo de etiqueta"] --> D["El nombre se une al
directorio de etiquetas
sin comprobar la ruta"] E["Un host comprometido envía
un nombre de prueba Robotmk"] --> F["El nombre se usa como
carpeta del registro
sin comprobar la ruta"] B --> G["Archivo escrito como
el usuario del sitio"] D --> G F --> G G --> H["Ejecución de código como
el usuario del sitio"] style D fill:#f8d7da style F fill:#f8d7da

¿Qué puede hacer un atacante?

Un atacante que alcanza la ejecución de código ejecuta comandos como el usuario del sitio Checkmk. Esa cuenta lee la configuración de monitorización, las credenciales que Checkmk usa para llegar a los dispositivos monitorizados y los archivos del sitio. Las condiciones varían según el fallo.

  • CVE-2026-121374: el atacante necesita un usuario que pueda editar reglas en al menos una carpeta. El sitio no necesita monitorizar Kubernetes, porque el atacante crea la regla. El resultado es ejecución de código como el usuario del sitio.
  • CVE-2026-118965: el atacante necesita controlar un sitio remoto en una instalación distribuida. El resultado es escritura arbitraria de archivos en el sitio central y ejecución de código como el usuario del sitio central.
  • CVE-2026-120495: el atacante debe proporcionar datos de agente manipulados para un host monitorizado, lo que según el werk suele requerir root en ese host, y el descubrimiento automático de servicios o un administrador que añada los servicios Robotmk. El resultado es ejecución de código como el usuario del sitio.

El usuario del sitio no es root en el servidor Checkmk. Aun así puede leer los datos de monitorización y las credenciales almacenadas para los sistemas monitorizados, así que trate un compromiso como una exposición de esos sistemas también.

¿Por qué funciona la corrección?

Las correcciones eliminan la ambigüedad entre un valor y una opción, y confinan las rutas que el sitio escribe.

La corrección de Kubernetes pasa cada rol como una opción propia con un valor adjunto, y el analizador los añade uno a uno:

     match params.cluster_resource_aggregation:
         case None:
-            args.extend(["--cluster-aggregation-exclude-node-roles", "control-plane", "infra"])
+            args.extend(
+                f"--cluster-aggregation-exclude-node-role={role}"
+                for role in ("control-plane", "infra")
+            )
         case ("cluster_aggregation_include_all_nodes", None):
             args.append("--cluster-aggregation-include-all-nodes")
         case ("cluster_aggregation_exclude_node_roles", list(excluded_roles)):
-            args.append("--cluster-aggregation-exclude-node-roles")
-            args.extend(excluded_roles)
+            args.extend(
+                f"--cluster-aggregation-exclude-node-role={role}" for role in excluded_roles
+            )
     group.add_argument(
-        "--cluster-aggregation-exclude-node-roles",
-        nargs="+",
-        default=["control-plane", "infra"],
+        "--cluster-aggregation-exclude-node-role",
+        action="append",
+        default=None,
         dest="roles",
+        metavar="ROLE",
     )

Un rol forma ahora parte del token --cluster-aggregation-exclude-node-role=<role>, así que argparse no puede leerlo como una opción separada. El agente también restringe --profile a ~/tmp/check_mk/debug y escribe la ruta en el lanzador como un literal de Python:

+def _profile_file(filename: str) -> Path:
+    path = Path(filename).resolve()
+    allowed = (Path.home() / _PROFILE_DIR).resolve()
+    if not path.is_relative_to(allowed):
+        raise argparse.ArgumentTypeError(f"Profiles can only be stored in {allowed}")
+    return path
-                f'stats = pstats.Stats("{self._profile_file}")\n'
+                f"stats = pstats.Stats({str(self._profile_file)!r})\n"

La corrección de las etiquetas de host resuelve el destino y exige que permanezca bajo el directorio de etiquetas, que lleve el sufijo .mk y que use un nombre de host válido como raíz:

+def _make_secure_path(base: Path, leaf: str) -> Path:
+    path = Path(base).resolve()
+    resolved = (path / leaf).resolve()
+    if not resolved.is_relative_to(path):
+        raise ValueError(f"Path {resolved} is not relative to {path}")
+    if resolved.suffix != ".mk":
+        raise ValueError(f"Path {resolved} does not have .mk suffix")
+    HostName(resolved.stem)  # would raise if validation fails
+    return resolved
+
+
 def save_updated_host_label_files(updated_host_labels: list[UpdatedHostLabelsEntry]) -> None:
     for file_name, mtime, content in updated_host_labels:
-        file_path = cmk.utils.paths.discovered_host_labels_dir / file_name
+        file_path = _make_secure_path(cmk.utils.paths.discovered_host_labels_dir, file_name)

El cambio de Robotmk confina los registros generados a ~/var/robotmk/html_logs y devuelve un estado de servicio UNKNOWN cuando un registro quedaría fuera de ese directorio. El cambio de Robotmk se entrega en el mismo parche, y su werk es el registro público de la nueva regla de rutas.

¿Cómo compruebo si estuve comprometido?

Checkmk no publica indicadores de compromiso para estos fallos. Un intento que falla, o que escribe un archivo que el sitio nunca ejecuta, puede no dejar rastro más allá del propio archivo. Revise los tres lugares que tocan los fallos.

  1. Revise el registro de auditoría en busca de cambios de reglas que añadan una regla del agente especial de Kubernetes o modifiquen "Exclude nodes based on their role". En la interfaz web, abra Setup > General > Audit log y filtre por el conjunto de reglas de Kubernetes.
  2. Busque archivos inesperados en el directorio de depuración del sitio: sudo -u <site> find ~/tmp/check_mk/debug -type f -name '*.py' 2>/dev/null
  3. Liste los archivos de etiquetas de host descubiertos y confirme que cada nombre es un host que monitoriza: sudo -u <site> ls -la ~/var/check_mk/discovered_host_labels
  4. En sitios Pro, Ultimate, Cloud y Ultimate MT, busque registros HTML fuera del directorio esperado: sudo -u <site> find ~/var/robotmk/html_logs -type f 2>/dev/null Un archivo bajo una ruta que contiene .. no puede existir como nombre literal, así que revise los archivos cuyos directorios padre no son carpetas de casos de prueba que reconozca.

Un hallazgo es una pista que investigar, no una prueba de explotación. El registro de auditoría solo ayuda si el cambio de regla del atacante se registró y el registro no se alteró después.

Cronología

  • 2026-10-29: el commit de la corrección de CVE-2026-118965 se registra en el repositorio de Checkmk.
  • 2026-11-02: la corrección de CVE-2026-120495 queda registrada en su werk.
  • 2026-11-03: el commit de la corrección de CVE-2026-121374 se registra en el repositorio de Checkmk.
  • 2026-11-11: Checkmk publica 2.5.0p19 y 2.4.0p40 y publica los tres registros CVE.

Las correcciones llegaron entre el 29 de octubre y el 11 de noviembre de 2026, y el parche cerró una ventana de 13 días
Figura 1: del primer commit de la corrección al parche. Fuente: werks y notas de versión de Checkmk, consultados el 11 de noviembre de 2026

Preguntas frecuentes

¿Afecta a la edición Community?

Sí para CVE-2026-121374 y CVE-2026-118965, que afectan a todas las ediciones. CVE-2026-120495 solo afecta a las ediciones Pro, Ultimate, Cloud y Ultimate MT, porque la monitorización sintética Robotmk no forma parte de la edición Community. Los paquetes corregidos 2.5.0p19 y 2.4.0p40 existen para todas las ediciones.

No monitorizo Kubernetes. ¿Sigo afectado por el primer fallo?

Sí, si un usuario puede editar reglas. El atacante crea la regla del agente especial de Kubernetes, así que el sitio no necesita monitorizar un clúster. Quite el permiso de edición de reglas a los roles no administrativos hasta que actualice.

¿Está corregida la versión 2.3.0?

No. La rama 2.3.0 está al final de su vida, y los tres werks la indican como afectada sin versión corregida. Migre esos sitios a 2.4.0p40 o 2.5.0p19.

¿Se explotó alguno de estos fallos en entornos reales?

No se conoce explotación a 11 de noviembre de 2026, y ninguno de los tres CVE está en el catálogo KEV de CISA. El fallo de Kubernetes se encontró en una revisión interna, y el de las etiquetas de host llegó por el proceso de divulgación de seguridad del proveedor. La situación puede cambiar, así que consulte el catálogo KEV antes de planificar un retraso largo.

¿Debo cambiar mi configuración después de actualizar?

No. Los tres werks están marcados como compatibles sin interacción manual. El agente de Kubernetes acepta la nueva opción por rol, y las comprobaciones de etiquetas de host y Robotmk aplican automáticamente las nuevas reglas de ruta.

¿Cómo sé qué edición ejecuto?

omd version muestra la versión de cada sitio, y el nombre del paquete en el servidor indica la edición, por ejemplo check-mk-raw para Community o check-mk-enterprise para Enterprise. La interfaz web muestra la edición en Help > About.

Encuentre servidores Checkmk expuestos con Ostorlab

Utilice Ostorlab Attack Surface Discovery para inventariar los hosts e interfaces web expuestos a Internet que posee, y así saber qué sitios Checkmk son accesibles y necesitan la actualización primero. Cartografía los servicios expuestos; no detecta este CVE.

Referencias

  • Werk de Checkmk 17994, "Kubernetes: Arbitrary file write and code execution via excluded node roles", CVE-2026-121374, fechado el 2026-11-03: https://checkmk.com/werk/17994 (consultado el 11 de noviembre de 2026)
  • Werk de Checkmk 22120, "Code execution through compromised remote site", CVE-2026-118965, fechado el 2026-10-29: https://checkmk.com/werk/22120 (consultado el 11 de noviembre de 2026)
  • Werk de Checkmk 22624, "Code execution through Robotmk HTML log test service", CVE-2026-120495, fechado el 2026-11-02: https://checkmk.com/werk/22624 (consultado el 11 de noviembre de 2026)
  • El registro CVE-2026-121374, CNA Checkmk, publicado el 2026-11-11: https://www.cve.org/CVERecord?id=CVE-2026-121374 (consultado el 11 de noviembre de 2026)
  • El registro CVE-2026-118965, CNA Checkmk, publicado el 2026-11-11: https://www.cve.org/CVERecord?id=CVE-2026-118965 (consultado el 11 de noviembre de 2026)
  • El registro CVE-2026-120495, CNA Checkmk, publicado el 2026-11-11: https://www.cve.org/CVERecord?id=CVE-2026-120495 (consultado el 11 de noviembre de 2026)
  • Commit de corrección 5a2c448efa para CVE-2026-121374, 2026-11-03: https://github.com/Checkmk/checkmk/commit/5a2c448efafb1e3f2dab75babdecb5f99d0a0618 (consultado el 11 de noviembre de 2026)
  • Commit de corrección 8efcf83a15 para CVE-2026-118965, 2026-10-29: https://github.com/Checkmk/checkmk/commit/8efcf83a153b201ce640a6c1da9de9c52d9c3650 (consultado el 11 de noviembre de 2026)
  • Commit de corrección 4ba4c9f638 para CVE-2026-120495, 2026-11-03: https://github.com/Checkmk/checkmk/commit/4ba4c9f638cf83509970e78468f79bf846327d7b (consultado el 11 de noviembre de 2026)
  • Índice de notas de versión de Checkmk 2.5.0p19, fechado el 11 de noviembre de 2026: https://checkmk.com/werks?version=2.5.0p19 (consultado el 11 de noviembre de 2026)
  • Índice de notas de versión de Checkmk 2.4.0p40, fechado el 11 de noviembre de 2026: https://checkmk.com/werks?version=2.4.0p40 (consultado el 11 de noviembre de 2026)
  • El catálogo Known Exploited Vulnerabilities de CISA, versión 2026.11.10, que no incluye los tres CVE: https://www.cisa.gov/known-exploited-vulnerabilities-catalog (consultado el 11 de noviembre de 2026)
  • Figura 1, la cronología corrección-versión: fechas de los tres werks y los dos índices de versión incluidos en las Referencias, consultados el 11 de noviembre de 2026.