L'exploit est facile. Savoir quoi chercher est dur

Test d'intrusionPar l'équipe SwarmAttacker chez JolocorpPublié 8 min de lecture

Frelon robotique noir pointant un laser rouge sur un seul endroit d'une large grille radar : trouver le signal en test d'intrusion

Voici ce dont personne ne nous avait prévenus à propos des tests d'intrusion automatisés sur un modèle frontière actuel : écrire l'exploit s'est révélé être la partie facile. Pointez SwarmAttacker sur une application web vulnérable, demandez-lui de produire une commande fonctionnelle ou une requête forgée, et il le fait. Proprement. Les ennuis commencent une étape plus tôt, dans la partie qui ne figure jamais dans un tutoriel : comprendre ce que l'on est même en train de regarder.

Nous avons repassé en revue chaque cible manquée par l'agent, en nous attendant à trouver le modèle en train de trébucher sur la syntaxe ou de choisir la mauvaise technique. Nous n'avons pas trouvé cela. Pas une seule fois une cible ne nous a échappé parce que l'agent ne savait pas écrire le code. Quand il échouait, c'était pour une raison plus discrète : la bonne idée n'a jamais été formée, ou jamais poursuivie assez loin.

Bonne classe, mauvaise technique : là où le pentest automatisé cale

Sur l'ensemble de la suite, SwarmAttacker était presque toujours dans la bonne zone. Il nommait correctement la classe de vulnérabilité. Il établissait souvent un point d'appui fonctionnel. Il savait qu'un formulaire de connexion était probablement injectable, il savait où circulait l'entrée utilisateur, il connaissait la forme du problème. Ce qu'il ne parvenait pas toujours à faire, c'était tomber sur la technique exacte que la cible exigeait.

Savoir qu'une cible a une injection SQL n'est pas la même chose que savoir qu'elle est aveugle et basée sur des booléens.

Cet écart paraît petit. Il ne l'est pas. La distance entre « il y a une injection ici » et « voici la technique précise, aveugle, un bit à la fois, qui extrait réellement les données » est là où vivait une bonne part de nos échecs. L'exploit était à portée de main tout du long. Le diagnostic était le mur.

Là où le mur est le plus haut : l'injection SQL aveugle

Si vous voulez voir cela en un seul chiffre, regardez l'injection SQL aveugle. Sur toute la suite, c'était la pire classe que l'agent ait affrontée, résolue seulement 1 fois sur 3. Tout le reste de cette famille, les vulnérabilités bruyantes, bavardes, l'agent l'a presque entièrement balayé.

La raison est exactement le problème du diagnostic, poussé à l'extrême. Un bug de cross-site scripting réfléchi vous crie dessus : vous envoyez une sonde, le payload rebondit directement dans la page, et le signal est sans équivoque. L'injection de commandes, c'est pareil : lancez une sonde et la sortie de la cible elle-même vous dit que cela a marché. La SQLi aveugle n'a pas cette courtoisie. Il n'y a rien à lire. La page est identique que votre hypothèse soit juste ou fausse. La réponse doit être reconstruite une inférence vrai-ou-faux à la fois, et cette boucle longue et fragile, demander, attendre, interpréter une différence si subtile qu'elle pourrait être du bruit, ajuster, redemander, est précisément là où l'agent perdait le plus souvent le fil.

Les signaux bruyants sont balayés ; l'aveugle ne l'est pas

Les classes de vulnérabilité avec une réponse évidente sont presque parfaites. L'injection SQL aveugle, où il n'y a rien de visible à lire, est la seule valeur aberrante faible.

XSS
22/23
IDOR
14/14
SSTI
12/13
Inj. de cmd
11/11
SQLi aveugle
1/3
RésoluesÉchouées

Pourquoi la recherche web n'a-t-elle pas sauvé les échecs difficiles ?

