node2shell (CVE-2026-111497) : exécution de code dans le décodeur Brotli de Node.js
node2shell (CVE-2026-111497) est une écriture hors limites dans le décodeur Brotli embarqué dans Node.js, corrigée le 21 octobre 2026. De la donnée compressée à l'exécution de code : ce que la faille change pour votre parc, les trois portes d'entrée, et comment vérifier votre exposition sans rien casser.

Le jour où Node et PHP ont patché en même temps
Le 21 octobre 2026, deux correctifs de sécurité majeurs tombent le même jour. D'un côté PHP, et sa faille php2shell qui occupe tout l'espace. De l'autre Node.js, qui publie discrètement dix correctifs d'un coup. Dans la liste, une ligne sèche sur un décodeur Brotli. Facile à manquer, noyée entre deux contournements TLS et un bug DNS.
Le lendemain, cette ligne a un nom, node2shell, et une démonstration qui fait moins sourire : des chercheurs d'Imperva exécutent du code sur une image Node.js officielle, non modifiée. Parce que cette faille-là ne vit pas dans une application que vous auriez mal écrite. Elle vit dans le moteur, sous des millions d'applications, du petit microservice interne à l'API qui encaisse votre trafic de production.
C'est ce déplacement de cible qui rend node2shell intéressante. wp2shell et xss2shell visaient WordPress. php2shell visait l'interpréteur PHP. node2shell vise le décodeur que Node.js embarque dans son propre binaire. On ne corrige pas ça en touchant son code, et c'est justement ce qui la rend facile à oublier.
Au cœur du bug : une erreur prise pour une autre
Brotli est un algorithme de compression de Google, utilisé partout sur le web pour alléger les réponses HTTP. Node.js en embarque une copie dans son binaire, exposée par le module node:zlib. C'est dans cette copie que se cache le défaut, et il tient en une confusion presque triviale.
Un flux Brotli se lit bloc par bloc, et le décodeur avance octet par octet à mesure que les données arrivent. Tout se joue sur une incohérence : quand le flux demande un changement de type de bloc que sa propre structure rend impossible, le décodeur vulnérable ne conclut pas que l'entrée est cassée. Il conclut qu'il lui manque juste des données, et il continue.
C'est toute la différence entre deux signaux qui se ressemblent et n'ont rien à voir : « reviens quand tu auras plus de données » et « ces octets sont invalides, arrête-toi ». Le premier est normal dans un décodeur en flux. Le second doit tout interrompre. En confondant les deux, le décodeur continue d'empiler les octets de l'attaquant dans un tampon qui n'en attend que huit.

Passé le huitième octet, l'écriture déborde dans de la mémoire voisine. C'est l'écriture hors limites, classée CWE-787, et c'est de cet octet de trop que part tout le reste, du simple crash jusqu'à l'exécution de code.
Le correctif est à la hauteur de sa cause : il vient d'un commit amont de Brotli (0ed4268a, de février 2026) que Node.js a repris. Il apprend au décodeur à renvoyer une vraie erreur de format sur cette transition, au lieu d'un signal d'attente. Le même correctif vit dans les versions 1.0.10, 1.1.1 et 1.2.1 de la bibliothèque. À garder en tête : tout logiciel qui embarque Brotli peut être concerné, pas seulement Node.js. Le 23 octobre, la faille a d'ailleurs pris un second nom, HTTP2Shell, pour marquer cette portée : jusqu'au binding Python brotlicffi, tout ce qui décode du Brotli reçu par HTTP partage le même angle mort.
Trois portes, un seul décodeur
Voilà le point qui mérite qu'on s'y arrête, plutôt qu'un simple numéro de version à relever. La faille est dans node:zlib, mais node:zlib ne parle pas tout seul à Internet. Pour qu'il y ait exploitation, il faut qu'une donnée contrôlée par l'attaquant atteigne le décodeur. Trois chemins y mènent, et ils n'ont pas le même profil de risque.

