Ein Angebot von

Fuzzing von Embedded Software – Grundlagen und Erfahrungen

| Autor / Redakteur: Axel Wintsche * / Sebastian Gerstl

Screen von American Fuzzy Lop, ein von Google-Entwickler Michal Zalewski (Lcamtuf) geschriebenes Fuzzing-Tool zur Analyse von Codepfaden. Fuzzing-Tests empfehlen sich nicht nur für Desktop-Software, auch für die Embedded-Entwicklung lässt es sich die Methode zur Robustheitsprüfung einsetzen.
Screen von American Fuzzy Lop, ein von Google-Entwickler Michal Zalewski (Lcamtuf) geschriebenes Fuzzing-Tool zur Analyse von Codepfaden. Fuzzing-Tests empfehlen sich nicht nur für Desktop-Software, auch für die Embedded-Entwicklung lässt es sich die Methode zur Robustheitsprüfung einsetzen. (Bild: Michal Zalewski (Lcamtuf))

Softwaresicherheit lässt sich häufig nur unzureichend als Anforderung formulieren und testen. Teststrategien wie Fuzzing bietet aber eine Möglichkeit, automatisiert die Robustheit von Software zu prüfen. dieser Arikel beschreibt, was Fuzzing ausmacht, welche Hürden es beim Testen von Embedded Software gibt und mögliche Lösungsansätze sich daraus ergeben.

Das V-Model ist ein klassisches Beispiel dafür, wie sich Anforderungen aus den verschiedenen Phasen der Softwareentwicklung durch geeignete Testverfahren prüfen lassen. Und auch bei heutzutage oft verwendeten inkrementellen, agilen Methoden gibt es meist eine enge Verzahnung zwischen Entwicklung und Test mit dem Ziel, die Qualität zu steigern. Um die Sicherheit von Software zu erhöhen können entsprechende Methoden bei Entwicklung und Tests angewandt werden – mit dem Unterschied, dass sich das Kriterium 'Sicherheit' nur unzureichend als Anforderung formulieren und testen lässt. Selbst wenn durch eine umfassende Bedrohungsmodellierung entsprechende Anforderungen abgeleitet werden und von der Software erfüllt werden, können sich durch neue, bisher unbekannte Bedrohungen nicht erfüllte Anforderungen ergeben. Diese Fälle lassen sich durch entsprechendes Incidence Response Management behandeln. Solche neuen Bedrohungen – also bislang nicht durch Anforderungen spezifizierte Situationen – lassen sich aber auch proaktiv durch entsprechende Testverfahren identifizieren. Hier bietet sich beispielsweise der Fuzzing-Test an.

Sichere Software ist als ein Prozess zu verstehen, welcher durch Einsatz verschiedener Methoden und Verfahren – vom Entwurf über die Implementierung bis zur Auslieferung und Wartung – gelebt werden muss. Als Beispiele sind hier der SDL (Secure Development Lifecycle) von Microsoft und die Ressourcen von der Open Web Application Security Project (kurz OWASP) zu nennen, die beide den Einsatz von Fuzzing empfehlen.

Welche Konsequenzen Schwachstellen in Software eingebetteter Systeme haben können, beweisen verschiedene Untersuchungen von Sicherheitsforschern. Die Bandbreite bekannter Fälle reicht hier mittlerweile von erfolgreichen Hacks zum Fernsteuern von Autos [1,2], dem Manipulieren von Herzschrittmachern und anderer Medizintechnik [3] oder aufgedeckten Sicherheitslücken sowie Angriffen auf Infrastruktur [4,5] und darüber hinaus. Die Notwendigkeit, einen Security-Testprozess auch für diese Systeme zu etablieren, ist daher groß.

Sichere Software Entwicklung mit dem Fuzzing-Test

