La panne CrowdStrike résultait d'une discordance de 21 contre 20 champs. Kestrel contrôle le code avant tout redémarrage.
CrowdStrikeSécurité des terminauxRésilience informatique

Le crash CrowdStrike tenait à un décompte : 21 champs au lieu des 20 attendus. Aucune couche indépendante ne vérifiait.

Ashutosh SinghalAshutosh Singhal20 juillet 202611 min

Le 19 juillet 2024, une simple mise à jour fournisseur a fait planter des millions de machines Windows en moins de 90 minutes, et la cause en était un nombre. Un fichier de canal « Rapid Response Content » de CrowdStrike déclarait 21 champs là où l'interpréteur de noyau déployé en attendait 20. Ce champ supplémentaire a provoqué une lecture hors limites (out-of-bounds read), un écran bleu instantané, et parce que le crash est survenu si tôt lors du démarrage, l'agent en panne n'a jamais pu redémarrer pour recevoir un ordre de rollback. La récupération a exigé de se rendre physiquement auprès de chaque machine pour la réparer manuellement en mode sans échec (Safe Mode).

J'ai lu l'analyse des causes profondes publiée par CrowdStrike en août plus d'une fois avant de comprendre ce qui me dérangeait. Ce n'était pas un piratage. Ce n'était pas un mauvais modèle. C'était un fait arithmétique décidable, 21 contre 20, logé dans une charge utile qu'aucune couche indépendante n'avait jamais vérifiée avant d'atteindre la production. Le validateur du fournisseur l'a approuvée. Les entreprises paralysées ne possédaient pas ce validateur. Elles en ont subi les conséquences.

J'ai passé la dernière période à construire une démonstration autour de cette faille : une console que j'ai nommée Kestrel, qui s'intercale entre un éditeur de logiciels et un parc en production, et qui décide, par le code, de ce que l'éditeur est autorisé à livrer. Vous pouvez observer son fonctionnement sur veriprajna.com/demos/software-update-integrity. Ce qui m'a surpris lors de sa construction, c'est l'endroit où résidait la solution. J'étais parti convaincu qu'il me faudrait un modèle plus intelligent, et quelques lignes de simple Python ont intercepté le crash en premier.

J'ai reconstitué le crash, puis j'ai laissé le code trancher

J'ai reconstruit la signature de défaillance du 19 juillet sous forme de jeu d'essai (fixture) et j'y ai confronté mon propre système, m'attendant presque à être déçu par mon propre rejeu. Le paquet est C-00000291, un fichier de canal Rapid Response Content provenant d'un fournisseur fictif que j'ai appelé SentinelEdge, poussé vers un parc synthétique de 8 500 terminaux nommé Acme Financial. Aucune de ces entités n'est une vraie entreprise. La signature de défaillance est quant à elle bien réelle : un schéma déclaré de 20 champs passant à 21, déployé à 100 % du parc en une seule vague, sans plan de déploiement progressif (canary).

La porte de contrôle (gate) s'active sur quatre vérifications simultanées, et chacune relève d'une arithmétique élémentaire ou d'une consultation directe, jamais d'un jugement subjectif. Le différentiel de schéma détecte 21 champs là où l'interpréteur en attend 20 et signale la lecture hors limites. Un bac à sable simulé, qui est un modèle déterministe de résultats par profil et non une ferme de vraies machines virtuelles Windows, déclenche une boucle de redémarrage sur 5 des 6 profils du parc à travers les cycles de redémarrage. Il déduit cela d'un signal de compatibilité de pilote indépendant de la vérification de schéma, de sorte que les deux conclusions se corroborent mutuellement plutôt que de se faire écho. Le détecteur d'agent inerte (dead-agent) marque la boucle de rollback comme vraie, car l'agent qui plante est lui-même celui qui devrait recevoir le rollback, et il est inerte avant la fin du démarrage. Le rayon d'impact est de 100 % contre une politique canary de 5 %. Verdict : BLOCK. À l'écran, on peut lire : bloqué avant qu'un quelconque terminal de production n'ait redémarré.