La solution évidente est de donner la recherche web à l'agent, et nous l'avons fait. Sur ces échecs difficiles, elle n'a presque rien sauvé. Cela nous a surpris jusqu'à ce que nous regardions pourquoi. Le goulot d'étranglement n'a jamais été que le modèle ignore la réponse : la réponse à « comment exploiter une injection aveugle basée sur des booléens » est écrite à cent endroits. Le goulot d'étranglement, c'était l'agent incapable de formuler une question assez précise à partir de sa propre sortie d'outil trouble.

La recherche récompense une requête nette. « Le point de terminaison de réinitialisation du mot de passe renvoie un 200 pour les jetons valides comme invalides, et la longueur de la réponse diffère de deux octets » est une question à laquelle on peut répondre. « Quelque chose cloche ici » ne l'est pas. Quand l'agent pouvait déjà articuler le problème, il n'avait généralement pas besoin de chercher. Quand il ne le pouvait pas, aucune recherche n'aidait, parce qu'il n'avait rien de net à chercher.

La réponse du swarm : patient, puis parallèle

Si le diagnostic est la partie difficile, alors la pire chose à faire est de jeter une foule de workers sur une cible avant que quiconque ne l'ait comprise. Cinquante sondes parallèles contre une vulnérabilité que personne n'a lue correctement produisent simplement cinquante rapports faux et sûrs d'eux. SwarmAttacker est donc construit pour aller lentement avant d'essaimer.

Pendant les ~10 premières minutes d'une exécution, il garde délibérément chaque vague petite, seulement 4 workers, pour pouvoir regarder chaque sonde revenir, la lire et replanifier souvent. C'est la phase de lecture, celle où il détermine ce qu'il regarde réellement. Ce n'est qu'une fois qu'il tient de vraies pistes qu'il se déploie plus largement, 6 workers par vague, pour presser l'avantage, le tout dans un plafond strict de 40 minutes.

Ce rythme laisse une empreinte dans les temps. Les temps de résolution sont bimodaux. Il y a une grosse bosse précoce entre dix et quinze minutes environ, les captures en un coup où le signal était bruyant et où l'agent l'a lu tout de suite. Puis une seconde bosse, plus tardive, au-delà de 20 minutes : les cibles qui avaient besoin d'un second acte d'inférence prudente et patiente avant de lâcher quoi que ce soit. La médiane tombe à 15 minutes, et la plupart des résolutions arrivent avant 20, mais la queue difficile, les cibles aveugles, silencieuses, lourdes en diagnostic, arrive lentement.

Les temps de résolution sont bimodaux

Une bosse précoce entre dix et quinze minutes environ pour les captures en un coup, puis une seconde bosse au-delà de 20 minutes pour les cibles qui ont besoin d'un second acte d'inférence prudente.

0
10
20
30
minutes
médiane 15 minplafond 40 min
0
échecs dus à un mauvais exploit
1 / 3
SQLi aveugle, la pire classe
15 min
temps de résolution médian

Ce que cela change

Pendant des années, le modèle mental d'un attaquant automatisé était une machine à code : le danger était qu'il devienne bon pour écrire des exploits. Sur un modèle moderne, ce cap est discrètement passé, et ce n'était pas la partie intéressante. La partie intéressante, encore non résolue, c'est la perception : lire la sortie d'un outil quand on ne sait pas à quoi le signal est censé ressembler, et réduire un vague sentiment de « il y a quelque chose ici » à une seule technique exacte.

C'est le pari que fait toute l'architecture. Si vous voulez le détail des raisons pour lesquelles nous l'avons structurée en vagues patientes-puis-parallèles plutôt qu'en un seul grand swarm, c'est dans comment ça marche. Et si vous voulez la ventilation complète classe par classe, y compris celles qu'il a balayées et celles qui ont résisté, tout est dans les résultats. La version courte : le modèle sait déjà écrire l'exploit. Lui apprendre à savoir ce qu'il cherche, voilà la frontière désormais.