CrowdStrike-Fiasko: Der Null Pointer ist Schuld

Nach dem IT-Ausfall durch ein CrowdStrike-Update gibt es erste Analysen des Speicherfehlers, die auf einen grundlegenden Fehler von CrowdStrike hindeuten.

In Pocket speichern vorlesen Druckansicht 856 Kommentare lesen

Der Fehler-Dump zeigt eine Assembler-Verschiebeoperation (MOV R9D, DWORD PTR [R8]). Zeiger werden verwendet, um indirekt auf Speicheradressen zuzugreifen. Ein häufiges Problem ist das Dereferenzieren eines Nullzeigers, was zu einem Programmabsturz führt, da auf eine ungültige oder nicht initialisierte Speicheradresse zugegriffen wird.

(Bild: Zach Vorhies / Bearbeitung heise online)

Update
Lesezeit: 6 Min.
Von
Inhaltsverzeichnis

Am Freitagmorgen brachte ein fehlerhaftes Update des IT-Security-Anbieters CrowdStrike weltweit Windows-Computer in Unternehmen und Behörden zum Absturz. Flughäfen, Banken, Geschäfte, Krankenhäuser und viele weitere Einrichtungen waren betroffen und mussten teilweise ihren Betrieb einstellen.

Nun tauchen erste forensische Analysen des Memory Dumps des Blue Screen of Death (BSOD) auf, etwa von Zack Vorhies bei X. Seine Untersuchung legt nahe, dass das Problem auf einen sogenannten Null-Pointer-Fehler in CrowdStrikes CSAgent.sys hindeutet. Der Code versucht anscheinend, auf eine ungültige Speicheradresse (0x9c bzw. 156) zuzugreifen.

CrowdStrike-Fiasko – weltweite IT-Ausfälle

Als Beispiel führte Vorhies C++ an. Dort wird üblicherweise die Adresse 0x0 als Null-Pointer verwendet, um anzuzeigen, dass kein gültiges Objekt vorhanden ist. Programmierer sollten stets auf Null-Pointer prüfen, bevor sie auf Objekte oder deren Eigenschaften zugreifen. Ein typisches Beispiel für eine solche Prüfung sieht wie folgt aus:

string* p = get_name();​
if (p == NULL) { print("Could not get name"); }​

In diesem Fall wird überprüft, ob der Pointer p null ist, bevor versucht wird, darauf zuzugreifen. Im Fall von CrowdStrike wurde eine solche Prüfung offenbar versäumt. Um das Problem besser zu verstehen, betrachten wir ein Beispiel mit einer einfachen Struktur:

struct Obj {​
int a;​
int b;​
};​

Wenn man einen Pointer auf ein solches Objekt erstellt, könnte es wie folgt aussehen:

Obj* obj = new Obj();

Nimmt man an, das Objekt beginne an der Speicheradresse 0x9030. Bei dieser einfachen Datenstruktur ist in C++ die Adresse eines Objekts und der ersten Mitgliedsvariablen identisch. Daher wären die Adressen der Mitglieder (jeweils ermittelt mit der C++-Funktion std::addressof()):

obj: 0x9030​
obj->a: 0x9030 + 0x0​
obj->b: 0x9030 + 0x4​

Wenn der Pointer jedoch null ist (Obj* obj = NULL), verweisen die Speicheradressen für alle Mitgliedsvariablen auf uninitialisierte Speicherbereiche. Versucht man nun, auf ein Mitglied dieses leeren Objekts zuzugreifen (z.B. std::cout << obj->a;), führt dies zu einem Programmabsturz. Denn: Die Speicheradressen der Mitgliedsvariablen zeigen in den uninitalisierten Speicher. Im Beispielaufruf sieht das etwa so aus:

~ % ./null    
obj: 0x9030
obj->a: 0x9030 + 0x0
obj->b: 0x9030 + 0x4
zsh: segmentation fault  ./null

Im Fall von CrowdStrike versuchte der Code, auf die Adresse 0x9c zuzugreifen, was auf einen ähnlichen Fehler hindeutet. Da es sich um einen Systemtreiber mit privilegiertem Zugriff handelte, führte dieser Fehler zu einem kompletten Systemabsturz. Da der Treiber bei jedem Start des Rechners geladen wurde, hingen die betroffenen Computer in einer Reboot-Schleife fest und mussten manuell über den abgesicherten Modus repariert werden.

Dieser Vorfall unterstreicht die immense Bedeutung sorgfältiger Programmierung und gründlichen Testens, insbesondere bei systemkritischer Software wie Sicherheitstreibern. CrowdStrike wird wahrscheinlich ihre Qualitätssicherungsprozesse überprüfen und verbessern müssen, um ähnliche Vorfälle in Zukunft zu verhindern. Unklar ist auch, warum ein Update für eine derart kritische Software parallel an mutmaßlich Millionen Rechner weltweit ausgespielt wurde und nicht über einen gestaffelten Rollout, wie er in solchen Situationen üblich wäre.