La console Kestrel affichant le panneau Block Rollout pour le paquet C-00000291 du fournisseur SentinelEdge, avec les quatre preuves déterministes, un parc de 8 500 terminaux et une estimation de 5 000 000 $ d'interruption évitée.
C-00000291, la signature du 19 juillet rejouée. La porte déclenche l'ensemble des quatre contrôles : incohérence de nombre de champs du schéma (20 attendus, 21 fournis), boucle de redémarrage en bac à sable sur 5/6 profils, boucle de rollback d'agent inerte, et rayon d'impact de 100 % contre une politique canary de 5 %. Verdict BLOCK, temps d'arrêt évité estimé à 5 000 000 $.

L'estimation du temps d'arrêt évité sur cette seule mise à jour s'élève à 5 000 000 $, et je tiens à être précis sur la signification de ce montant. Il s'agit du modèle propre à la démo : la part affectée multipliée par une entrée de 5 M$ par heure et par un plancher de récupération d'une heure, la formule étant affichée à l'écran. Ce n'est pas de l'argent réel économisé par un client. La récupération réelle du 19 juillet a pris des jours, pas une heure ; le plancher est donc délibérément conservateur.

Le cas vert m'a fait plus peur que le rouge

J'étais plus inquiet pour le cas vert que pour le rouge, car une couche de gouvernance qui bloque la mise à jour dangereuse mais étouffe également celle qui est sûre n'est rien d'autre qu'une panne que vous avez vous-même programmée. Le même fournisseur fictif pousse RRC-7741, une mise à jour bénigne de signature de détection, avec un schéma déclaré de 20 pour 20 et un plan canary échelonné à 1,2 %. L'équipe d'agents s'exécute, le schéma correspond, 5 profils sur 6 valident leurs cycles de redémarrage, la boucle d'agent inerte est fausse, le rayon d'impact reste dans les limites de la politique. Verdict : APPROVE ROLLOUT, diffusé à un groupe canary de 102 terminaux. Vert, rapide, sans histoire.

La console Kestrel affichant le panneau vert Approve Rollout pour RRC-7741 diffusé à un groupe canary de 1,2 %, avec la trace d'évaluation 7/7 et l'enregistrement de preuves.
La mise à jour bénigne du même fournisseur, RRC-7741. Le schéma correspond, 5/6 profils réussissent leurs cycles de redémarrage, boucle d'agent inerte fausse, rayon d'impact de 1,2 % conforme à la politique. Verdict APPROVE ROLLOUT, diffusé à un canary de 102 terminaux, enregistrement de preuve sha256:798431b4c96612a9.

Sur les six mises à jour bénignes du jeu d'essai, la porte a produit zéro faux blocage. Je l'affirme en mentionnant le dénominateur, parce que six, c'est six, et je refuse que cela soit arrondi en une promesse pour votre propre parc. La valeur du cas ALLOW est plus circonscrite et plus essentielle qu'un simple pourcentage. Une porte n'est crédible que si elle est invisible sur le trafic normal et intraitable lors de l'unique incident susceptible de terrasser votre parc.

Pourquoi j'ai retiré le verdict du modèle

J'ai abordé cette construction en supposant que la difficulté résidait dans le raisonnement, et qu'un modèle plus affûté ou un critique plus astucieux serait l'élément qui intercepterait la mauvaise mise à jour. J'avais tort, d'une manière qu'il m'a fallu du temps pour admettre. Il y a bien une équipe de LLM au sein de Kestrel : un normalisateur, un interpréteur de bac à sable et deux critiques opposés, l'un plaidant que la mise à jour est sûre à livrer et l'autre soutenant qu'elle va planter. Cette paire contradictoire justifie sa présence, car elle soumet le verdict à un examen approfondi (red-teaming) sous tous les angles avant toute décision. Mais pas un seul de ces agents ne fixe le verdict.

Le verdict est établi par deux simples fichiers Python, verifier.py et gate.py, qui résident entièrement hors du cadre des agents. L'équipe tourne sur Pydantic AI avec pour modèle par défaut claude-opus-4-8, et l'ensemble fonctionne également hors ligne, sans clé API, grâce à un mécanisme consultatif déterministe de secours. Dans chacun de ces modes, la porte est identique et rend la même décision, parce que la décision relève de l'arithmétique, pas de l'inférence. Les agents conseillent, le code décide. Un agent consultatif penchant pour « autoriser » ne peut pas annuler un résultat déterministe critique, et ce n'est pas une affaire de goût.

