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

RCE por recorrido de directorios gRPC en FortiSIEM con PoC pública (CVE-2026-106173)

El servicio gRPC sin autenticación de FortiSIEM 7.5.0 en TCP 27918 permite sobrescribir archivos para ejecutar código de forma remota; corregido en 7.5.1, con PoC pública y sin explotación conocida.

Resumen

  • CVE-2026-106173 combina recorrido de directorios e inyección de bytes nulos en un servicio gRPC de FortiSIEM, lo que permite a un atacante no autenticado escribir contenido arbitrario en cualquier ruta del dispositivo.
  • Sobrescribir un script de comprobación de estado que cron ejecuta cada 60 segundos convierte esa escritura de archivos en ejecución remota de código.
  • Severidad: crítica, CVSS v3.1 9.4, según Fortinet.
  • Solo está afectado FortiSIEM 7.5.0. FortiSIEM 7.6 y FortiSIEM Cloud no están afectados, y no se enumera ninguna otra versión.
  • Fortinet descubrió el fallo internamente (Gwendal Guégniaud, del equipo Fortinet Product Security) y publicó el aviso FG-IR-26-180 el 13 de octubre de 2026, sin explotación conocida.
  • Horizon3.ai publicó un análisis técnico detallado con una prueba de concepto funcional el 20 de octubre de 2026.
  • El registro de CISA señala una prueba de concepto pública sin explotación conocida.
  • Actualice ahora FortiSIEM 7.5.0 a 7.5.1 o posterior y, hasta entonces, restrinja el acceso al puerto TCP 27918. Esto bloquea la explotación al cortar el acceso no autenticado al servicio vulnerable, pero no elimina el fallo.

¿Estoy afectado?

Está afectado si se cumplen ambas condiciones:

  • Utiliza FortiSIEM 7.5.0. El aviso de Fortinet no enumera otras versiones afectadas: 7.6 no está afectada y FortiSIEM Cloud tampoco.
  • El servicio gRPC en cuestión es accesible desde una ubicación de red no confiable. phQueryMaster es el servicio gRPC que escucha en TCP 27918 en todas las interfaces del dispositivo, y su configuración TLS no solicita certificados de cliente. Por tanto, cualquier ubicación de red que pueda alcanzar el puerto es una ubicación desde la que se puede atacar.
Software Versiones afectadas Versión corregida
FortiSIEM 7.5.x (autogestionado) 7.5.0 7.5.1 o posterior
FortiSIEM 7.6.x no afectado no aplicable
FortiSIEM Cloud no afectado no aplicable

Compruebe la exposición relevante, la del puerto, desde las redes que podría utilizar un atacante:

nmap -p 27918 --open <fortisiem-host>       # phQueryMaster's gRPC service; a "27918/tcp open" line means reachable, no output means filtered or closed, which is the safe state

FortiSIEM 7.5.0 es la única versión afectada, por lo que basta una comprobación: confirme la compilación en la interfaz de administración de FortiSIEM y, si es 7.5.0, actualice antes de hacer cualquier otra cosa. La segmentación no sustituye al parche. Un SIEM interno bien segmentado sigue ejecutando el servicio vulnerable, y un solo host comprometido en ese segmento puede alcanzarlo. Actualice con independencia de la accesibilidad actual, porque la corrección elimina el propio fallo.

¿Cómo lo corrijo?

Actualice FortiSIEM 7.5.0 a 7.5.1 o posterior siguiendo la guía de actualización de FortiSIEM de Fortinet y reinicie los servicios del dispositivo.

Hasta que complete la actualización, restrinja el acceso al puerto TCP 27918. El aviso de Fortinet ofrece esta medida provisional («Restringir el acceso al puerto tcp 27918 evita el problema»): bloquea la explotación al cortar el acceso no autenticado al servicio, pero el fallo permanece.

El servicio se vincula a todas las interfaces del dispositivo, por lo que debe aplicar la restricción en el firewall circundante o mediante segmentación de red, no en el dispositivo. Es un bloqueo de red, no un parche: cualquier ruta que siga llegando al puerto 27918, incluido un host comprometido en el mismo segmento, mantiene accesible el fallo.

Después de actualizar, inspeccione /opt/charting/redishb.sh y /opt/phoenix/bin/phLicenseTool, los dos archivos que Horizon3 señala como objetivos realistas de sobrescritura, porque una sobrescritura anterior a la actualización sobrevive a ella.

