
De CrowdStrike-crash kwam neer op een veldentelling: 21 waar de kernel 20 verwachtte. Geen onafhankelijke laag controleerde dit.
Op 19 juli 2024 liet een enkele update van een leverancier miljoenen Windows-machines in minder dan 90 minuten crashen, en de oorzaak was een getal. Een „Rapid Response Content”-kanaalbestand van CrowdStrike declareerde 21 velden waar de geïmplementeerde kernel-interpreter er 20 verwachtte. Het extra veld veroorzaakte een out-of-bounds read, een direct blauw scherm en doordat de crash zo vroeg tijdens het opstarten plaatsvond, kon de crashende agent nooit meer opstarten om een rollback-opdracht te ontvangen. Herstel betekende dat men fysiek naar elke machine moest toelopen en deze handmatig in de Veilige Modus (Safe Mode) moest herstellen.
Ik heb CrowdStrike's eigen root-cause analysis, gepubliceerd in augustus, meer dan eens gelezen voordat hetgeen mij stoorde duidelijk werd. Dit was geen hack. Het was geen slecht model. Het was een beslisbaar rekenkundig feit, 21 tegen 20, genesteld in een payload die door geen enkele onafhankelijke laag ooit was gecontroleerd voordat deze productie bereikte. De validator van de leverancier keurde het goed. De ondernemingen die op zwart gingen, bezaten die validator niet. Zij droegen de gevolgen.
Ik heb de afgelopen periode besteed aan het bouwen van een demo rondom die kloof: een console genaamd Kestrel, die zich tussen een softwareleverancier en een productievloot bevindt en in code beslist wat de leverancier mag verzenden. U kunt zien hoe het werkt op veriprajna.com/demos/software-update-integrity. Wat mij tijdens het bouwen verraste, was waar de oplossing uiteindelijk bleek te zitten. Ik begon met de overtuiging dat ik een slimmer model nodig zou hebben, en een paar regels eenvoudige Python vingen de crash als eerste op.
Ik bouwde de crash na en liet vervolgens code beslissen
Ik heb de foutsignatuur van 19 juli nagebouwd als een testfixture en mijn eigen systeem erop gericht, waarbij ik half verwachtte teleurgesteld te worden door mijn eigen replay. Het pakket is C-00000291, een Rapid Response Content-kanaalbestand van een fictieve leverancier die ik SentinelEdge heb genoemd, uitgerold naar een synthetische vloot van 8.500 endpoints genaamd Acme Financial. Geen van deze bedrijven is echt. De foutsignatuur is wel de echte: een gedeclareerd schema van 20 dat groeit naar 21, uitgerold naar 100% van de vloot in één enkele golf, zonder canary-plan.
De gate slaat aan op vier controles tegelijk, en elk daarvan is pure rekenkunde of een eenvoudige lookup, nooit een subjectief oordeel. De schema-diff ziet 21 velden waar de interpreter er 20 verwacht en markeert de out-of-bounds read. Een gesimuleerde sandbox, wat een deterministisch uitkomstmodel per profiel is en geen farm van echte Windows-VM's, brengt 5 van de 6 vlootprofielen over herstartcycli heen in een bootloop. Het leidt dit af uit een stuurprogrammacompatibiliteitssignaal dat onafhankelijk is van de schemacontrole, zodat de twee bevindingen elkaar bevestigen in plaats van slechts een echo te zijn. De dead-agent-detector markeert de rollback-loop als waar, omdat de crashende agent zelf het onderdeel is dat de rollback zou moeten ontvangen, en deze is dood vóór het voltooien van de opstartprocedure. De blast radius is 100% tegenover een canary-beleid van 5%. Verdict: BLOCK. Op het scherm staat: geblokkeerd voordat enig productie-endpoint herstartte.

De geschatte voorkomen downtime bij die ene update bedraagt $5.000.000, en ik wil exact zijn over wat dat getal inhoudt. Het is het eigen model van de demo: het getroffen aandeel vermenigvuldigd met een input van $5 miljoen per uur vermenigvuldigd met een herstelondergrens van één uur, met de formule op het scherm getoond. Het is geen geld dat een klant heeft bespaard. Het werkelijke herstel op 19 juli duurde dagen, niet één uur, dus de ondergrens is bewust conservatief gekozen.
Het groene geval maakte me angstiger dan het rode
Ik was zenuwachtiger over het groene geval dan over het rode, omdat een governancelaag die de gevaarlijke update blokkeert maar ook de veilige verstikt, gewoon een storing is die u voor uzelf hebt ingepland. Dezelfde fictieve leverancier pusht RRC-7741, een goedaardige detectiesignatuurupdate, met gedeclareerd schema 20 naar 20 en een gefaseerd canary-plan van 1,2%. De agentcrew draait, het schema klopt, 5 van de 6 profielen voltooien hun herstartcycli zonder fouten, de dead-agent-loop is onwaar, de blast radius blijft binnen het beleid. Verdict: APPROVE ROLLOUT, vrijgegeven aan een canary-ring van 102 endpoints. Groen, snel, rimpelloos.

