Blog ·
Pourquoi un scanner de vulnérabilités doit envoyer zéro octet
La frontière entre un contrôle et une attaque, ce sont les octets mis sur le fil. Comment faire de cette promesse un nombre sur lequel un test peut échouer.
Par Provibr
Si vous exploitez de l’infrastructure pour d’autres personnes, vous finissez par vouloir savoir si le port 3306 de la machine d’un de vos clients répond à l’internet entier. C’est une question raisonnable, le client voudrait la réponse, et à la seconde où vous allez regarder vous êtes devenu une partie qui envoie du trafic non sollicité vers une machine placée sous le contrat de quelqu’un d’autre.
Ce qui distingue cela d’une intrusion, ce n’est pas votre intention, et ce n’est pas une case à cocher dans vos conditions générales. C’est ce qui a réellement quitté le socket. Tout le reste est un récit sur ce qui a quitté le socket.
Nous avons récemment intégré un contrôle d’exposition dans notre agent, avec une règle qui a commencé comme une phrase dans un document de conception : n’envoyer aucune requête modifiante, aucune tentative de connexion, aucun identifiant par défaut, aucune charge utile d’exploitation, aucune collecte large de bannières. Voici comment cette phrase est devenue quelque chose sur quoi un test peut échouer, et ce que les sondes qui en résultent coûtent en octets.
Une promesse dans un commentaire est une promesse périssable
Le problème de la phrase ci-dessus n’est pas qu’elle soit difficile à suivre. C’est qu’elle est facile à suivre pendant environ six mois.
Quelqu’un ajoute un EHLO à la sonde SMTP pour qu’elle puisse détecter si STARTTLS est proposé, ce qui est une information réellement utile. Les tests existants passent toujours, parce qu’ils portent sur ce que la sonde a trouvé et non sur ce qu’elle a dit. À partir de ce jour, le logiciel envoie des commandes de protocole à des machines clientes, et le document de conception dit toujours qu’il ne le fait pas. Rien n’est passé au rouge. Rien ne le fera jamais.
Nous en avons donc fait un nombre là où un nombre est possible. Chaque socket ouvert par le contrôle est enveloppé dans un flux qui compte les octets écrits, et le total revient avec le résultat. Quatre des six sortes de sonde ont un chiffre exact affirmé dans les tests : zéro pour les deux qui n’envoient rien, trente-deux pour SSH, huit pour PostgreSQL. Les deux autres ne le peuvent pas, car un client hello et un negotiate SMB varient en longueur selon ce qui est proposé dedans ; leur borne est plutôt une forme, à savoir un seul message et rien après. Ajouter un EHLO à une sonde affirmée à zéro est désormais un test rouge avec un nombre dedans.
Deux détails de ce compteur se sont révélés importants.
Il compte les écritures et non les lectures. Ce que l’autre partie offre spontanément n’est pas quelque chose que nous avons causé ; ce que nous envoyons, si. Et c’est un compteur partagé plutôt qu’un champ sur notre propre type de flux, parce que la sonde TLS confie son socket à une bibliothèque qui écrit ensuite ses propres enregistrements de poignée de main par-dessus. Sans compteur partagé, la seule sonde qui utilise le code de quelqu’un d’autre serait la seule sonde non comptée, ce qui est précisément le point sur lequel il est le plus difficile de raisonner quand on tient un budget d’octets.
Ce que coûte chaque sonde
| Sonde | Ce que nous envoyons | Ce qui est lu |
|---|---|---|
| Ouverture seule | rien, affirmé à 0 octet | rien |
| Bannière : FTP, Telnet, SMTP, MySQL | rien, affirmé à 0 octet | un salut borné |
| SSH | une ligne d’identification, affirmée à 32 octets | la ligne d’identification du serveur, puis sa liste d’algorithmes s’il en propose une |
| TLS | un client hello, longueur variable | le hello du serveur et le certificat |
| SMB | un negotiate, longueur variable | la réponse au negotiate |
| PostgreSQL | la requête SSL, affirmée à 8 octets | un octet |
Rien ne suit l’ouverture. Pas de deuxième message, pas de maintien en vie, pas d’achèvement de poignée de main. Une fois que l’autre partie a parlé, le socket se ferme.
SSH : trente-deux octets, et pourquoi il ne peut pas aller plus loin
Le protocole SSH fait envoyer aux deux extrémités une ligne d’identification en premier. La nôtre est une ligne de trente-deux octets, et c’est la seule chose que la sonde écrit. Ensuite elle lit : la ligne d’identification du serveur, puis, si le serveur en envoie un sans y être invité, son message d’échange de clés avec les algorithmes proposés dedans.
La partie importante est ce qui est absent. La sonde n’envoie jamais son propre message d’échange de clés. Ce n’est pas une politique, c’est une borne structurelle : sans lui, l’échange de clés ne peut pas se poursuivre, aucune session n’est jamais établie, et il n’existe donc aucun état du protocole où une demande d’authentification pourrait être envoyée. « Aucune tentative de connexion » cesse d’être une règle dont quelqu’un doit se souvenir.
La ligne d’identification nomme le produit plutôt que d’imiter un client. Un administrateur qui lit son journal d’authentification devrait pouvoir voir qui s’est connecté. Une chaîne neutre ou usurpée ferait passer un contrôle légitime pour de la reconnaissance, ce qui est un mauvais échange contre les deux secondes d’obscurité que cela achète.
Il y a un coût, et il mérite d’être nommé parce qu’il se manifeste comme un problème de qualité de données plutôt que comme une erreur. Certains serveurs attendent le message d’échange de clés du client avant d’envoyer le leur, et le protocole le permet dans les deux ordres. Quand cela arrive, la sonde obtient une chaîne de version et aucune liste d’algorithmes. La couverture de ce port est alors partielle, et elle doit être enregistrée comme partielle. Si elle était enregistrée comme complète avec une liste vide, le moteur de règles conclurait qu’aucun algorithme obsolète n’est proposé, ce qui est la pire sortie possible : un « sûr » assuré, dérivé d’une absence de données.
Les protocoles qui parlent en premier, et la tentation de répondre
FTP, Telnet, SMTP et MySQL vous saluent tous. La sonde pour ceux-là n’envoie absolument rien : se connecter, lire un nombre borné d’octets, fermer. Ce n’est pas une collecte de bannières, car il n’existe aucun moyen d’ouvrir un tel port sans recevoir le salut, et rien n’a été demandé.
La tentation de l’EHLO est réelle et nous avons décidé de ne pas y céder. Savoir si un serveur de messagerie propose STARTTLS a une vraie valeur. Cela exige aussi d’envoyer une commande de protocole, et la règle portait sur les faits que l’autre partie livre sans y être invitée. La conséquence est qu’un constat SMTP atterrit à une gravité et à une confiance plus basses qu’il ne le ferait autrement. C’est le prix honnête et il devrait être visible dans le constat plutôt qu’écarté par un argument.
PostgreSQL est le cas intermédiaire intéressant. Huit octets, c’est le paquet de demande SSL en entier : une longueur et un nombre magique, ce qui est la première chose qu’envoie n’importe quel client et qui n’est ni une requête, ni un identifiant, ni une connexion. Le serveur répond par un octet unique disant s’il parlera TLS. Un octet de réponse suffit à distinguer une base de données qui exige le chiffrement d’une base qui ne l’exige pas, et il n’existe aucune version de ce contrôle qui envoie moins.
Un port ouvert n’est pas une vulnérabilité
Le port 445 qui répond, ce n’est pas SMB1 activé. Ce sont deux faits distincts et ils ont besoin de deux champs distincts.
Notre sonde enregistre un état pour le port, qui est de savoir si quelqu’un est là, et au plus un élément de preuve, qui est ce qu’il a dit. Un negotiate qui revient sans dialecte ancien vous laisse avec un port ouvert et rien d’autre, et c’est un résultat complet et non partiel. La tentation de fondre les deux en une seule gravité est forte parce qu’elle produit un tableau de bord bien plus excitant.
L’hygiène des preuves appartient au même paragraphe, car un salut n’est pas une chaîne inoffensive. Les bannières FTP et SMTP portent en pratique le nom d’hôte du client, parfois le nom d’hôte de son client, et parfois un chemin sur disque. Les stocker brutes fait de votre table de constats un endroit où s’accumulent des informations personnelles et structurelles sur les réseaux d’autres personnes, conservées aussi longtemps que le sont les constats, visibles par quiconque peut lire un constat.
Ce qui sort de la sonde est donc un jeton de protocole normalisé, un fragment de version découpé selon une règle documentée, le nombre d’octets lus, et une empreinte des octets bruts. L’empreinte rend deux analyses comparables sans que personne ne conserve le texte. Cette dernière propriété est celle qui vous manque si vous la sautez : sans elle, « est-ce que ceci a changé depuis le mois dernier » exige la chose que vous avez décidé de ne pas garder.
Le capteur n’a pas d’opinions
Encore une séparation, et c’est celle qui rend le reste maintenable.
L’agent mesure. Il ne résout aucun nom d’hôte, ne tient aucune liste de ports à lui, et ne choisit aucune cible : l’adresse et les ports sont écrits par la plateforme, qui a contrôlé avant de les envoyer que l’adresse est actuellement attribuée à cette organisation et à ce serveur. Sans cible, l’agent ne mesure rien.
Le jugement se fait sur la plateforme, à partir d’un jeu de règles lisible et versionné. Cela compte parce qu’un constat survit au code qui l’a produit. Dans six mois, quelqu’un demandera pourquoi une analyse de septembre a été jugée élevée, et la seule façon d’y répondre est de disposer des règles et des mesures comme de choses séparées, récupérables individuellement. Si la gravité était figée dans le capteur, la réponse serait « parce que cette version de l’agent le pensait », ce qui n’est pas une réponse.
Le même raisonnement explique pourquoi l’agent se connecte vers l’extérieur et n’écoute jamais, et pourquoi les identifiants restent sur l’hôte au lieu de voyager jusqu’à nous. Un composant qui ne fait jamais que rapporter ce qu’il a mesuré est un composant dont vous pouvez décrire le rayon d’explosion en une phrase.
Ce que nous écririons
- Transformez la promesse en un nombre. Si votre règle porte sur ce que vous envoyez, comptez ce que vous envoyez et affirmez sur le compte. Un commentaire ne peut pas échouer.
- Préférez les bornes structurelles. Ne pas envoyer de message d’échange de clés rend l’authentification inatteignable, ce qui est plus fort qu’une règle disant de ne pas s’authentifier.
- Laissez une sonde être partielle. Les données manquantes doivent être visibles comme manquantes, car l’alternative est un négatif assuré que personne ne peut auditer.
- Gardez « ouvert » et « vulnérable » dans des champs différents, et faites combiner les deux par le moteur de règles à un endroit que vous pouvez lire.
- Ne stockez pas de bannières brutes. Normalisez, hachez, et gardez l’empreinte pour la comparaison.
- Mettez l’opinion sur la plateforme, pour qu’un jugement du trimestre dernier reste explicable ce trimestre-ci.
Pour être clair sur notre propre produit : il s’agit d’un seul détecteur, il est derrière un interrupteur au niveau de l’organisation qui est actuellement désactivé, et ce n’est pas quelque chose que vous pouvez acheter aujourd’hui. Les mesures ci-dessus proviennent de l’implémentation et de ses tests, pas d’une page marketing.
Construit pour Provibr, où l’agent tourne à l’intérieur du réseau du client. Le versant vérification de la même conception, prouver qu’un nom d’hôte appartient à celui qui le demande, est dans comment prouver qu’un nom d’hôte est à vous.