Zu dem Fiasko beigetragen hat möglicherweise auch, dass CrowdStrike eine Programmiersprache wie C++ verwendet haben könnte, bei der die Programmierer explizit prüfen müssen, ob ein Zeiger gültig ist, bevor sie ihn verwenden. Wird diese Prüfung vergessen, kann es zu sogenannten Null Pointer Dereferences kommen – der Zugriff auf einen ungültigen Speicherbereich. Moderne Programmiersprachen wie Rust verhindern solche Fehler durch strikte Überprüfungen zur Compile-Zeit.

Neuer Stoff für die Lektion "Einführung in Pointer": Kommentar auf TikTok zur Analyse des Null-Pointer-Problems

(Bild: Thread auf TikTok)

Wenn ein solch fundamentaler Programmierfehler den Weg in ein produktives Update finden konnte, würde das kein gutes Licht auf die Qualitätssicherung bei CrowdStrike werfen. Denn in diesem Fall würden Prüfmechanismen fehlen, die solche Fehler frühzeitig erkennen. CrowdStrike selbst bleibt bei der Ursache bedenkt und bezeichnet sie bisher lediglich als "Logic Error".

CrowdStrike-CEO George Kurtz war zuvor CTO bei McAfee. Auch dort kam es 2010 zu einem fehlerhaften Update, das weltweit Windows-XP-Rechner lahmlegte. Zwar waren die Auswirkungen damals nicht so dramatisch, dennoch hätte man erwarten können, dass Kurtz aus der damaligen Erfahrung gelernt hat.

Für die betroffenen Kunden stellt sich nun die Frage nach Schadensersatz. Fluggesellschaften, Krankenhäuser und Einzelhändler dürften allein durch die Ausfälle am Freitag Schäden in Millionenhöhe erlitten haben. Hinzu kommen die Kosten für die Behebung der Schäden. Auch die mittelbar Betroffenen, etwa Urlaubende, die ihrem Flug verpasst haben und ihr Hotel umbuchen mussten, dürften sich jetzt fragen, wer für ihren Schaden aufkommt. Eine mögliche Anlaufstelle, um Schadenersatzansprüche von Fluggästen zu prüfen, ist die Flugärger-App der Verbraucherzentrale NRW.

CrowdStrike wird sich unangenehme Fragen gefallen lassen müssen, wie es zu dem Fehler kommen konnte und welche Konsequenzen das Unternehmen daraus zieht. Eine Abkehr von C++ und der Einsatz moderner Programmiersprachen sowie verschärfte Qualitätssicherungsmaßnahmen wären das Mindeste.

Wer jetzt meint, das Problem hätte nur Windows betreffen können, irrt. Schon im April ist etwas Ähnliches Linux widerfahren: Am 19. April veröffentlichte CrowdStrike ein Update für die Software falcon-sensor, die mit der Linuxdistribution Debian Stable inkompatibel war. Dies führte zum Absturz aller Server eines städtischen Tech-Labors auf mehreren Websites und Cloud-Hosts wie Hacker News berichtete. Der Fehler wurde durch Überprüfung der Festplattenprotokolle entdeckt. Durch manuelle Deinstallation konnten die Maschinen wieder booten, aber eine erneute Installation führte abermals zu Abstürzen. CrowdStrike benötigte mehrere Tage, um den Fehler zu bestätigen und stellte fest, dass die spezifische Debian-Version nicht in ihrer Testmatrix enthalten war.

Um solche Probleme zu umgehen, empfiehlt der Autor des Hacker-News-Beitrags unter anderem, die CrowdStrike-Software unter Linux im User-Mode (eBPF) statt Kernel-Mode zu betreiben, um Abstürze zu minimieren.

Wie sich inzwischen herausstellte, war nicht nur Debian betroffen. Auch unter Red Hat löste falcon-sensor Anfang Juni bei Kunden eine Kernel Panic aus, weil der Prozess auf einen nicht vorhandenen Speicherbereich zugreifen wollte.

falcon-sensor bereitet nicht nur unter Windows Probleme, im Juni berichteten Anwender von Speicherzugriffsfehlern unter Red Hat.

(Bild: Red Hat Customer Portal)

Update

Verschiedene Nutzer des Forums wiesen uns darauf hin, dass die Code-Beispiele auf inkorrekten Annahmen beruhen – wir hatten sie aus der auf X veröffentlichten Analyse übernommen, der mittlerweile auch auf X berichtigt wurde. Wir haben die Code-Beispiele angepasst, die unrealistische Basisadresse 0x9030 jedoch aus Gründen der Lesbarkeit beibehalten. Außerdem haben wir einige Passagen sprachlich überarbeitet. Vielen Dank an die aufmerksamen Leser!

Inzwischen haben sich Hinweise verdichtet, nach denen der oben im Beitrag verlinkte Artikel "CrowdStrike: Microsoft nennt Zahlen, Ursachenforschung dauert an" eine bessere Erklärung für die Ursache des Speicherfehlers liefert.

(vza)