Une couche conçue pour contrôler le fournisseur ne peut pas croire le fournisseur sur parole quant à la sécurité. Elle ne peut pas non plus croire sur parole son propre modèle.

Cette phrase explique l'architecture même du système. La confiance en un produit dont l'unique mission est de gouverner ce qu'un fournisseur livre ne doit jamais transiter par un composant susceptible d'être persuadé de dire oui.

Ce que je remettrais à un auditeur

J'ai gardé le Cyber Resilience Act de l'UE ouvert sur un second écran pendant que j'élaborais l'enregistrement de preuves, car cet enregistrement est l'artefact que j'aurais réellement à défendre. Chaque décision exporte un fichier HTML immuable et un fichier JSON signé portant un hachage de contenu SHA-256, le verdict, les preuves déterministes, les résultats du bac à sable par profil, les verdicts des agents consultatifs avec l'identifiant de leur modèle, les règles de politique déclenchées et une trace d'évaluation étape par étape où chaque étape indique sa propre latence.

La vue de décision de Kestrel pour le paquet C-00000291 bloqué, montrant l'enregistrement de preuve avec sha256:0f4f71b2bd7d1753 et les boutons pour ouvrir l'enregistrement HTML et le JSON signé.
L'enregistrement de preuve exporté pour la mise à jour bloquée. Un hachage de contenu SHA-256, un enregistrement HTML et un fichier JSON signé, générés directement sur la décision elle-même.

La trace est l'élément que j'avais sous-estimé jusqu'à ce que je clique sur une étape individuelle. Un événement indique : « Normalisation du manifeste fournisseur signé, terminée en 184 ms », et il est conservé avec le résultat de la décision pour la révision d'audit. Chaque étape peut être recalculée. Un régulateur n'a pas à croire mon tableau de bord sur parole. Il peut réexécuter l'arithmétique et obtenir le même résultat.

Une fenêtre modale d'étape de trace d'évaluation de Kestrel affichant « Normaliser le manifeste fournisseur signé, terminé en 184 ms », avec une mention indiquant que l'événement est conservé pour l'audit.
Une étape de la trace d'évaluation, ouverte. Normaliser le manifeste fournisseur signé, terminé en 184 ms, conservé avec la sortie de décision pour examen d'audit.

Je veille à préciser ce qu'est cette signature et ce qu'elle n'est pas. Il s'agit d'un SHA-256 local, pas d'une infrastructure à clés publiques (PKI) d'entreprise. Le flux de mises à jour du fournisseur et les tickets ITSM sous-jacents sont des bouchons d'essai (stubs), pas des connecteurs en direct. L'enregistrement est conçu pour s'aligner sur les exigences de déclaration : le signalement d'incident à court délai du CRA, la divulgation sous quatre jours ouvrés d'un incident de cybersécurité matériel exigée par la SEC, les questions de responsabilité des fournisseurs soulevées par l'affaire Delta v. CrowdStrike dans le comté de Fulton en 2025. Conçu pour s'aligner avec. Il ne certifie personne, ne constitue aucunement un conseil juridique, et quiconque vous vend un journal d'audit en prétendant qu'il vous rend conforme cherche simplement à vous vendre quelque chose.

Il y a une autre décision dont je suis fier, et c'est un refus. Le jeu d'essai XX-0000 est un bloc de contenu propriétaire chiffré que la porte ne peut pas analyser : elle ne devine donc pas. Elle renvoie ABSTAIN et transmet à un opérateur humain, car une porte qui valide ce qu'elle ne sait pas lire est pire qu'aucune porte. Les hôtes hérités que le bac à sable ne peut pas modéliser sont signalés et exclus, jamais présumés sûrs. Le vocabulaire se résume à quatre termes : ALLOW, HOLD, BLOCK, ABSTAIN, et ce dernier est celui que je défendrais avec le plus d'énergie.

Ce que 12 sur 12 a le droit de signifier

