FortiSIEM : RCE par traversée de répertoires gRPC avec une PoC publique (CVE-2026-106173)
Le service gRPC non authentifié de FortiSIEM 7.5.0 sur TCP 27918 permet d’écraser des fichiers pour exécuter du code à distance ; corrigé en 7.5.1, PoC publique, aucune exploitation connue.
En bref
- CVE-2026-106173 combine une traversée de répertoires et une injection d’octet nul dans un service gRPC de FortiSIEM, permettant à un attaquant non authentifié d’écrire un contenu arbitraire à n’importe quel chemin sur l’appliance.
- L’écrasement d’un script de contrôle de santé exécuté par cron toutes les 60 secondes transforme cette écriture de fichier en exécution de code à distance.
- Sévérité : critique, CVSS v3.1 9.4, selon l’évaluation de Fortinet.
- Seul FortiSIEM 7.5.0 est affecté. FortiSIEM 7.6 et FortiSIEM Cloud ne sont pas affectés, et aucune autre version n’est mentionnée.
- Fortinet a découvert la faille en interne (Gwendal Guégniaud, de l’équipe Fortinet Product Security) et publié le bulletin FG-IR-26-180 le 13 octobre 2026, sans exploitation connue.
- Horizon3.ai a publié une analyse technique approfondie accompagnée d’une preuve de concept fonctionnelle le 20 octobre 2026.
- La fiche de CISA signale une preuve de concept publique, sans exploitation connue.
- Mettez immédiatement à jour FortiSIEM 7.5.0 vers la version 7.5.1 ou ultérieure et, en attendant, limitez l’accès au port TCP 27918. Cette mesure bloque l’exploitation en coupant l’accès non authentifié au service vulnérable, mais ne supprime pas la faille.
Suis-je concerné ?
Vous êtes concerné si ces deux conditions sont réunies :
- Vous utilisez FortiSIEM 7.5.0. Le bulletin de Fortinet ne mentionne aucune autre version affectée : la version 7.6 et FortiSIEM Cloud ne sont pas affectés.
- Le service gRPC en question est accessible depuis une position réseau non fiable. phQueryMaster est le service gRPC qui écoute sur TCP 27918 sur toutes les interfaces de l’appliance. Sa configuration TLS ne demande pas de certificat client : toute position réseau permettant d’atteindre ce port peut donc être utilisée par un attaquant.
| Logiciel | Versions affectées | Version corrigée |
|---|---|---|
| FortiSIEM 7.5.x (autogéré) | 7.5.0 | 7.5.1 ou ultérieure |
| FortiSIEM 7.6.x | non affecté | sans objet |
| FortiSIEM Cloud | non affecté | sans objet |
Vérifiez l’exposition qui compte, celle du port, depuis les réseaux qu’un attaquant pourrait utiliser :
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 est la seule version affectée. La vérification se résume donc à confirmer le build dans l’interface d’administration de FortiSIEM et, s’il s’agit de la version 7.5.0, à effectuer la mise à jour avant toute autre action. La segmentation ne remplace pas le correctif. Un SIEM interne bien segmenté exécute toujours le service vulnérable, et un seul hôte compromis sur ce segment peut l’atteindre. Effectuez la mise à jour quelle que soit l’accessibilité actuelle, car le correctif supprime la faille elle-même.
Comment corriger le problème ?
Mettez à jour FortiSIEM 7.5.0 vers la version 7.5.1 ou ultérieure en suivant le guide de mise à niveau FortiSIEM de Fortinet, puis redémarrez les services de l’appliance.
Dans l’attente de la mise à jour, limitez l’accès au port TCP 27918. Le bulletin de Fortinet présente cette restriction comme solution de contournement (« Restreindre l’accès au port tcp 27918 empêche le problème ») : elle bloque l’exploitation en coupant l’accès non authentifié au service, mais laisse la faille en place.
Le service écoute sur toutes les interfaces de l’appliance. Appliquez donc la restriction sur le pare-feu environnant ou par segmentation réseau, et non sur l’appliance. C’est un blocage réseau, pas un correctif : tout chemin qui atteint encore le port 27918, y compris depuis un hôte compromis sur le même segment, laisse la faille accessible.
Après la mise à jour, inspectez /opt/charting/redishb.sh et /opt/phoenix/bin/phLicenseTool, les deux fichiers qu’Horizon3 identifie comme des cibles réalistes d’écrasement : un écrasement survenu avant la mise à jour lui survit.
Quelle est la cause racine ?
Le gestionnaire gRPC SendQueryResult construit un chemin de fichier en y concaténant un en-tête contrôlé par l’attaquant et copie les valeurs d’en-tête selon la longueur déclarée par le client. Un octet nul dans l’en-tête tronque donc le chemin au nom de fichier exact choisi par l’attaquant.
Le service phQueryMaster lance un serveur gRPC sur TCP 27918 ; Horizon3 cite son code de démarrage extrait de 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() convertit le port de commande de base de l’appliance (7918) en port d’écoute 27918 du service gRPC. C’est pourquoi le code cité part de 7918 mais écoute sur [::]:27918. Cette adresse correspond à toutes les interfaces, et l’option de certificat client laissée à zéro signifie que tout client peut se connecter sans certificat et sans authentification. Voici le RPC vulnérable et son gestionnaire, cités depuis 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());
Traversée de répertoires : l’en-tête query-id du message gRPC est concaténé au chemin du répertoire sans validation. Ainsi, ../../../../../tmp/x sort du répertoire des requêtes et donne à l’attaquant un contrôle arbitraire du répertoire. À lui seul, ce mécanisme conserve dans le nom du fichier des éléments de la requête : l’écriture a donc lieu dans le répertoire choisi, sous un nom result-...dat ajouté par le serveur.
Troncature par octet nul : le serveur copie les valeurs d’en-tête selon la longueur exacte déclarée par le client. Un query-id valant ../../../../../opt/charting/redishb.sh\0ignored introduit ainsi un octet nul dans le chemin. Le système d’exploitation interprète cet octet comme la fin de la chaîne, ignore le suffixe result-...dat ajouté par le serveur et écrit au chemin exact qui précède cet octet.
Comment la faille est-elle exploitée ?
L’exploitation nécessite un seul appel gRPC SendQueryResult non authentifié : un query-id contenant une traversée ../../ et un octet nul fait écraser /opt/charting/redishb.sh par le flux file_content, puis cron l’exécute dans les 60 secondes.
Dans la preuve de concept publiée par Horizon3, cette requête est assemblée à la main. Elle se connecte à TCP 27918 sans authentification ni certificat client, appelle SendQueryResult avec les métadonnées query-id: ../../../../../opt/charting/redishb.sh\0ignored, src-ip: 127.0.0.1, period-start: 0 et period-end: 0, puis transmet le payload comme file_content.
Le serveur fait ensuite le reste. Le gestionnaire construit le répertoire à partir de la traversée, en dehors de /query/result. L’octet nul tronque le chemin construit : les octets transmis écrasent donc /opt/charting/redishb.sh sur place, tout en conservant le propriétaire et les permissions d’origine du fichier.
redishb.sh est un script de contrôle de santé du service Redis qu’une tâche cron exécute toutes les 60 secondes. Dans la minute qui suit l’écrasement, l’appliance exécute donc le code de l’attaquant.
Écraser un fichier existant nécessite une requête ; créer un fichier entièrement nouveau sous un nom exact en nécessite deux, car la première doit établir le répertoire de destination avant que la seconde écrive sous le nom exact grâce à la troncature par octet nul. La requête est assemblée à la main parce que curl et les bibliothèques gRPC standard limitent les octets nuls dans les valeurs d’en-tête : la faille est exploitable, mais son automatisation n’est pas triviale.
Quel est le déroulement de l’attaque ?
Un seul appel gRPC non authentifié écrase un script exécuté chaque minute par cron : le délai entre l’écriture du fichier et l’exécution du code est donc d’au plus 60 secondes.
atteint tcp/27918"] --> B["SendQueryResult avec query-id
../../../../../opt/charting/redishb.sh
+ octet nul"] B --> C["Répertoire construit par
concaténation, sans validation"] C --> D["L’octet nul tronque le chemin
au nom de fichier exact"] D --> E["Les octets transmis écrasent
/opt/charting/redishb.sh"] E --> F["Cron exécute le script
dans les 60 secondes"]
Que peut faire un attaquant ?
Un attaquant ayant accès au port TCP 27918 d’une appliance FortiSIEM 7.5.0 peut :
- Écrire un contenu arbitraire à un chemin arbitraire sur l’appliance, en une seule requête lorsqu’il écrase un fichier existant.
- Exécuter ce contenu avec les privilèges du contexte d’exécution du script écrasé dans les 60 secondes, en ciblant un script ou un binaire exécuté périodiquement par l’appliance. Horizon3 l’a démontré avec le script de contrôle de santé de Redis
/opt/charting/redishb.sh. - Utiliser la même primitive pour corrompre la configuration, altérer les binaires du SIEM lui-même (Horizon3 cite
/opt/phoenix/bin/phLicenseToolcomme deuxième fichier d’intérêt) ou établir une persistance.
Aucune authentification, aucune interaction utilisateur et aucun privilège ne sont requis : seul un accès réseau au port est nécessaire. La limite pratique est l’accessibilité. La solution de contournement de Fortinet est précisément une restriction réseau parce que tout chemin atteignant encore 27918, y compris depuis un hôte compromis sur le même segment, maintient la faille exploitable.
Que doit empêcher le correctif ?
Fortinet a corrigé la faille dans FortiSIEM 7.5.1. Le bulletin ne publie pas le diff du correctif : l’indication de version constitue donc la seule information définitive sur la correction.
Ce que le correctif doit empêcher découle de l’analyse des deux composantes du bug par Horizon3 : query-id ne doit plus atteindre le chemin du répertoire sans validation, afin que les séquences de traversée ne permettent plus de sortir de /query/result, et les valeurs d’en-tête copiées ne doivent plus pouvoir injecter un octet nul dans le chemin construit. La solution de contournement ne traite aucun de ces deux aspects ; elle supprime l’accès réseau au gestionnaire vulnérable.
Comment vérifier si j’ai été compromis ?
Horizon3 indique explicitement qu’aucun journal applicatif ne révèle directement cette exploitation. La vérification repose donc sur l’analyse forensique des fichiers. À partir des indicateurs de l’analyse approfondie :
- Exécutez
stat /opt/charting/redishb.shet comparez les horodatages de modification (mtime) et de changement (ctime) à ceux des fichiers voisins ; des horodatages divergents après un écrasement constituent un signal à examiner. - Faites de même pour
/opt/phoenix/bin/phLicenseTool, le deuxième fichier qu’Horizon3 identifie comme cible. - Examinez l’activité de cron et des processus sur l’appliance à la recherche de processus enfants inattendus. Considérez tout shell ou interpréteur lancé par un script planifié, ou toute connexion sortante initiée spontanément par l’appliance, comme lié à l’incident jusqu’à preuve du contraire.
Le bulletin de Fortinet ne recense aucune exploitation connue, et la fiche de CISA signale une preuve de concept publique sans exploitation connue. Au moment de la rédaction, aucun indicateur de campagne n’est donc disponible pour la recherche de traces. Les vérifications possibles sont la comparaison des horodatages et l’examen des processus décrits ci-dessus.
Chronologie
- 2026-10-13 : Fortinet publie le bulletin FG-IR-26-180 avec la fiche CVE-2026-106173, classée critique, CVSS v3.1 9.4, exploitable sans authentification et sans exploitation connue.
- 2026-10-20 : Horizon3.ai publie l’analyse approfondie et une preuve de concept fonctionnelle, retraçant toute la chaîne depuis le port gRPC jusqu’à l’exécution par cron au bout de 60 secondes.
- 2026-10-26 : publication de cet article. Aucune exploitation réelle n’est recensée par Fortinet ou CISA.
FAQ
Le port 27918 est-il exposé par défaut ?
Pas du seul fait de la configuration propre à FortiSIEM, mais oui en ce qui concerne l’écoute par défaut : le service phQueryMaster écoute sur toutes les interfaces. Son accessibilité réelle dépend du réseau environnant ; une appliance accessible uniquement depuis un réseau d’administration maintient le port hors de portée de l’attaquant. Vérifiez depuis les réseaux pertinents avec nmap -p 27918 --open <fortisiem-host> : une ligne 27918/tcp open signifie que le port est accessible ; l’absence de ligne signifie qu’il est filtré ou fermé comme prévu.
La configuration TLS bloque-t-elle un attaquant ?
Non. Le service présente des identifiants TLS, mais laisse l’option de certificat client sur « ne pas demander ». Tout client peut donc établir une connexion et appeler le RPC sans certificat. L’octet nul est transporté dans une valeur d’en-tête HTTP/2 brute, sous la couche inspectée par une pile TLS : une pile TLS standard ne le rejette donc pas.
Pourquoi l’octet nul donne-t-il un contrôle total sur le nom du fichier ?
Comme le serveur copie les valeurs d’en-tête selon la longueur exacte déclarée par le client, un octet nul dans l’en-tête est conservé dans la chaîne du chemin, et le système d’exploitation cesse de lire le chemin à cet octet. Cela élimine le suffixe result-<src>-<start>-<end>-<id>.dat ajouté par le serveur, qui imposerait autrement le nom du fichier. Le résultat est une écriture à un chemin exact ; la seule étape supplémentaire consiste à assembler le message à la main.
FortiSIEM 7.6 ou FortiSIEM Cloud sont-ils affectés ?
Non. Le bulletin de Fortinet indique que FortiSIEM 7.6 et FortiSIEM Cloud ne sont pas affectés, et que seule la version 7.5.0 est concernée. Les déploiements en 7.6 ne nécessitent aucune action pour cette CVE ; ceux en 7.5.0 nécessitent la mise à jour 7.5.1.
Un attaquant pourrait-il utiliser l’écriture autrement que pour exécuter du code ?
Oui. L’écriture arbitraire de fichiers est une primitive générale : elle permet de corrompre des fichiers de configuration, de remplacer des binaires exécutés au démarrage ou d’implanter des identifiants. L’analyse d’Horizon3 recense plusieurs services pilotés par cron sur l’appliance qui consomment des fichiers. L’exécution de code via le script de contrôle de santé exécuté toutes les 60 secondes est simplement le chemin démontré le plus court.
Des victimes ont-elles été identifiées ?
Le bulletin de Fortinet indique qu’aucune exploitation n’est connue, et la fiche CISA de cette CVE signale une preuve de concept publique sans exploitation connue. La preuve de concept d’Horizon3 est publique : le délai entre sa publication et une exploitation réelle peut donc être court, ce qui justifie de ne pas attendre pour effectuer la mise à jour.
Références
- Bulletin Fortinet PSIRT FG-IR-26-180, publié le 13 octobre 2026, avec le tableau des versions affectées, la restriction du port TCP 27918 comme solution de contournement et l’attribution de la découverte interne : https://www.fortiguard.com/psirt/FG-IR-26-180 (consulté le 26 octobre 2026)
- Fiche CVE-2026-106173, classée critique par Fortinet avec un score CVSS v3.1 de 9.4, sans authentification requise, avec mention d’une preuve de concept publique par CISA ADP : https://cveawg.mitre.org/api/cve/CVE-2026-106173 (consultée le 26 octobre 2026)
- Analyse approfondie d’Horizon3.ai par Zach Hanley, le 20 octobre 2026, citant le code de démarrage gRPC et du gestionnaire, le mécanisme de l’octet nul, la cible cron redishb.sh et les indicateurs fondés sur les horodatages : https://horizon3.ai/attack-research/cve-2026-106173-deep-dive-fortisiem-grpc-path-traversal-file-write-rce/ (consultée le 26 octobre 2026)
- Preuve de concept d’Horizon3 : https://github.com/horizon3ai/CVE-2026-106173 (consultée le 26 octobre 2026)
- Recherches antérieures d’Horizon3 sur le service phMonitor de FortiSIEM, CVE-2023-34992, CVE-2024-23108 et CVE-2025-64155 : https://horizon3.ai/attack-research/disclosures/cve-2025-64155-three-years-of-remotely-rooting-the-fortinet-fortisiem/ (consultées le 26 octobre 2026)
- The Hacker News sur la preuve de concept publique, octobre 2026 : https://thehackernews.com/2026/10/critical-fortisiem-flaw-gets-public.html (consulté le 26 octobre 2026)