Fuzzing ist eine Methode, mit der Software durch ungültige oder zufällige Eingaben getestet wird, um ein Fehlverhalten zu provozieren. In ihrer simpelsten Form werden durch Fuzzing

  • a) eine zufällige Abfolge von Bits erzeugt, oder
  • b) von einer bestehenden Bitfolge eine beliebige Anzahl Bits zufällig verändert und diese als neue Eingabe für die zu testende Software verwendet.

Da die meisten auf diese Weise erzeugten Eingaben keiner formalen Anforderung entsprechen, lässt sich vor allem testen, wie stabil die Software mit fehlerhaften Eingaben umgeht. Fuzzing-Tests zielen also auf die Robustheit von Software gegenüber unerwarteten Eingaben ab. Ein Kriterium für Robustheit ist z.B., dass die Software zu jeder Zeit in einem definierten Zustand ist und nicht etwa abstürzt. Prinzipiell lässt sich so jede datenverarbeitende Software oder Softwarekomponente mittels Fuzzing testen.

Fuzzing besteht im Wesentlichen aus drei aufeinander folgenden Schritten:

  • 1) Daten generieren,
  • 2) Daten bereitstellen und
  • 3) Monitoring der Software.

Diese Schritte sind unabhängig voneinander, werden aber oft in einer Schleife ausgeführt. Häufig werden dieselben Fehler mehrfach gefunden so dass am Ende eines Fuzzing Tests sinnvoller Weise gefundene Fehler zusammen gefasst werden (Bug Triage).

Fuzzing Test, Schritt 1: Daten generieren

Dies ist der eigentliche Fuzzing-Prozess, in welchem die Testdaten erzeugt werden. Man unterscheidet hier zwischen zwei Methoden: „Generieren“ und „Mutieren“.

Beim Generieren werden anhand einer Spezifikation oder eines Modells Testdaten erzeugt und bei Inhalt und Struktur zufällige Änderungen vorgenommen. Werden hingegen bestehende Daten mit zufälligen Änderungen versehen wird dies Mutieren genannt. Beide Varianten kommen in unterschiedlichen Szenarien zum Einsatz.

Schritt 2: Daten bereitstellen

Die gefuzzten Daten werden anschließend an die zu testende Software übergeben, je nach Art der Eingabeschnittstelle z.B. als Parameter beim Programmaufruf, als Datei oder in Form einer Netzwerknachricht.

Schritt 3: Monitoring der Software

Fehler bei der Ausführung der Software lassen sich durch das Monitoring ermitteln. Dies kann von der Analyse der Rückgabewerte bis hin zur Nutzung von Debuggern reichen, welche den internen Zustand detektieren können. Die Art des Monitoring entscheidet, welche Fehler überhaupt detektiert werden können.

Möglichkeiten des Fuzzing-Tests

Fuzzing kann als Black-Box- und als White-Box-Test eingesetzt werden. Programme für Black-Box-Fuzzing sind oft einfacher zu konfiguriere, leichter zu automatisieren und meist universell einsetzbar. Sie finden tendenziell aber eher simple Fehler (Dumb Fuzzing).

Sind Spezifikation oder gar Quellcode verfügbar lohnt es sich, mehr Aufwand in das Erzeugen der Daten und das Monitoring zu investieren. Denn hierdurch lässt sich die Effizienz erheblich steigern (Smart Fuzzing). Mittlerweile gibt es eine große Palette an verfügbaren Fuzzing Werkzeugen (z.B. unter in diesem GitHub-Repository). Neben vielen spezialisierten Tools (wie z.B. Fuzzer für bestimmte Dateiformate oder Netzwerkprotokolle) existieren auch Frameworks, die durch entsprechende Konfiguration vielseitig einsetzbar sind.

Durch Fuzzing gefundene Fehler sind in ihrer Natur oft schwerwiegend, wie z.B. Buffer Overflows, Integer Overflows, Denial of Service (DoS) oder Code Injection Schwachstellen (XSS, SQLi) [6]. Darüber hinaus ist Fuzzing ein gut automatisierbares, kosteneffektives Testverfahren mit dem Potenzial, die bei Code Review, Unit Test und Co nicht entdeckten Fehler zu detektieren (extreme oder nicht sinnvolle Eingaben).