Je dois ralentir ici, car c'est précisément à cet endroit qu'un fondateur commence à enjoliver les chiffres. Or, j'ai nommé l'entreprise Veriprajna, « vraie sagesse », de sorte que l'arrondi complaisant est exclu. Sur un ensemble fixe et étiqueté de douze mises à jour, la porte rend la décision exacte sur les douze. Six d'entre elles sont bénignes et elle n'en bloque aucune. L'une d'elles correspond à l'honnête ABSTAIN. Le tableau d'affichage indique 12/12 décisions vérifiées, 0/6 faux blocages et 13,3 M$ d'interruption évitée estimée sur l'ensemble, dont 5 M$ pour le seul blocage de classe CrowdStrike.

Le panneau de référence Kestrel affichant 12/12 décisions vérifiées, 0/6 faux blocages et 13,3 M$ d'exposition évitée sur l'ensemble de jeux d'essai de diffusion étiquetés.
Le tableau de valeur sur l'ensemble de tests étiquetés. 12/12 décisions vérifiées, 0/6 faux blocages sur les mises à jour bénignes, 13,3 M$ d'interruption estimée évitée sur l'ensemble. Petits dénominateurs, assumés délibérément.

Venons-en à la partie que je refuse d'abréger. Ce sont des résultats obtenus sur douze éléments étiquetés, pas une promesse sur la prochaine mise à jour qui atterrira sur votre parc. Six éléments bénins restent six. Cela ne signifie pas « bloque 100 % des mauvaises mises à jour », cela ne le sera jamais, et si vous me surprenez un jour à écrire cette phrase, vous devriez cesser de me lire. Le chiffre que je défends est d'une autre nature. Même entrée, même décision, à chaque exécution, car le verdict ne comporte aucune température de modèle. Exécutez à nouveau le jeu d'essai demain et il renverra un résultat identique à l'octet près, ce qui permet à une couche déterministe d'être auditée là où une couche probabiliste ne le peut pas.

La question qui m'accompagne

Ce qui me marque dans cette expérience, c'est la banalité de la défaillance. Vingt et un champs là où vingt étaient attendus. Un nombre que n'importe quel vérificateur indépendant aurait pu intercepter par simple arithmétique avant même qu'une seule machine ne redémarre, si un vérificateur indépendant s'était tenu entre le fournisseur et le parc. Il n'y en avait aucun. Aujourd'hui encore, il n'y en a pratiquement aucun.

Chaque entreprise fait tourner huit à douze agents privilégiés au niveau du noyau provenant de fournisseurs qu'elle ne contrôle pas, et chacun d'eux peut injecter un fichier directement dans le ring 0. Les outils SBOM surveillent les dépendances open source. L'identité surveille les accès. Personne ne lit la mise à jour propriétaire du fournisseur à son arrivée pour prouver son innocuité. Kestrel n'est pas un EDR et ne touche jamais au noyau. Il se place au-dessus de ces agents et régit ce qu'ils ont le droit de livrer. C'est cette couche que j'ai tenté de construire, et la présentation intégrale se trouve sur veriprajna.com/demos/software-update-integrity.

Et si vous préférez le voir en action plutôt que de me lire le décrire, voici le système complet s'exécutant de bout en bout.

Voici donc ce que je demande désormais à propos de chaque parc que je croise : lorsque la prochaine mise à jour fournisseur arrivera, qu'est-ce qui s'interposera entre ce fichier et la production, et cela peut-il apporter la preuve de son travail ? Si la réponse est un comité consultatif des changements (CAB) qui fait aveuglément confiance au fournisseur, alors l'arithmétique qui a terrassé des millions de machines continue de tourner sans aucun contrôle. Elle ne s'annoncera pas. Elle ressemblera exactement à toutes les mises à jour qui l'ont précédée, jusqu'à la seconde même du redémarrage.

Recherche associée

Également publié sur

Développez votre IA en toute confiance.

Collaborez avec une équipe forte d'une solide expérience dans la conception de la prochaine génération d'IA d'entreprise. Nous vous aidons à concevoir, développer et déployer une stratégie d'IA digne de confiance.

Veriprajna société de conseil en Deep Tech est spécialisée dans la conception de systèmes d'IA critiques pour la sûreté destinés aux secteurs de la santé, de la finance et de la réglementation. Nos architectures sont validées au regard de protocoles établis et accompagnées d'une documentation de conformité complète.