Over de zes goedaardige updates in de set produceerde de gate nul foutieve blokkades. Ik zeg dat met de noemer erbij vermeld, want zes is zes, en ik laat dat niet afronden naar een belofte over uw vloot. De waarde van het ALLOW-geval is specifieker en belangrijker dan een percentage. Een gate is alleen geloofwaardig als deze onzichtbaar is bij normaal verkeer en onwrikbaar blijft bij die ene gebeurtenis die uw infrastructuur plat kan leggen.
Waarom ik het oordeel uit het model heb gehaald
Ik begon deze bouw met de aanname dat het moeilijke deel in het redeneren zat, en dat een scherper model of een slimmere criticus het element zou zijn dat de slechte update zou opvangen. Ik had het mis op een manier die me enige tijd kostte om toe te geven. Er zit een LLM-crew in Kestrel: een normalisator, een sandbox-interpreter en twee tegenover elkaar staande critici, van wie de een betoogt dat de update veilig kan worden verzonden en de ander dat deze zal crashen. Het contradictoire paar verdient zijn plaats, omdat het het oordeel vanuit beide richtingen aan een stresstest (red-team) onderwerpt voordat er iets wordt beslist. Maar niet één van die agenten velt het definitieve oordeel.
Het oordeel wordt geveld door twee eenvoudige Python-bestanden, verifier.py en gate.py, die volledig buiten het agentframework opereren. De crew draait op Pydantic AI met als standaardmodel claude-opus-4-8, en het geheel functioneert ook offline zonder API-sleutel via een deterministische adviserende fallback. In elk van die modi is de gate identiek en retourneert deze dezelfde beslissing, omdat de beslissing rekenkunde is, geen inferentie. Agenten adviseren, code beslist. Een adviserende agent die neigt naar „toestaan” kan een kritieke deterministische bevinding niet tenietdoen, en dat is geen kwestie van smaak.
Een laag die gebouwd is om de leverancier te controleren, kan niet vertrouwen op het woord van de leverancier over veiligheid. Hij kan ook niet vertrouwen op het woord van zijn eigen model.
Die zin is de reden waarom de architectuur er zo uitziet. Vertrouwen in een product waarvan de enige taak is om te beheren wat een leverancier uitlevert, mag nooit lopen via een component die overgehaald kan worden om ja te zeggen.
Wat ik aan een auditor zou overhandigen
Ik hield de Cyber Resilience Act van de EU op een tweede monitor geopend terwijl ik het bewijsrecord opbouwde, omdat dat record het artefact is dat ik daadwerkelijk zou moeten verdedigen. Elke beslissing exporteert een onveranderlijk HTML-bestand en een ondertekend JSON-bestand met een SHA-256-contenthash, het oordeel, de deterministische bewijzen, de sandbox-resultaten per profiel, de oordelen van de adviserende agenten met hun model-ID, de beleidsregels die zijn afgegaan en een stapsgewijze evaluatietrace waarin elke stap zijn eigen latentie registreert.

De trace is het onderdeel dat ik had onderschat totdat ik op een afzonderlijke stap klikte. Een gebeurtenis luidt: „Ondertekend leveranciersmanifest normaliseren, voltooid in 184 ms”, en deze wordt bewaard bij de beslissingsuitvoer voor auditbeoordeling. Elke stap is herleidbaar. Een toezichthouder hoeft mijn dashboard niet blind te vertrouwen. Zij kunnen de rekenkunde opnieuw uitvoeren en krijgen hetzelfde antwoord.