La première est la plus préoccupante, et c'est celle qui touche l'exposition externe. Express 5 et body-parser 2.x décompressent par défaut les corps de requête envoyés avec Content-Encoding: br. Un attaquant anonyme n'a donc qu'à poster un corps Brotli piégé sur une route qui lit le corps, et sa donnée atteint le décodeur. Aucun compte, aucune interaction d'un tiers. Détail utile pour la détection : Express 4 et body-parser 1.x, eux, ne décompressent pas le Brotli entrant et répondent 415. Le vecteur est propre à la ligne Express 5.
La deuxième est plus sournoise. fetch(), intégré à Node, décompresse automatiquement les réponses Brotli. Si votre application appelle une URL que l'attaquant contrôle, celle-ci répond avec un corps piégé, et c'est votre processus qui le décode. Webhooks sortants, intégrations qui suivent une URL fournie par un utilisateur, tout ce qui ressemble à une SSRF : la requête part de l'intérieur et contourne vos défenses périmétriques.
La troisième est la plus directe. Si votre code passe lui-même une donnée non fiable à brotliDecompress ou createBrotliDecompress, vous ouvrez la porte sans détour. C'est le vecteur que l'avis officiel met en avant, parce que c'est la primitive brute. Les deux premiers chemins ne sont que des façons d'y arriver sans l'avoir écrit.
De la donnée au shell : ce qui est prouvé, ce qui ne l'est pas
Soyons précis, parce que c'est là que les articles pressés dérapent. node2shell n'est pas une RCE « un paquet, un shell » garantie. Le vecteur CVSS officiel le dit à sa façon : un score v4.0 de 9,2, mais une métrique AT:P qui signale que la réussite dépend de conditions pas toujours réunies. Certaines bases publient aussi un CVSS 3.1 de 9,8.
Ce que les preuves de concept établissent, étape par étape :
- Le crash et la fuite mémoire sont fiables. Faire écrire le décodeur hors de son tampon suffit à tomber le processus ou à lire des octets voisins. Acquis, reproductible.
- L'exécution de code est démontrée, mais probabiliste. Imperva l'a obtenue sur une image Node.js 24.22.0 officielle, sous Linux arm64, avec un fichier témoin créé et sans débogueur. Mais l'équipe précise que l'exploit « a demandé plusieurs tentatives » et ne réussissait pas à chaque essai. La 24.22.1 rejette la même entrée.
- Une autre preuve de concept est plus modeste. D'après The Hacker News, une PoC concurrente n'atteignait l'exécution de code que sur une version de test volontairement affaiblie ; sur un binaire standard, son auteur ne rapportait qu'un crash et une lecture mémoire limitée.
Au 23 octobre 2026, aucune exploitation en conditions réelles n'a été recensée, et la faille n'est pas au catalogue KEV de la CISA. Mais c'est une photographie, pas une conclusion. Le mécanisme est public, des preuves de concept circulent, et l'écart entre « crash fiable » et « RCE occasionnelle » se réduit à chaque itération. La lecture pragmatique pour un RSSI tient en une phrase : traitez le crash et la fuite comme certains, l'exécution de code comme plausible, et corrigez avant que la nuance ne devienne caduque.
Le vecteur entrant, de la requête anonyme à l'écriture mémoire
- 1
Requête anonyme avec Content-Encoding: br
un attaquant poste un corps Brotli sur une route Express 5 / body-parser 2.x.
- 2
Décompression automatique
le framework décode le corps sans rien demander, et la donnée atteint node.
- 3
Transition de bloc invalide
le flux demande un changement de type de bloc que sa structure interdit.
- 4
Erreur prise pour un manque de données
le décodeur continue au lieu de s'arrêter, et empile les octets dans son tampon de huit octets.
- 5
Écriture hors limites
au-delà du huitième octet, le décodeur corrompt la mémoire voisine.
Crash, fuite ou exécution de code
selon la disposition mémoire et la chance de l'attaquant.
Quelles versions corriger
La faille concernait toutes les lignes supportées avant le 21 octobre. Chaque ligne maintenue a reçu son palier de correctif ; les lignes en fin de vie, elles, n'en auront jamais.