¿Cuál es la causa raíz?

El manejador gRPC SendQueryResult construye una ruta de archivo concatenando en ella un encabezado controlado por el atacante y copia los valores de los encabezados con la longitud declarada por el cliente. Así, un byte nulo dentro del encabezado trunca la ruta hasta el nombre de archivo exacto que el atacante elija.

El servicio phQueryMaster inicia un servidor gRPC en TCP 27918; Horizon3 cita su código de inicio en libphGrpcServices.so:

// grpcNotificationServer::run()
unsigned short port = mServerPort;                          // command_port = 7918
std::string addr = "[::]:" + phSecureUtils::getGrpcPort(port);  // [::]:27918
grpc::SslServerCredentialsOptions sslOps;
// sslOps.client_certificate_request stays 0 == GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE
auto creds = grpc::SslServerCredentials(sslOps);
builder.AddListeningPort(addr, creds, 0);

phSecureUtils::getGrpcPort() transforma el puerto base de comandos del dispositivo (7918) en el puerto de escucha 27918 del servicio gRPC; por eso, el código citado parte de 7918 pero se vincula a [::]:27918. La dirección abarca todas las interfaces y la opción de certificado de cliente, que queda en cero, permite que cualquier cliente se conecte sin certificado ni autenticación. El RPC vulnerable y su manejador, citados de grpcNotificationService.cpp:

replaceString(srcIp, ".", "_");
std::string fileDir = "/query/result/" + queryId;   // concatenation, no validation
if (!phMiscUtils::DirExists(fileDir.c_str()))
    phMiscUtils::mkdir(fileDir.c_str());

std::stringstream path;
path << fileDir << "/result-" << srcIp << "-"
     << periodStart << "-" << periodEnd << "-"
     << mGetQueryResultUniqId() << ".dat";
std::ofstream output;
output.open(path.str().c_str(), ios::binary|ios::out|ios::trunc);
while (reader->Read(&req))
    output.write(req.file_content().data(), req.file_content().size());

Recorrido de directorios: el encabezado query-id del mensaje gRPC se concatena en la ruta del directorio sin validación, por lo que ../../../../../tmp/x sale del directorio de consultas y permite al atacante controlar arbitrariamente el directorio. Por sí solo, el nombre de archivo sigue incorporando partes de la solicitud, de modo que la escritura termina en el directorio elegido con un nombre result-...dat añadido por el servidor.

Truncamiento por byte nulo: el servidor copia los valores de los encabezados utilizando la longitud exacta declarada por el cliente. Por tanto, un query-id de ../../../../../opt/charting/redishb.sh\0ignored introduce un byte nulo en la ruta. El sistema operativo trata ese byte como el final de la cadena, descarta el sufijo result-...dat del servidor y la escritura termina en la ruta exacta que lo precede.

¿Cómo se explota?

La explotación consiste en una llamada gRPC SendQueryResult no autenticada: un query-id con recorrido ../../ y un byte nulo hace que el file_content transmitido sobrescriba /opt/charting/redishb.sh, y cron lo ejecuta en un máximo de 60 segundos.

En la prueba de concepto publicada por Horizon3, esa solicitud se construye manualmente. Se conecta a TCP 27918 sin autenticación ni certificado de cliente, llama a SendQueryResult con los metadatos query-id: ../../../../../opt/charting/redishb.sh\0ignored, src-ip: 127.0.0.1, period-start: 0 y period-end: 0, y transmite el payload como file_content.

El servidor hace el resto. El manejador construye el directorio a partir del recorrido, fuera de /query/result. El byte nulo trunca la ruta construida, por lo que los bytes transmitidos sobrescriben /opt/charting/redishb.sh en su ubicación, conservando el propietario y los permisos originales del archivo.

redishb.sh es un script de comprobación de estado del servicio Redis que una tarea cron ejecuta cada 60 segundos. Así, en menos de un minuto tras la sobrescritura, el dispositivo ejecuta el código del atacante.

Sobrescribir un archivo existente requiere una solicitud; crear un archivo nuevo con un nombre exacto requiere dos, porque la primera debe establecer el directorio de destino antes de que la segunda escriba el nombre exacto mediante truncamiento por byte nulo. La solicitud se construye manualmente porque curl y las bibliotecas gRPC estándar restringen los bytes nulos en los valores de encabezado: el fallo es explotable, pero automatizarlo no es trivial.