Beachtet werden muss, dass die Ausführen von Millionen von Testfällen mitunter sehr zeitaufwendig sein kann und daher eine gute Planung und Integration in den Entwicklungsprozess erfordert. Wie bei allen Testmethoden kann auch der Fuzzing-Test kein komplettes Bild vorhandener Schwachstellen wiedergeben – eine Software ohne Fehler beim Fuzzing ist damit nicht automatisch sicher.

Fuzzing von eingebetteten Systemen

Für Software auf Steuergeräten gelten oft andere Rahmenbedingungen, woraus sich neue Herausforderungen an das Fuzzing ergeben. Hauptsächlich entsteht dies dadurch, dass das Fuzzing-Tool und die getestete Software auf getrennten Systemen laufen. Als Testdatenformat sind meist spezielle Nachrichtenprotokolle wie CAN, LIN oder ARINC einzuhalten, die Übertragung an das Steuergerät erfordert oft zusätzliche Hardware und Software.

Mit sogenannte Breakout-Boxen lässt sich zudem eine Systemumgebung simulieren (Sensoren und Nachrichten Bus). Beim Monitoring besteht die Schwierigkeit Fehler bei der Ausführung auf dem Steuergerät zu detektieren. JTAG Debugger sind hier oft die einzige Möglichkeit den internen Zustand zu beobachten. In einem Blackbox Testszenario können Time-out Kriterien eine praktikable Lösung darstellen wobei ein vorhandener Watchdog deaktiviert werden sollte.

Beispiel aus der Praxis: CAN-Nachrichten-Fuzzing

Getestet werden soll eine Softwarekomponente welche auf der Anwendungsschicht CAN Nachrichten auf einem Steuergerät verarbeitet. Eine CAN-Nachricht besteht vereinfacht aus einer ID, der Angabe zur Nachrichtenlänge und dem Nachrichteninhalt. Nicht protokollkonforme Nachrichten würden bereits bei der Übertragung über die CAN Hardware blockiert und bei Nachrichten mit ungültiger ID nicht an die entsprechende Softwarekomponente übergeben. Spezielle Login- und Logoff-Nachrichten müssen zu Beginn und Ende einer Verbindung gesendet werden. Auf jede gesendete Nachricht folgt eine Antwortnachricht vom Steuergerät.

Für das Fuzzing eines solchen Systems müssen die Voraussetzungen zum Senden und Empfangen auf den CAN-Bus geschaffen werden. Die notwendige Hardware und Treiber müssen installiert und eine Anbindung des Fuzzing Programms an die Treiber API hergestellt sein. Die Erzeugung der Testdaten sollte Login und Logoff berücksichtigen sowie vorwiegend den Datenbereich und die Längenangabe fuzzen. Dies steigert die Effizienz des Fuzzing und so auch die Chance, Fehler aufzuspüren.

Trotz der benutzerdefinierten Gegebenheiten ist es oft nicht notwendig, ein spezialisiertes Fuzzing-Programm zu entwickeln; es existieren mittlerweile zahlreiche Fuzzing-Frameworks, die sich entsprechend konfigurieren lassen. Für das Monitoring ergeben sich mehrere Möglichkeiten. Z.B. kann bereits das Ausbleiben einer Antwortnachricht vom Steuergerät zur Fehlerdetektion genutzt werden (Timeout-Kriterium). Durch Verwendung eines JTAG-Debuggers lässt sich z.B. der Stacktrace bei auftretenden Fehlern ermitteln.

Erfahrungen aus Fuzzing-Tests für die Embedded-Entwicklung nutzen