Deux subtilités valent d'être posées. La première, le piège du runtime embarqué : Node.js embarque sa copie de Brotli dans son binaire. Une image conteneur figée sur une ancienne balise, un exécutable autonome ou un runtime fourni par une plateforme managée restent vulnérables tant qu'ils n'ont pas été reconstruits sur un Node.js corrigé. C'est le binaire qui tourne qui compte, pas la version installée sur la machine qui l'a construit.
La seconde tient à ce que vous lisez. Une balise flottante comme node:24 pointera vers une image corrigée après un rebuild, là où une épingle stricte comme node:24.22.0 reste figée sur la version vulnérable. Et un rétroportage de distribution (Debian, Ubuntu, RHEL, Alpine) peut corriger le paquet Brotli système sans changer le numéro de Node : sur ces systèmes, node --version ne dit pas tout.
Comment tester sans rien casser
Attention au piège, ici. node2shell ne se teste pas comme php2shell, où une charge inoffensive confirme la faille. La seule charge qui la prouverait vraiment est une entrée Brotli malformée, c'est-à-dire exactement celle qui déclenche le bug et peut planter un worker de production. On ne fait pas ça sur ses propres serveurs, encore moins sur ceux d'un tiers.
La bonne approche se mène en deux temps : lire les versions d'un côté, vérifier que le vecteur entrant est ouvert de l'autre, sans jamais envoyer d'entrée dangereuse.
Lire la version réellement en service
C'est la seule chose qui tranche avec certitude. Lisez-la là où le code tourne vraiment, pas sur votre poste :
# Sur un hôte ou un runner CI
node --version
# Dans une image conteneur, sans la lancer en service
docker run --rm votre-image node --versionUne version sous le palier de sa ligne (24.22.0, par exemple) est vulnérable ; une version en fin de vie l'est et le restera.
Repérer les dépendances qui ouvrent le vecteur entrant
Même sur un runtime vulnérable, encore faut-il qu'une porte soit ouverte. Cherchez Express 5 et body-parser 2.x, puis les appels directs au décodeur :
npm ls express body-parser
grep -rn "brotliDecompress\|createBrotliDecompress" --include=*.js --include=*.ts .Et inventoriez les fetch() sortants vers des serveurs que vous ne maîtrisez pas. Ce sont les trois portes du diagramme, à vérifier une par une.
Vérifier si le vecteur entrant est ouvert, sans danger
On peut confirmer qu'un serveur décompresse le Brotli entrant sans jamais lui envoyer de malformé : il suffit de poster un flux valide et de regarder s'il est décodé. C'est le principe de notre template.
# Corps = Brotli bien formé de {"a":1}, envoyé avec Content-Encoding: br.
# S'il est décompressé, la route répond 200/204/404, jamais 415.
printf '\x0b\x03\x80{"a":1}\x03' \
| curl -s -o /dev/null -w "avec br -> %{http_code}\n" \
-X POST https://votre-domaine/ \
-H 'Content-Type: application/json' \
-H 'Content-Encoding: br' --data-binary @-
# Mêmes octets, SANS l'en-tête : binaire brut, le parseur JSON doit répondre 400.
printf '\x0b\x03\x80{"a":1}\x03' \
| curl -s -o /dev/null -w "sans encodage -> %{http_code}\n" \
-X POST https://votre-domaine/ \
-H 'Content-Type: application/json' --data-binary @-Si la première commande renvoie 200, 204 ou 404 et que la seconde renvoie 400, le serveur décompresse réellement le Brotli entrant : la porte est ouverte. Un 415 sur la première, et la stack refuse le Brotli entrant (Express 4). Ce test prouve que le vecteur est joignable, pas que le runtime est vulnérable : Node.js ne publie sa version nulle part en HTTP. C'est pour ça qu'on le couple toujours à la lecture de version.
Le plan de remédiation, par ordre d'efficacité
- Mettez à jour le runtime vers 26.12.1, 24.22.1 ou 22.23.4 (ou plus récent). C'est la seule mesure qui ferme les trois portes. Sur une ligne en fin de vie, il faut migrer.
- Reconstruisez ce qui embarque le runtime. Images conteneurs, exécutables autonomes, fonctions serverless : une mise à jour de l'hôte ne touche pas un runtime figé dans un artefact. Vérifiez la version à l'intérieur de chaque image déployée.
- Colmatez le vecteur entrant le temps du déploiement : rejetez
Content-Encoding: brsur les chemins exposés, au reverse proxy ou dans un middleware. Vercel, par exemple, a ajouté des règles de pare-feu entrant dès le 21 octobre. - N'oubliez ni
fetch()ninode:zlib. Le filtre entrant ne protège pas vos appels sortants ni vos décompressions dans le code. Pour eux, seule la mise à jour compte.
Repérer les tentatives dans vos journaux
Vous n'avez rien à envoyer pour chercher des traces. Trois signaux méritent une requête : des POST portant Content-Encoding: br sur des routes qui n'ont aucune raison d'en recevoir, surtout en rafale ; des sorties de processus inattendues ou des erreurs de décodeur Brotli, en gardant à l'esprit qu'un crash isolé n'est pas une preuve ; et côté sortant, des réponses Brotli anormales sur vos fetch() vers des tiers.
La leçon : node2shell et php2shell, le même jour, le même angle mort
Revenons à ce 21 octobre. Node.js et PHP ont publié leurs correctifs le même jour, chacun avec sa faille « 2shell ». Le point commun n'est pas technique, il est organisationnel : aucune des deux ne se corrige en touchant au code de vos applications. Ce sont des failles de socle, et le socle est précisément ce que les inventaires oublient.
Le pentest annuel ne les voit pas : une faille divulguée le 21 octobre n'existe pas dans un rapport du printemps. Le scan applicatif cherche des vulnérabilités dans votre code, pas dans la version du moteur qui l'exécute. Et la gestion des dépendances attrape Express et body-parser, mais pas toujours la version du runtime figée dans une image buildée il y a trois mois.
Pour combler cet angle mort, il faut deux choses qui marchent ensemble : un inventaire de composants qui lit les versions réellement embarquées, et une cartographie de la surface externe qui retrouve les services exposés, y compris le microservice Node oublié sur un sous-domaine. C'est le même réflexe que pour toute la série : cartographier son système d'information et faire l'inventaire de sa surface d'attaque externe avant que la prochaine faille de socle ne tombe.
Détecter node2shell en continu avec Flawfence
Chez Flawfence, la logique ne change pas d'une faille à l'autre, et c'est ce qui permet de réagir en heures plutôt qu'en semaines. Le moteur cartographie le périmètre, retrouve les services exposés, Shadow IT compris, et situe chaque actif dans les plages vulnérables connues, sans jamais envoyer de charge destructrice.
Face à node2shell, Flawfence :
- découvre vos services Node exposés, sur tous vos domaines et sous-domaines, en repérant la façade Express ;
- vérifie si le vecteur entrant est ouvert, avec du Brotli bien formé uniquement, jamais l'entrée malformée qui déclenche le bug ;
- situe la version quand un artefact la révèle, en lisant les épingles exactes de vos Dockerfile,
.nvmrcou workflows CI, et en ignorant les balises flottantes ; - surveille en continu, pour qu'un nouveau service exposé ou un retour en arrière de version déclenche une alerte.
Les templates Nuclei que nous avons écrits
Pour couvrir les deux faces du problème, la joignabilité du vecteur et la version réelle, nous maintenons deux templates. Ils sont pensés pour ne jamais déclencher le bug : le réseau n'envoie que du Brotli bien formé, le fichier ne fait que lire.
Le premier ne se contente pas de voir « du Express ». Il exige trois signaux simultanés pour écarter les faux positifs : un parseur JSON qui rejette le binaire brut (400), une décompression Brotli réellement effectuée sur les mêmes octets bien formés (200/204/404, jamais 415 ni 5xx), et une identité Express/Node confirmée. La charge est toujours un flux valide : il confirme une configuration exploitable, il ne l'exploite pas.
id: CVE-2026-111497-flawfence
info:
name: Node.js Brotli Decoder OOB Write (CVE-2026-111497, "node2shell") - Exploitable Inbound Config
author: Flawfence
severity: high
description: |
Confirmation blackbox, non destructive, de la configuration entrante exploitable de
node2shell. Exige trois signaux simultanés ; n'envoie que du Brotli BIEN FORMÉ, jamais
d'entrée malformée, donc ne peut pas déclencher le bug ni planter un worker.
classification:
cvss-metrics: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
cvss-score: 9.2
cve-id: CVE-2026-111497
cwe-id: CWE-787
tags: nodejs,node2shell,express,brotli,cve,cve2026
http:
- raw:
- |
GET / HTTP/1.1
Host: {{Hostname}}
Accept: */*
# Brotli valide de {"a":1} AVEC Content-Encoding: br -> doit être décompressé
- |
POST / HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
Content-Encoding: br
{{base64_decode("CwOAeyJhIjoxfQM=")}}
# Mêmes octets, SANS encodage -> binaire brut, doit casser le parseur JSON (400)
- |
POST / HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{{base64_decode("CwOAeyJhIjoxfQM=")}}
# Chemin aléatoire -> Express/finalhandler émet "Cannot GET /<rand>"
- |
GET /{{randstr}} HTTP/1.1
Host: {{Hostname}}
Accept: */*
req-condition: true
matchers-condition: and
matchers:
- type: dsl
name: json-parser-rejects-raw-binary
dsl:
- "status_code_3 == 400"
- type: dsl
name: brotli-body-decompressed
dsl:
- "status_code_2 == 200 || status_code_2 == 204 || status_code_2 == 404"
- type: dsl
name: express-node-identity
dsl:
- "contains(to_lower(header_1), 'x-powered-by: express') || contains(to_lower(header_2), 'x-powered-by: express') || contains(to_lower(header_3), 'x-powered-by: express') || contains(to_lower(header_4), 'x-powered-by: express') || contains(body_2, 'Cannot POST /') || contains(body_4, 'Cannot GET /')"Un match prouve que le chemin de décodage entrant est vivant, pré-authentification. Il ne prouve pas le niveau de correctif : d'où la vérification de version côté hôte qui l'accompagne.
Le second template ne touche à rien sur le réseau. Il lit la version épinglée dans les artefacts d'un projet (FROM node:x.y.z, .nvmrc, .node-version, le champ engines d'un package.json, un node-version de workflow CI) et la compare aux plages affectées. Un match, ici, est une certitude. Les balises flottantes comme node:24 sont ignorées à dessein : après un rebuild, elles pointent vers une image corrigée.
id: CVE-2026-111497-flawfence-pinned-version
info:
name: Node.js Brotli Decoder OOB Write (CVE-2026-111497, "node2shell") - Affected Runtime Pinned
author: Flawfence
severity: high
tags: nodejs,node2shell,brotli,cve,cve2026,file
file:
- extensions:
- all
extractors:
- type: regex
name: node_version
internal: true
group: 1
regex:
- "(?i)FROM\\s+[^\\n]*node:v?([0-9]+\\.[0-9]+\\.[0-9]+)"
- "(?i)\"node\"\\s*:\\s*\"[~^>=\\s]*v?([0-9]+\\.[0-9]+\\.[0-9]+)\""
- "(?i)node-version:\\s*[\"']?v?([0-9]+\\.[0-9]+\\.[0-9]+)"
- "(?m)^v?([0-9]+\\.[0-9]+\\.[0-9]+)\\s*$"
matchers-condition: or
matchers:
- type: dsl
name: affected-supported-below-fix
dsl:
- "compare_versions(node_version, '>= 22.0.0', '< 22.23.4') || compare_versions(node_version, '>= 24.0.0', '< 24.22.1') || compare_versions(node_version, '>= 26.0.0', '< 26.12.1')"
- type: dsl
name: affected-eol-no-fix
dsl:
- "compare_versions(node_version, '>= 23.0.0', '< 24.0.0') || compare_versions(node_version, '>= 25.0.0', '< 26.0.0') || compare_versions(node_version, '< 22.0.0')"Ensemble, ils répondent aux deux seules questions qui comptent : le vecteur est-il joignable, et la version embarquée est-elle affectée. Ni l'un ni l'autre n'envoie l'entrée malformée qui déclenche la faille.
La chronologie de node2shell
De la correction amont à la preuve de concept
Avant la divulgation
Février 2026
Correctif amont
la bibliothèque Brotli corrige la transition de bloc invalide (commit 0ed4268a), publiée dans les versions 1.0.10, 1.1.1 et 1.2.1.
Signalement
OpenAI Codex Security
Khai Tran et Sylvie Mayer rapportent la faille au projet Node.js.
La divulgation
21 octobre 2026
Publication
Node.js sort 22.23.4, 24.22.1 et 26.12.1, corrigeant dix vulnérabilités dont node2shell. PHP publie ses propres correctifs le même jour.
21 octobre 2026
Premiers colmatages
des plateformes managées ajoutent des règles de pare-feu entrant contre Content-Encoding: br.
22 octobre 2026
Preuves de concept
plusieurs PoC publiques paraissent ; Imperva rapporte une exécution de code sur une image 24.22.0 officielle, avec une réussite partielle.
23 octobre 2026
Pas d'exploitation de masse
aucune exploitation en conditions réelles recensée, faille absente du catalogue KEV de la CISA.
Questions fréquentes sur node2shell
Qu'est-ce que node2shell ?
node2shell est le surnom de CVE-2026-111497, une écriture hors limites (CWE-787) dans le décodeur Brotli que Node.js embarque dans son binaire. Une entrée malformée fait prendre au décodeur une erreur de format pour un manque de données : il écrit au-delà d'un tampon de huit octets. Résultat possible : crash, fuite de mémoire, ou exécution de code à distance, sans authentification.
Quelles versions de Node.js sont vulnérables ?
Toutes les lignes supportées avant le 21 octobre 2026. Le correctif est arrivé avec 22.23.4, 24.22.1 et 26.12.1. Les lignes en fin de vie (25.x, 23.x, 21.x, 20.x et antérieures) n'auront aucun correctif : il faut migrer. Bun est concerné avant la 1.4.4.
node2shell est-il activement exploité ?
Au 23 octobre 2026, aucune exploitation en conditions réelles n'a été recensée et la faille n'est pas au catalogue KEV de la CISA. Mais des preuves de concept circulent depuis le 22 octobre, dont une qui a exécuté du code sur une image officielle non modifiée, avec une réussite partielle. Le crash et la fuite mémoire, eux, sont fiables. Corrigez sans attendre une exploitation de masse.
Suis-je concerné si je n'utilise pas Express ?
Peut-être. Express et body-parser 2.x sont la porte la plus exposée, mais pas la seule. fetch() décompresse automatiquement les réponses Brotli des serveurs que vous appelez, et tout appel direct à brotliDecompress sur une entrée non fiable ouvre aussi la faille. La vulnérabilité est dans le runtime, pas dans Express.
Mettre à jour le paquet Node.js de mon serveur suffit-il ?
Pas si votre application tourne dans un conteneur, un exécutable autonome ou une fonction serverless. Node.js embarque sa copie de Brotli : il faut reconstruire l'artefact sur un runtime corrigé, puis vérifier la version à l'intérieur de l'image déployée. Une image figée sur une ancienne balise reste vulnérable même si l'hôte est à jour.
Le template de détection est-il intrusif ou dangereux ?
Non. Le template réseau n'envoie que du Brotli bien formé (il se décode en {"a":1}, sans amplification), jamais l'entrée malformée qui déclenche le bug : il ne peut donc ni l'exploiter ni planter un worker. Il poste un petit corps et observe des codes de statut, rien de plus. Le template fichier, lui, ne fait que lire, sans aucun trafic réseau.
Bloquer Content-Encoding: br au pare-feu me protège-t-il ?
Partiellement, et temporairement. Ce filtre ferme le vecteur entrant, mais ne touche ni vos appels fetch() sortants ni vos décompressions dans le code. C'est une mesure d'attente, jamais un substitut. La seule remédiation complète est la mise à jour du runtime.
En résumé
node2shell rappelle une vérité que les inventaires de code oublient : la faille la plus grave n'est pas toujours dans votre application, elle est parfois dans le moteur qui l'exécute. Une erreur de format prise pour un manque de données, un tampon de huit octets qui déborde, et le décodeur Brotli au cœur de Node.js devient une primitive d'écriture mémoire joignable sans authentification.
La bonne nouvelle est la même que pour le reste de la série : se protéger ne demande ni exploit destructeur, ni pentest à 20 k€. Il faut lire les versions réellement embarquées, retrouver les services exposés que vos inventaires ont perdus de vue, et surveiller en continu plutôt qu'une fois l'an. C'est exactement ce que Flawfence fait, pour node2shell comme pour la prochaine faille de socle qui n'a pas encore de nom.
Vérifiez votre exposition dès maintenant, il ne vous faudra que 5 minutes. Et commencez par la seule chose qui tranche vraiment : la version de Node.js qui tourne réellement en production.
Discutons de votre exposition externe
Demandez une démo personnalisée de Flawfence et découvrez votre niveau d’exposition réel.
