Security Reinigung für den Code

Gefahrenstellen wenn kein Standard-Betriebssystem vorhanden ist.

Durch Einlesen externer Daten können schädliche Befehle ausgeführt werden. Mit guten Analysewerkzeugen kommt man potenziellen Schwachstellen aber auf die Spur.

Bisher stand bei eingebetteten Systemen oftmals der Safety-Aspekt (Schutz des Menschen vor dem System) im Vordergrund. Der Security-Aspekt (Schutz des Systems vor dem Menschen) wurde stiefmütterlich behandelt. Security rückt jedoch verstärkt ins Bewusstsein, insbesondere durch Medienberichte über Hackerangriffe auf Kraftfahrzeuge, Sicherheitslücken in medizinischen Geräten und verschiedenen Bestandteilen des Internet of Things (IoT) wie Webcams, TV-Geräten, Stromzählern und ähnlichem. Für „große“ eingebettete Systeme mit Betriebssystemen wie Windows oder Linux gibt es Werkzeuge, die vollautomatisiert gewisse Security-Schwachstellen aufdecken. Diese Verfahren eignen sich aber auch für „kleinere“ eingebettete Systeme, die nur ein rudimentäres, möglicherweise proprietäres Betriebssystem haben.

Security-Schwachstellen gibt es in unterschiedlichen Ausprägungen. Es fängt damit an, das IoT-Systeme wie Webcams alle mit dem gleichen Passwort ausgeliefert werden (was den Zugriff recht einfach macht) und endet beim Ausnutzen von Zero-Day-Schwachstellen (was tiefgehende Kenntnisse und Arbeitsaufwand erfordert).

Schädlicher Code

Eine bekannte Security-Schwachstelle ist die Einspeisung von schädlichem (tainted) Code bzw. Daten. Hierzu gehört beispielsweise die Einspeisung von manipulierten Befehlen (Command Injection) in ein System, die dann „offiziell“ ausgeführt werden, aber keine offiziell erwünschten Auswirkungen haben. Ein Beispiel hierfür ist SQL-Injektion, wobei ein schädlicher SQL-Befehl für eine Datenbank ausgeführt wird, was im Extremfall zur Löschung der kompletten Datenbank führen kann. Ein anderes Beispiel ist XPath-Injektion, durch welche die Daten einer Web-Applikation offengelegt werden können. Moderne statische Quellcode-Analysewerkzeuge können solche Einspeisungen prinzipiell ohne weiteres Erkennen, denn es ist bekannt, wie und wo ein SQL-Befehl ausgeführt wird. An den fraglichen Stellen im Quellcode kann man überprüfen, ob der zur Ausführung kommende SQL-Befehl möglicherweise schädlich ist oder nicht.

Unglücklicherweise ist dies von Vorne herein nur für „größere“ eingebet¬tete Systeme möglich, die SQL-Datenbanken, Web-Dienste, Dateisysteme etc. besitzen. Für „kleinere“ eingebettete Systeme fehlt dieses Wissen über potenzielle Schwachstellen. Sie haben oftmals nur ein minimales beziehungsweise proprietäres Betriebssystem oder möglicherweise überhaupt kein Betriebssystem. Und sie haben üblicherweise auch keine SQL-Datenbanken. Deshalb kann man für solche Systeme die standardmäßigen Fähigkeiten statischer Quellcode-Analysewerkzeuge nicht nutzen. Aber diese Systeme sind mit dem Internet verbunden, aus dem sie Daten empfangen und verarbeiten. Glücklicherweise können moderne statische Quellcode-Analysewerkzeuge erweitert werden, um auch die proprietäre Verwendung von möglicherweise infizierten Daten bzw. Befehlen zu erkennen.

Im Folgenden wird gezeigt, wie eine potenziell gefährliche Befehlseinspeisung auf einem Standard-Betriebssystem wie Windows aussehen und wie diese Schwachstelle beseitigt werden kann. Danach wird eine vergleichbare Befehlseinspeisung auf einem kleineren eingebetteten System betrachtet, wo naturgemäß proprietäre Eingabefunktionen verwendet werden. Das Aufspüren der Schwachstellen erfolgt im Beispiel mit dem statischen Quellcode-Analysewerkzeug Klocwork.

Standardschwachstellen finden

Als Beispiel für eine von vornherein bekannte Befehlseinspeisung dienen die beiden Funktionen fgets() und system() aus der C-Standardbibliothek. Auf der linken Seite von Listing 1 werden in Zeile 59 mittels der Standard-C-Bibliotheksfunktion fgets() Daten von der Außenwelt eingelesen und im Puffer input_buf[] abgelegt. Der Puffer ist ein Array mit BUF_LEN Zeichen. Es ist garantiert, dass nicht mehr Zeichen in den Puffer geschrieben werden, als er fassen kann, denn es werden nicht mehr als sizeof(input_buf) Zeichen eingelesen. Die Quelle der Daten ist eine Datei, was durch den dritten Parameter fp bestimmt wird, der einen Zeiger auf eine Datei darstellt. Der dritte Parameter könnte auch stdin sein, wodurch die Zeichen über die Standardeingabe eingelesen würden. In diesem Fall gilt das Folgende sinngemäß.

Der Eingabepuffer wird in der folgenden Zeile 60 als Parameter an die Standard-C-Bibliotheksfunktion system() übergeben. Die Funktion system() übergibt den Puffer an das Betriebssystem und das Betriebssystem führt daraufhin den Pufferinhalt als Befehl aus. Das bedeutet, dass jeder Befehl aus der Datei (deren

Herkunft unbekannt ist), vom Betriebssystem ausgeführt wird, egal was der Befehl bewirkt. Das vorliegende Beispiel wurde unter Windows erarbeitet, gilt im Prinzip aber auch für Linux.

Unter Windows wäre es harmlos, das Kommando „dir“ auszuführen, aber es kann katastrophal sein, wenn das Kommando „del“ ist. Deshalb erzeugt Klocwork bei der Analyse die Warnung SV.TAINTED.INJECTION für den Aufruf von system() in Zeile 60 (Bild 1) und zeigt den Weg der möglicherweise schädlichen Daten von der Quelle zur Senke. Im vorliegenden Fall ist dies offensichtlich. Es kann aber durchaus Fälle geben, in denen Quelle und Senke in unterschiedlichen Quelldateien liegen und der Weg über mehrere Stationen geht. Dann ist es komfortabel, ein Werkzeug zu haben, das diese Information automatisch ermittelt.