Fuzzing ist eine ernstzunehmende Testmethode und wird bereits erfolgreich bei Desktop- und Web-Applikationen eingesetzt. Im Kontext eingebetteter Systeme werden Fuzzing-Tests bislang allerdings kaum eingesetzt. Dafür gibt es keinen ersichtlichen Grund, denn es existieren gewisse Ähnlichkeiten zu herkömmlichen Computernetzwerken. Einzelne Steuergräte sind über verschiedene Netzwerktypen wie CAN, LIN oder ARINC miteinander verbunden. Somit ist Fuzzing als Teil des Security-Testprozesses gut denkbar.

Da immer mehr eingebettete Systeme über Komponenten mit „online“ Funktionalität verfügen, lassen sich zudem die Erfahrungen aus Fuzzing-Tests klassischer Protokolle (TCP/IP) und moderner Anwendungen (z.B. Web- und Smartphone Apps) nutzen. Security Testmethoden wie Fuzzing könnten sich zukünftig auch in Safety Standards wie der ISO 26262 wiederfinden um den aktuellen Stand der Technik an die Sicherheitsanforderungen zu erfüllen [7].

Formal korrekten C-Code durch Benutzung von SPARK programmieren

Formal korrekten C-Code durch Benutzung von SPARK programmieren

31.08.18 - Ein einfacher Weg zu sicherer Software: Durch den Einsatz der Ada-Variante SPARK ist es möglich, schnell und unkompliziert in C geschriebene Programme automatisch auf Korrektheit zu überprüfen. lesen

Robuste Architekturen: Health Check für die Entwicklung von Echtzeitsystemen

Robuste Architekturen: Health Check für die Entwicklung von Echtzeitsystemen

05.08.18 - Viele Software-Projekte für eingebettete Systeme haben Probleme bzgl. Budget- oder Terminüberschreitungen. Basierend auf vielen Entwicklungsprojekten wurde ein kompakter Fragenkatalog (Real-Time Health Check) entwickelt, der Schwachpunkte im Entwurfsprozess aufdeckt. lesen

Quellenverweise

[1] C. Miller and C. Valasek, "Adventures in Automotive Networks and Control Units," DEFCON 21 Hacking Conference, 2013.

[2] “Car Hacking Research: Remote Attack Tesla Motors”, Keen Security Lab

[3] "Los, Hacker, brecht mir das Herz!: Sicherheit von vernetzter Medizintechnik auf dem Prüfstand", heise online, 20.03.2016

[4] “Schadsoftware im Atomkraftwerk Gundremmingen”, heise.de News, 26.04.2016

[5] „92 Percent of Internet-Available ICS Hosts Have Vulnerabilities”, Softpedia news, 11.07.2016

[6] Prasanna Padmarajulu, "Discovering vulnerabilities with Fuzzing", PenTest Magazin 04/2017,

[7] Bayer S., Enderle T., Oka DK., Wolf M. (2016) Automotive Security Testing - The Digital Crash Test. In: Langheim J. (eds) Energy Consumption and Autonomous Driving. Lecture Notes in Mobility. Springer

* Axel Wintsche ist Diplom Informatiker. Er ist seit 2009 als Softwareentwickler und -Tester tätig sowie Referent zum Thema Sichere Software-Entwicklung in der Philotech Academy.

Kommentar zu diesem Artikel abgeben

Schreiben Sie uns hier Ihre Meinung ...
(nicht registrierter User)

Zur Wahrung unserer Interessen speichern wir zusätzlich zu den o.g. Informationen die IP-Adresse. Dies dient ausschließlich dem Zweck, dass Sie als Urheber des Kommentars identifiziert werden können. Rechtliche Grundlage ist die Wahrung berechtigter Interessen gem. Art 6 Abs 1 lit. f) DSGVO.
Kommentar abschicken
copyright

Dieser Beitrag ist urheberrechtlich geschützt. Sie wollen ihn für Ihre Zwecke verwenden? Infos finden Sie unter www.mycontentfactory.de (ID: 45483130 / Safety & Security)