Ik ben nauwgezet over wat de ondertekening wel en niet is. Het is een lokale SHA-256, geen enterprise PKI. De updatefeed van de leverancier en de achterliggende ITSM-tickets zijn teststubs, geen live connectors. Het record is ontworpen om aan te sluiten bij rapportagevereisten: de incidentrapportage op korte termijn van de CRA, de openbaarmaking binnen vier werkdagen van een materieel cyberbeveiligingsincident conform de SEC, en de vragen over leveranciersaansprakelijkheid uit Delta v. CrowdStrike in Fulton County in 2025. Ontworpen om aan te sluiten bij. Het certificeert niemand, het is geen juridisch advies, en iedereen die u een auditlogboek verkoopt dat u compliant zou maken, probeert u simpelweg iets te verkopen.
Er is nog een beslissing waar ik trots op ben, en het is een weigering. Fixture XX-0000 is een versleutelde propriëtaire inhoudsblob die de gate niet kan parsen, dus er wordt niet geraden. Deze retourneert ABSTAIN en routeert naar een mens, want een gate die groen licht geeft aan wat hij niet kan lezen, is slechter dan helemaal geen gate. Legacy hosts die de sandbox niet kan modelleren, worden gemarkeerd en uitgesloten, nooit als veilig verondersteld. De woordenschat telt vier termen: ALLOW, HOLD, BLOCK, ABSTAIN, en de laatste is degene die ik het felst zou verdedigen.
Wat 12 van de 12 mag betekenen
Hier moet ik even gas terugnemen, want dit is precies waar een oprichter begint naar boven af te ronden. En ik heb het bedrijf Veriprajna genoemd, ware wijsheid, dus naar boven afronden is uitgesloten. Over een vaste, gelabelde set van twaalf updates geeft de gate bij alle twaalf de juiste beslissing. Zes daarvan zijn goedaardig en er wordt er geen enkele geblokkeerd. Eén is de oprechte ABSTAIN. Het scorebord vermeldt 12/12 geverifieerde beslissingen, 0/6 foutieve blokkades en $13,3M aan geschatte voorkomen downtime over de gehele set, waarvan $5M afkomstig is van de enkele CrowdStrike-klasse blokkade.

Nu het deel dat ik weiger in te korten. Dat zijn resultaten op twaalf gelabelde items, geen belofte over de volgende update die in uw vloot landt. Zes goedaardige items is zes. Dit betekent niet „blokkeert 100% van de slechte updates”, dat zal het nooit betekenen, en als u mij ooit die zin ziet schrijven, moet u stoppen met mij te lezen. Het getal waar ik wel achter sta, is van een andere aard: dezelfde invoer, dezelfde beslissing, bij elke run, omdat het oordeel geen modeltemperatuur draagt. Voer de fixtureset morgen opnieuw uit en deze retourneert byte-identieke resultaten, wat het mogelijk maakt een deterministische laag te auditeren op een manier die bij een probabilistische laag onmogelijk is.
De vraag die mij bijblijft
Wat mij bijblijft van deze bouw is hoe alledaags de fout was. Eenentwintig velden waar er twintig werden verwacht. Een getal dat elke onafhankelijke verificateur met simpele rekenkunde had kunnen opvangen voordat een enkele machine herstartte, als er een onafhankelijke verificateur tussen de leverancier en de vloot had gestaan. Die was er niet. Tot op de dag van vandaag is die er meestal nog steeds niet.
Elke onderneming draait acht tot twaalf agents met kernelprivileges van leveranciers die zij niet controleert, en elk daarvan kan een bestand rechtstreeks in ring 0 injecteren. SBOM-tools bewaken opensource-afhankelijkheden. Identiteitssystemen bewaken toegang. Niemand leest de propriëtaire update van de leverancier bij binnenkomst om te bewijzen dat deze veilig is. Kestrel is geen EDR en raakt de kernel nooit aan. Het bevindt zich boven die agents en reguleert wat zij mogen verzenden. Dat is de laag die ik heb geprobeerd te bouwen, en het volledige overzicht staat op veriprajna.com/demos/software-update-integrity.
En als u het liever in actie ziet dan dat u mijn beschrijving leest: hier draait het hele systeem van begin tot eind.
Dus dit is wat ik nu vraag over elke vloot die ik zie: wanneer de volgende update van een leverancier binnenkomt, wat staat er dan tussen dat bestand en de productie, en kan het zijn werk aantonen? Als het antwoord een change advisory board (CAB) is dat blind vertrouwt op de leverancier, dan draait de rekenkunde die miljoenen machines platlegde nog steeds ongecontroleerd door. Het zal zichzelf niet aankondigen. Het zal er precies zo uitzien als elke update die eraan voorafging, tot het exacte moment van de herstart.

