Security Mehr Widerstandsfähigkeit gegen Cyberattacken mittels statischer Code-Analyse
Anbieter zum Thema
Cyberattacken auf eingebettete Systeme nehmen zu. Wie statische Code-Analyse dazu beitragen kann, ihre sicherheitskritischen Funktionen zu schützen, erklären Dr. Jacob Palczynski & Dr.-Ing. Martin Becker von Mathworks.

Eingebettete Systeme aller Art, auch solche mit sicherheitskritischen Funktionen, werden immer häufiger Ziele von Cyberattacken [1]. Obwohl viele Entwickler bereits statische Code-Analysen und dynamische Tests einsetzen, um die Qualität und die (funktionale) Sicherheit ihrer Software zu erhöhen [2], bleibt das Problem bestehen. Da aber die Zahl von eingebetteten Systemen, denen wir vertrauen können müssen, nicht zuletzt im Kommunikations-, Gesundheits- und Verkehrssektor, weiter zunehmen wird, ist Abhilfe dringend erforderlich. Es gilt auch, die Cyber-Sicherheit dieser Systeme Tool-gestützt zu verbessern.
Sicherheitslücken in Programmcode
Ziel von Cyberattacken ist es, Schwachstellen in Software zu finden, durch die unbeabsichtigtes Verhalten ausgelöst werden kann. Das kann etwa die Möglichkeit sein, auf dem betroffenen System vorhandene Befehle oder eigene Software remote auszuführen, eigentlich verschlüsselte Daten mitzulesen, oder Systeme abstürzen zu lassen. Eine Schwachstelle hat also einen Fehler (Defekt) als Ursache, der sich im System fortpflanzen können muss (Infektion) und dann zu unbeabsichtigtem Verhalten führt (Folge). Nur, wenn diese drei Faktoren zusammenkommen, ist eine Ausnutzung (Exploit) möglich.
Das MITRE hat 1999 das CWE-Projekt (Common Weakness Enumeration) gestartet, das bekannte Schwachstellen aufzählt. Es gehört zum Common Vulnerabilities and Exposures-(CVE) Programm, das eine Liste bekannter Schwachstellen und Anfälligkeiten publiziert. Diese systematisierte Liste warnt nicht nur vor verwundbarer Software, sondern hilft auch allgemein, verbreitete Sicherheitslücken besser zu verstehen. Die laut MITRE 10 gefährlichsten CWEs sind im Bild Tabelle aufgelistet [3]. NV steht für die relative Häufigkeit, CVSS für die Gefährlichkeit der Schwachstelle. Was ist Statische Code-Analyse?
Bei der statischen Code-Analyse (Static Application Security Testing, SAST) wird Software auf Fehler getestet, ohne sie auszuführen. Anstatt wie bei dynamischen Tests einzelne Testfälle an der laufenden Software abzuarbeiten, unterzieht man den Code einer analytischen Prüfung, die Entwurfsmängel und fehlerhafte Programmierstile sucht. Dazu werden u. a. Daten- und Kontrollflüsse verfolgt und sämtliche Ausführungspfade einer Software in Betracht gezogen.
Die Funktion von SAST-Tools geht damit über das schlichte Prüfen von Programmier-Richtlinien wie MISRA/CERT deutlich hinaus. Einige Tools sind sogar in der Lage, die Abwesenheit von Fehlern mit formalen Methoden zu beweisen [4], was einem erschöpfenden dynamischen Testen entspricht und damit in der Praxis normalerweise unerreichbar ist. Statische Analyse kann zudem bereits während der Entwicklung von Komponenten eingesetzt werden, ohne dass auf die Gesamtsoftware gewartet werden muss.
Der Möglichkeit, mit formalen Methoden sämtliche Schwachstellen zu finden, steht aber das Entscheidungsproblem entgegen: ein SAST-Tool kann entweder konsistent (sound) sein und keine False-Negatives ausgeben oder aber vollständig (complete) und keine False-Positives erzeugen [5].
Ausgereifte Werkzeuge geben nur wenige Warnungen aus und übersehen auch nur wenige Fehler. In der Praxis ist oft eine Kombination aus beiden Ansätzen sinnvoll. Einige Werkzeuge bieten sogar Modi an, bei denen man wählen kann, ob nur wahrscheinliche Fehler oder alle Fehler gemeldet werden sollen. Ein Beispiel dafür sind die Polyspace Static Code Analysis Tools [6]. Wir nehmen im Folgenden an, dass wir mit einem konsistenten Tool arbeiten, also eher zu viele Warnungen angezeigt, aber keine Fehler übersehen werden.
Was ein gutes SAST-Tool ausmacht
Das richtige SAST-Tool hilft dem Entwickler interaktiv bei der Interpretation von Ergebnissen. Beispielsweise verweisen vom SAST-Tool angezeigte Warnungen verweisen oft auf eine Folge anstatt auf eine Ursache. Damit besteht die Gefahr, die Warnung nicht richtig zu interpretieren und als False-Positive abzutun. Nur, wenn die eigentliche Ursache einer Warnung bekannt ist, lässt sich entscheiden, ob sie ein Problem darstellt, wie es zu diesem Problem kommt und wie bzw. wo im Code man es beseitigen kann [7].
Die für den Entwickler wahrscheinlich wertvollste Eigenschaft ist , wenn sein SAST-Tool ihm aussagekräftige Kontextinformationen zur ausgegebenen Warnung bereitstellt. Dazu gehören unter anderem:
- Aufrufkontext: Welcher Caller liefert die schädliche Eingabe (Kontextsensitivität)?
- Kontrollfluss: Welche Entscheidungen wurden getroffen, um hierher zu gelangen (Pfadsensitivität)?
- Variablenwerte: Was sind mögliche Werte von Array-indizierenden Variablen (CWE-787, CWE-125)? Werden Variablen vor deren Verwendung immer initialisiert (CWE-665)?
- Pointer-Analyse: Sind sie initialisiert? Wohin zeigen sie, und wie groß sind die zugrunde liegenden Speicherbereiche (CWE-119)?
- Taint-Analyse: Verwendet eine Funktion potenziell bösartige Benutzereingaben ohne vorherige Unbedenklichkeitsprüfung (CWE-20)?
- Zugriffe auf Globals: Wo werden sie verwendet und wie (lesen/schreiben)? Gibt es mögliche Race Conditions?
Ausgefeiltere SAST-Tools wie Polyspace berechnen Event-Traces, die die o.g. Information in Dialogfeldern als Schrittfolge aufführen. Beim interaktiven Durcharbeiten dieser Schrittfolge lassen sich Variablen und Pointer verfolgen sowie Entscheidungen im Kontrollfluss nachvollziehen. Diese Event-Traces stellen einen Ausschnitt aus der Infektionskette dar. Es lässt sich aus ihnen oft mit wenig Aufwand ein Angriffspfad ableiten (Abb. 1). Ein gutes SAST-Tool sollte diese Fähigkeiten bieten, da sie das Debugging und die Fehlerbehebung erheblich beschleunigen kann.
Contracts und Assertions
Eine bekannte Möglichkeit, unerwünschtes Verhalten zu identifizieren, sind Assertions und Contracts. Sie können aber auch die Ursachenforschung erleichtern und zudem Zugriff auf Informationen geben, die das SAST-Tool zwar kennt, aber nicht explizit anzeigt. Als Beispiel soll CVE-2018-16522 („FreeRTOS DoS/RCE“, CWE 665) dienen (Abb. 2). Die Sockets_Close-Funktion wird durch Userland-Code aufgerufen, wodurch pxContext beliebigen Inhalt haben kann. Die Dereferenzierung in Zeile 114 ist potenziell unsicher (orange), was aber an dieser Stelle nicht behoben werden kann. Annahmen zum Kontext sind nicht hilfreich, da dieser user-generiert ist. Stattdessen kann man eine Nachbedingung bei SOCKETS_SetSockOpt als Contract setzen:
Dieser nur für das SAST gesetzte Contract checkt bei Zugriff auf das i-te Array-Element, ob dieses korrekt initialisiert wurde. Der Indexoperator ist hier fehlerhaft (orange Hervorhebung durch Polyspace), die Initialisierung kann also unvollständig sein.
Über die statische Analyse hinaus
I. Defensive Programmierung
- Defensive Programmierpraktiken berücksichtigen explizit unvorhergesehene Umstände. In der Praxis wird dies durch umfangreiche Fehlerchecks von Zuständen, Call-Parametern und Rückgabewerten erreicht. Das hat zwei Vorteile:
- Anders als Contracts, die immer noch verletzt werden können, lässt defensive Programmierung Infektionsketten abbrechen und sorgt damit für echte Robustheit.
- Defensiver Programmcode wirkt als Filter für SAST-Tools. Diese prüfen sämtliche im Analysekontext erlaubten Programmzustände und stellen dabei fest, dass nicht erlaubte Werte gar nicht erst in kritische Bereiche propagiert werden.
II. Minimierung kontext-bezogener Annahmen
Nimmt man bei einer Analyse einen genau bekannten Aufrufkontext an, kann das die Zahl der Warnungen reduzieren. Gleichzeitig steigt aber die Gefahr, neue Angriffsflächen zu schaffen, wenn diese Annahmen verletzt werden. Lässt man Annahmen weg oder reduziert sie in bereits vorhandener Software nach und nach, erhöht sich zwar zunächst die Zahl der Warnungen. Das zwingt aber die Entwickler defensiver zu programmieren. Die weitestgehende Vermeidung von Annahmen sorgt also für robustere Software.
Zwei entscheidende Vorteile kommen hinzu. Erstens wird die Analyse beschleunigt, da man nun mit kleineren Codepartitionen arbeiten kann und weniger Kontext benötigt wird. Dies trägt zudem zum frühzeitigen Testen von Code bei.
Gleichzeitig bedeuten aber kleinere Partitionen auch geringere Komplexität für die Analyse. Es sind weniger Näherungen erforderlich, was präzisere und schnellere Ergebnisse bedeutet. Darüber hinaus wirkt hier übrigens auch der oben beschriebene Filtereffekt.
Polyspace [5] bietet dazu die Option „-unit-by-unit” und macht sich sämtliche hier beschriebenen Vorteile zunutze. Software wird auf diese Weise widerstandsfähiger gegen unvorhergesehene Schwachstellen, die nicht zuletzt auch durch Hardware entstehen können, wie in jüngster Zeit beobachtet werden konnte.
Fazit
Cyberattacken erfolgen über verschiedene Pfade und haben sehr unterschiedliche, teils fatale Folgen. Häufig ist eine Schwachstelle in der eingebetteten Software (mit-)verantwortlich. Wir haben gesehen, wie man selbst noch unbekannte Schwachstellen mittels statischer Code-Analyse systematisch auffinden und somit die Widerstandsfähigkeit gegen Cyberattacken deutlich verbessern kann.
A. Literatur
[1] The MITRE Corporation, "CVE Vulnerabilities by Date", 2020. [Online]. Abrufbar: https://www.cvedetails.com/browse-by-date.php.
[2] M. Beller, R. Bholanath, S. McIntosh and A. Zaidman, "Analyzing the state of static analysis: A large-scale evaluation in open-source software", in International Conference on Software Analysis, Evolution, and Reengineering, 2016.
[3] The MITRE Corporation, "2020 CWE Top 25 Most Dangerous Software Weaknesses", 2020. [Online]. Abrufbar: https://cwe.mitre.org/top25/archive/2020/2020_cwe_top25.html.
[4] The MathWorks Inc., "Polyspace Code Prover," 10 December 2020. [Online]. Abrufbar: https://www.polyspace.com.
[5] A. Turing, "On computable numbers, with an application to the Entscheidungsproblem", in Proceedings of the London mathematical society, 1937.
[6] "Polyspace Static Code Analysis Products", The MathWorks Inc., 2020. [Online]. Abrufbar: https://www.polyspace.com.
[7] J. Smith, B. Johnson, E. Murphy-Hill, B. Chu and H. Lipford, "Questions developers ask while diagnosing potential security vulnerabilities with static analysis", in Proceedings of Joint Meeting on Foundations of Software Engineering, 2015.
(mbf)
* Dr. Jacob Palczynski arbeitet als Senior Training Engineer bei MathWorks. Seit über 10 Jahren teilt er seine Expertise beim Einsatz statischer Codeanalyse und der Modellverifikation und -validierung mit Teams, die sicherheitskritische eingebettete Software entwickeln. Dabei arbeitet er auch mit Kunden zusammen, deren Entwicklungsprozesse Standards zur funktionalen Sicherheit wie ISO 26262 und DO178-C folgen. Vor seiner Tätigkeit bei MathWorks hat er als wissenschaftlicher Mitarbeiter am Lehrstuhl Informatik 11 der RWTH Aachen über die Anwendung formaler Methoden im Rahmen der Verifkation und Validierung eingebetteter Software geforscht und gelehrt.
* Martin Becker ist Senior Field Application Engineer für Verifikations- und Validierungsworkflows bei MathWorks. Er ist ein Verfechter von formalen Methoden und statischer Analyse und verfügt über mehr als 15 Jahre Erfahrung mit Embedded Systems. In seiner täglichen Arbeit unterstützt er Kunden aus einer Vielzahl von Branchen bei der effizienten Erstellung von Embedded-Software unter Einhaltung von Sicherheitsstandards. Vor seiner Tätigkeit bei MathWorks arbeitete er als Ingenieur für Luftfahrtkonzepte bei Airbus, als Forschungsingenieur bei Tata Consultancy Services und promovierte zum Dr.-Ing. auf dem Gebiet der Echtzeit-Computersysteme an der Technischen Universität München.
(ID:48734487)