Was kann man tun, um sicherzustellen, dass die Daten aus der Datei nicht gefährlich sind und deswegen ohne Bedenken an system() zur Ausführung übergeben werden können? In den Erläuterungen zur Warnung SV.TAINTED.INJECTION gibt Klocwork den Hinweis: „Prüfe den Inhalt!“
Im Folgenden wird eine Möglichkeit vorgestellt, wie der Inhalt von input_buf[] geprüft werden kann, was dann die Warnung SV.TAINTED.INJECTION von Klocwork verhindert. Dazu werden zwei Zeilen zwischen den Aufrufen von fgets() und system() eingefügt. Dabei handelt es sich um die Zeilen 60 und 61 auf der rechten Seite von Listing 1.
In Zeile 60 auf der rechten Seite von Listing 1 wird der Inhalt von input_buf[] in einen neu erzeugten Puffer command[] mit passender Größe und passendem Typ umkopiert. Das Umkopieren alleine garantiert natürlich nicht, dass der Inhalt ungefährlich ist und das Umkopieren wird von Klocwork deshalb nicht als valide Prüfung akzeptiert; die Warnung für den Aufruf von system() würde bestehen bleiben. Die tatsäch¬liche Prüfung erfolgt durch den Aufruf der Funktion sanitize(). Aber wie könnte eine valide Prüfung in sanitize() aussehen?
Es gibt mehrere Möglichkeiten: Die Funktion sanitize() könnte prinzipiell alle Befehle zulassen, aber die Ausführung bestimmter Befehle, beispielsweise „del“, verhindern (Blacklisting). Die Funktion sanitize() könnte umgekehrt alle Befehle blockieren und nur die Ausführung bestimmter Befehle zulassen (Whitelisting). Es gibt noch weitere Möglichkeiten, je nachdem, was gewünscht wird. Wenn beispielsweise gewünscht wird, dass nur ein einzelner Befehl ausgeführt wird, könnte man nach dem Zeichen ‚&‘ in der Eingabe suchen und es selbst und alles, was danach kommt, entfernen. Das Zeichen ‚&‘ trennt unter Windows zwei aufeinanderfolgende Befehle.
Die Funktion sanitize() in Listing 2 ist ein Beispiel für Whitelisting. Sie erlaubt im vorliegenden Fall nur die Ausführung der zwei vorgegebenen Befehlen „dir“ und „path“. Alle anderen Befehle schaffen es nicht durch sanitize() und werden deshalb nicht durch system() ausgeführt. Bei der Arbeit mit Klocwork ist zu beachten, dass ein separater Puffer buf[] als Zwischenspeicher für die zugelassenen Befehle verwendet werden muss. Dieser Puffer buf[] wird dann auf command[] kopiert. Klocwork akzeptiert Blacklisting nicht als Reinigungsmethode.
Im vorhergehenden Beispiel konnte Klocwork die Schwachstelle finden, weil die Semantik der Funktionen fgets() und system() bekannt ist, da sie aus der Standard-C-Bibliothek stammen. Aber kleinere eingebettete Systeme nutzen möglicherweise proprietäre Funktionen zur Kommunikation mit der Außenwelt, und die Semantik dieser proprietären Funktionen ist nicht bekannt. Als Beispiel ein Paar von zwei Funktionen:
Read_command_from_Bluetooth()
Exec_command_from_Bluetooth()
Die Funktion Read_command_from_Bluetooth() könnte ein gefährliches Kommando einlesen und an Exec_command_from_Bluetooth() übergeben. Diese Situation ähnelt sehr stark der Situation auf der linken Seite von Listing 1. Allerdings gibt es keine Warnung durch Klocwork. Das liegt daran, dass die Semantik der beiden Funktionsaufrufe unbekannt ist, da die Namen der Funktionen proprietär und willkürlich gewählt sind. Damit Klocwork warnen kann, muss es über die Bedeutung der Funktionen informiert werden.
Das statische Analyse-Werkzeug Klocwork verfügt über eine Wissensdatenbank, die Informationen über wohlbekannte Fakten enthält, beispielsweise die Semantik von fgets() und von system(). Diese Wissensdatenbank kann erweitert werden, und damit kann die Security-Schwachstelle auch bei dem Bluetooth-Befehlspaar automatisch erkannt werden. Zwei Einträge erweitern die Wissensdatenbank von Klocwork:
Read_command_from_Bluetooth - TSsrc $1
Exec_command_from_Bluetooth - TSSinkInj $1
Bei diesen beiden Einträgen steht am Beginn der Zeile der Funktionsname, den der Eintrag betrifft. Das bedeutet, man kann für beliebige Funktionsnamen Information hinzufügen. In der ersten Zeile steht „TSsrc“ für „Tainted String Source“, was bedeutet, dass es sich bei Read_command_from_Bluetooth() um eine Quelle von möglicherweise schädlichen Daten handelt. In der zweiten Zeile steht „TSSinkInj“ für „Tainted String Sink Injection“, was angibt, dass Exec_command_from_Bluetooth() eine Datensenke für eine (Befehls-)Injektion ist. In beiden Zeilen gibt $1 an, dass es sich bei den beschriebenen Daten jeweils um den ersten Parameter der Funktionen handelt.
Ist die Wissensdatenbank von Klocwork um die zwei Zeilen erweitert, meldet das Tool bei Aufruf von Read_command_from_Bluetooth analog zu Bild 1 die Warnung SV.TAINTED.INJECTION. Das ist analog zur gleichen Warnung für Zeile #60 auf der linken Seite von Listing 1. Analog zu Bild 1 zeigt Klocwork auch den Weg der möglicherweise schädlichen Daten an.
Analog zur rechten Seite von Listing 1 wird durch Einfügen von strncpy() und Sanitize_command_from_Bluetooth() die Warnung SV.TAINTED.INJECTION verhindert (siehe Listing 3). Der Inhalt der Funktion Sanitize_command_from_Bluetooth() kann analog der Funktion sanitize() aus Listing 2 sein.
Statische Code-Analysewerkzeuge können die Ausführung von möglicherweise gefährlichen Befehlen erkennen. Das ist ohne weiteres möglich, falls die Anwendung Standardmechanismen zum Einlesen und Ausführen der Befehle verwendet. Wenn proprietäre Mechanismen verwendet werden, können fortgeschrittene Werkzeuge erweitert werden, um auch Security-Schwachstellen durch proprietäre Mechanismen zu erkennen. In jedem Fall erspart die automatisierte Erkennung großen Aufwand gegenüber der manuellen Prüfung.
Literatur
[1] http://www.hitex.de/klocwork: More about the static software analysis tool Klocwork
[2] Seacord, Robert C., Secure Coding in C and C++, second edition 2013, Addison-Wesley.
Der Autor