¿Cómo se desarrolla el ataque?

Una llamada gRPC no autenticada sobrescribe un script que cron ejecuta cada minuto. Por tanto, el retraso entre la escritura del archivo y la ejecución del código es de 60 segundos como máximo.

flowchart LR A["Cliente gRPC no autenticado
accede a tcp/27918"] --> B["SendQueryResult con query-id
../../../../../opt/charting/redishb.sh
+ byte nulo"] B --> C["Directorio construido por
concatenación, sin validación"] C --> D["El byte nulo trunca la ruta
al nombre exacto del archivo"] D --> E["Los bytes transmitidos sobrescriben
/opt/charting/redishb.sh"] E --> F["Cron ejecuta el script
en un máximo de 60 segundos"]

¿Qué puede hacer un atacante?

Un atacante con acceso de red a TCP 27918 en un dispositivo FortiSIEM 7.5.0 puede:

  • Escribir contenido arbitrario en una ruta arbitraria del dispositivo, con una sola solicitud si sobrescribe un archivo existente.
  • Ejecutar ese contenido con los privilegios del contexto de ejecución del script sobrescrito en un máximo de 60 segundos, atacando cualquier script o binario que el dispositivo ejecute de forma programada. Horizon3 lo demostró mediante el script de comprobación de Redis /opt/charting/redishb.sh.
  • Utilizar la misma primitiva para corromper la configuración, manipular los binarios del propio SIEM (Horizon3 enumera /opt/phoenix/bin/phLicenseTool como segundo archivo de interés) o establecer persistencia.

No se requieren autenticación, interacción del usuario ni privilegios; solo acceso de red al puerto. El límite práctico es la accesibilidad: la medida provisional de Fortinet es una restricción de red precisamente porque cualquier ruta que siga alcanzando 27918, incluido un host comprometido en el mismo segmento, mantiene el fallo explotable.

¿Qué debe cerrar la corrección?

Fortinet corrigió el fallo en FortiSIEM 7.5.1; el aviso no publica el diff del parche, por lo que la afirmación definitiva sobre la corrección es la propia indicación de versión.

Lo que debe cerrar la corrección se deduce del análisis de Horizon3 sobre las dos partes del error: query-id debe dejar de llegar sin validación a la ruta del directorio, para que las secuencias de recorrido ya no puedan salir de /query/result, y los valores de encabezado copiados deben dejar de poder inyectar un byte nulo en la ruta construida. La medida provisional no resuelve ninguna de las dos cosas; elimina la ruta de red hacia el manejador vulnerable.

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

Horizon3 afirma claramente que ningún registro de la aplicación revela explícitamente esta explotación, por lo que la comprobación exige análisis forense de archivos. Según los indicadores del análisis:

  • Ejecute stat /opt/charting/redishb.sh y compare sus marcas de tiempo de modificación (mtime) y cambio (ctime) con las de los archivos circundantes; una sobrescritura cuyas marcas difieran de las de los archivos cercanos es una señal que debe investigarse.
  • Haga lo mismo con /opt/phoenix/bin/phLicenseTool, el segundo archivo que Horizon3 señala como objetivo.
  • Revise la actividad de cron y de procesos del dispositivo en busca de procesos hijos inesperados. Considere relacionados los shells o intérpretes iniciados por un script programado, o cualquier conexión saliente que el dispositivo inicie por su cuenta, hasta que se demuestre lo contrario.

El aviso de Fortinet no registra explotación conocida y el registro de CISA señala una prueba de concepto pública sin explotación conocida. Por tanto, al redactar este artículo no hay indicadores de campaña que buscar. La comparación de marcas de tiempo y la revisión de procesos anteriores son las comprobaciones disponibles.

Cronología

  • 2026-10-13: Fortinet publica el aviso FG-IR-26-180 con el registro CVE-2026-106173, calificado como crítico, CVSS v3.1 9.4, sin autenticación y sin explotación conocida.
  • 2026-10-20: Horizon3.ai publica el análisis detallado y una prueba de concepto funcional, trazando toda la cadena desde el puerto gRPC hasta la ejecución de cron a los 60 segundos.
  • 2026-10-26: este artículo. Fortinet y CISA no registran explotación en ataques reales.

Preguntas frecuentes

¿Está expuesto el puerto 27918 de forma predeterminada?

No por la configuración propia de FortiSIEM, pero sí por la vinculación predeterminada: el servicio phQueryMaster escucha en todas las interfaces. La accesibilidad real depende de la red circundante; un dispositivo accesible solo desde una red de administración mantiene el puerto fuera del alcance del atacante. Confírmelo desde las redes relevantes con nmap -p 27918 --open <fortisiem-host>: una línea 27918/tcp open significa que es accesible, y la ausencia de una línea del puerto significa que está filtrado o cerrado como se pretendía.

¿La configuración TLS detiene a un atacante?

No. El servicio presenta credenciales TLS, pero deja la opción de certificado de cliente en «no solicitar», por lo que cualquier cliente puede completar una conexión y llamar al RPC sin certificado. El byte nulo viaja dentro de un valor de encabezado HTTP/2 sin procesar, por debajo de la capa que inspecciona la pila TLS; por eso, una pila TLS estándar no lo rechaza.

¿Por qué el byte nulo da control total del nombre de archivo?

Como el servidor copia los valores de encabezado con la longitud exacta declarada por el cliente, un byte nulo dentro del encabezado sobrevive en la cadena de la ruta y el sistema operativo deja de leerla al llegar a ese byte. Esto descarta el sufijo result-<src>-<start>-<end>-<id>.dat del propio servidor, que de otro modo fijaría el nombre del archivo. El resultado es una escritura en una ruta exacta; el único paso adicional es construir el mensaje manualmente.

¿Están afectados FortiSIEM 7.6 o FortiSIEM Cloud?

No. El aviso de Fortinet enumera FortiSIEM 7.6 y FortiSIEM Cloud como no afectados, y 7.5.0 como única versión afectada. Los despliegues en 7.6 no necesitan ninguna acción para este CVE; los de 7.5.0 deben actualizarse a 7.5.1.

¿Puede un atacante utilizar la escritura para algo distinto de ejecutar código?

Sí. La escritura arbitraria de archivos es una primitiva general: sirve para corromper archivos de configuración, sustituir binarios que se ejecutan al arrancar o introducir credenciales. El análisis de Horizon3 enumera varios servicios del dispositivo controlados por cron que consumen archivos. La ejecución de código mediante el script de comprobación de estado de 60 segundos es simplemente la ruta demostrada más corta.

¿Ha habido víctimas?

El aviso de Fortinet indica que no hay explotación conocida, y el registro de CISA para el CVE señala una prueba de concepto pública sin explotación conocida. La prueba de concepto de Horizon3 es pública, por lo que la distancia entre una PoC pública y la explotación en ataques reales es corta; esa es la razón para no demorar la actualización.

Referencias

  • Aviso Fortinet PSIRT FG-IR-26-180, publicado el 13 de octubre de 2026, con la tabla de versiones afectadas, la medida provisional de TCP 27918 y el reconocimiento del descubrimiento interno: https://www.fortiguard.com/psirt/FG-IR-26-180 (consultado el 26 de octubre de 2026)
  • Registro CVE-2026-106173, calificado por Fortinet como crítico con CVSS v3.1 9.4, sin autenticación, con CISA ADP señalando una prueba de concepto pública: https://cveawg.mitre.org/api/cve/CVE-2026-106173 (consultado el 26 de octubre de 2026)
  • Análisis detallado de Horizon3.ai por Zach Hanley, 20 de octubre de 2026, que cita el código de inicio y del manejador gRPC, el mecanismo del byte nulo, el objetivo cron redishb.sh y los indicadores basados en marcas de tiempo: https://horizon3.ai/attack-research/cve-2026-106173-deep-dive-fortisiem-grpc-path-traversal-file-write-rce/ (consultado el 26 de octubre de 2026)
  • Prueba de concepto de Horizon3: https://github.com/horizon3ai/CVE-2026-106173 (consultado el 26 de octubre de 2026)
  • Investigación previa de Horizon3 sobre el servicio phMonitor de FortiSIEM, CVE-2023-34992, CVE-2024-23108 y CVE-2025-64155: https://horizon3.ai/attack-research/disclosures/cve-2025-64155-three-years-of-remotely-rooting-the-fortinet-fortisiem/ (consultado el 26 de octubre de 2026)
  • The Hacker News sobre la PoC pública, octubre de 2026: https://thehackernews.com/2026/10/critical-fortisiem-flaw-gets-public.html (consultado el 26 de octubre de 2026)