Hardware-Security Embedded-Bauteile absichern

Exploits erhöhen die Schwierigkeit

Das Ausnutzen (Exploit) des Anwendungscodes ist bei kostengünstigen Bauteilen von Bedeutung, die keine Hardware-Unterstützung für die Isolierung von Software zur Eindämmung der Exploits bieten. Code-Exploits gibt es in zwei Hauptvarianten.
Die erste Angriffsart versucht, den Speicher mithilfe einer vorhandenen Schnittstelle zu manipulieren, deren Eingaben auf unerwartete Weise nicht korrekt überprüft werden. Der bekannteste Angriff der Art ist ein Pufferüberlauf (Bild 2). Beim Pufferüberlauf sendet ein Angreifer mehr Daten als erwartet an eine Schnittstelle, wodurch der Anwendungscode oder die Daten mit ihren eigenen Werten überschrieben werden. Die Art des Angriffs kann Anwendungscode auf ungewollte Weise aktivieren. Zum Beispiel um einen Befehl zu erhalten, der eine Variable abrufen soll, um stattdessen Konfigurationsinformationen abzurufen. Im schlimmsten Fall kann er verwendet werden, um Code in den Speicher zu injizieren und die CPU dann dazu bringen, zu dem Code zu springen. Dann kann der Angreifer einen beliebigen Code ausführen und die vollständige Kontrolle über die Hardware erlangen.

Bei der zweiten Angriffsart wird versucht, den Code durch indirektes Einspeisen eines Fehlers zu Fehlfunktionen zu bringen, etwa durch eine Fehlfunktion des Netzteils oder eine Taktstörung. Das Ziel des Angriffs ist, den Programmfluss zu ändern. Ein Fehler oder eine Störung wird genau zu dem Zeitpunkt eingefügt, zu dem die CPU eine Sicherheitsentscheidung trifft oder auf eine Sicherheitsentscheidung reagiert. Das geschieht durch einen Verzweigungsbefehl oder das Beschädigen der Befehle, zu denen die Verzweigung führt. Es gibt Testcode, der geheime Informationen zu Testzwecken an einen Universal Asynchronous Receiver Transmitter (UART) sendet, der nicht im Normalbetrieb ausgeführt werden soll. Ein Angreifer kann den Code jedoch möglicherweise ausführen, indem er einen Fehler einfügt, der dazu führt, dass eine »if-Anweisung« die falsche Entscheidung trifft.

Der beste Weg, um die Art von Angriff zu verhindern, besteht darin, die Angriffsfläche zu verringern, das heißt die Anzahl der angreifbaren Schnittstellen zu reduzieren und die Komplexität der gesamten Anwendung zu verringern. Es ist verlockend, ein einzelnes Image zu erstellen, das den Anwendungs- und Testcode enthält, der für den Board-Test erforderlich ist. Das Image kann programmiert, der Board-Testlauf ausgeführt und das Produkt ausgeliefert werden. Das führt jedoch dazu, dass Testcode und eine Board-Testschnittstelle auf Bauteilen im Feld vorhanden sind. Stattdessen lassen sich separate Test- und Anwendungs-Images erstellen. Besser ist es, das Board-Test-Image zu programmieren und die Tests auszuführen. Anschließend kommt das Anwendungs-Image zum Einsatz. Das vereinfacht die Endanwendung und beseitigt einen möglichen Angriffspunkt. Ebenso sollten Fertigungs-Images keinen unnötigen Code enthalten. Wenn beispielsweise eine Funktion teilweise entwickelt und dann aus Kosten- oder Time-to-Market-Gründen gekürzt wird, sollte der Code aus dem Fertigungs-Image entfernt werden, anstatt an Ort und Stelle zu bleiben. Ebenso gilt zwei separate Images zu erstellen und die optionalen Funktionen über ein Firmware-Update zu aktivieren.

Regelmäßige Firmware-Updates sind eine gute Möglichkeit, Code-Exploits zu minimieren. Entwickelt ein Angreifer einen permanenten Exploit, der sich selbst im Flash installiert, wird ein Update den Exploit entweder mit einem neuen Image überschreiben oder der Angreifer wird gezwungen, das neue Image nicht zu installieren. Das wird wahrscheinlich erkannt, wenn die erwarteten Verhaltensänderungen nicht beobachtet werden. Firmware-Updates können auch absichtliche oder unbeabsichtigte Änderungen im Code enthalten, die die Funktion von Exploits der Vorgängerversion stören. Wird zum Beispiel die Datenstruktur im Random Access Memory (RAM) oder der Code im Flash geändert, funktioniert ein überschriebener Exploit nicht mehr. Denn die Adresse enthält nicht mehr die Variable, die der Exploit zu überschreiben versucht hat. Wird ein Kommunikationsprotokoll bei einem Update geändert und ein gefährdetes Bauteil unterdrückt das Update, ist sofort ersichtlich, dass der Baustein nicht ordnungsgemäß aktualisiert wurde.

Die Hardware-Funktionen lassen sich ebenfalls reduzieren, um die einem Angreifer zur Verfügung stehenden Tools einzuschränken. So ermöglichen viele ICs das Deaktivieren des RAM, um Strom zu sparen. Es ist möglich, dass eine Anwendung auf einem Baustein mit 4 KB RAM ausgeführt wird, aber nur 2 KB verwendet. Die anderen 2 KB werden deaktiviert und hindern einen Angreifer daran, sie zu nutzen. Ebenso können ungenutzte Flash-Seiten gesperrt werden, um zu verhindern, einen Exploit in ihnen zu installieren.

Andere Optionen für bestimmte Hardware umfassen eine Stapelgrenzwert- oder Überlauferkennung (Stack Canary), das Setzen des No-Execute-Bits im Datenspeicher – falls vorhanden – und die zufällige Zuweisung des Adressraums (ASLR, Address Space Layout Randomisation). ASLR ist eine Funktion des Anwendungsprozessors – aber auch bei Mikrocontrollern finden sich eine Stapelbegrenzung und No-Execute-Funktionen. Auch verschiedene Codierungstechniken lassen sich verwenden, um den Code robuster gegen Störimpulse (Glitching-Angriffe) und Seitenkanalabgriffe zu machen. Bild 3 beschreibt ein Code-Beispiel, das für einen Stör- oder Glitching-Angriff anfällig ist.
Zusätzlich zu all diesen Techniken wird empfohlen, Penetrationstests an Firmware-Images durchzuführen, um Schwachstellen zu identifizieren und zu beheben. Das kann durch Dritte oder intern mit Hilfe von Open-Source-Tools erfolgen. Eine Kombination aus internen und externen Tests ist zu empfehlen. Die häufigste Form der Analyse von Schnittstellen-Exploits ist das sogenannte Fuzzing.

Flash-Speicher gegen Manipulation sichern

Während wirklich sicheres Booten eine unveränderliche Root of Trust erfordert, zum Beispiel beim ROM, können viele kostengünstige Microcontroller-Units (MCUs) eine einigermaßen sichere Root of Trust im Flash erstellen. Das hängt von der Fähigkeit der Hardware ab, Flash-Seiten zu sperren, um deren Löschung oder  Programmierung zu verhindern. Steht die Funktion zur Verfügung, kann ein nicht aktualisierbarer Bootloader auf einer Flash-Seite platziert und die Seite gesperrt werden. Zusätzlich zur Bereitstellung von Bootloading-Diensten bietet der unveränderliche Bootloader auch sicheres Booten, wobei er die Signatur der Anwendung überprüft, bevor er sie ausführen lässt. In diesem Fall enthält der Boot­loader einen öffentlichen Schlüssel, mit dem zu installierende Images und der Flash-Inhalt beim Booten überprüft werden. Versucht nun ein Angreifer, den Inhalt der Anwendung zu bearbeiten, erkennt der Bootloader, dass die Signatur nicht mehr übereinstimmt und verhindert die Ausführung des manipulierten Codes.

Dabei ist zu beachten, dass bei vielen MCUs der Flash-, Lösch- und Debug-Zugriff von anderen Bauteilen über einen JTAG- oder Serial-Wire-Befehl freigeschaltet werden kann. Falls vorhanden, muss die Funktion deaktiviert werden, um sicherzustellen, dass der verriegelte Flash nicht einfach gelöscht und neu programmiert werden kann.

Vertrauliche Daten schützen

Der Schutz vertraulicher Daten auf einem kostengünstigen IC ist schwierig. Während einige der besprochenen Techniken es Angreifern erschweren, vertrauliche Daten abzugreifen, werden sie letztendlich – wenn die Informationen wertvoll genug sind – doch abgegriffen. Die primäre Verteidigungsstrategie eines kostengünstigen Systems besteht darin, dass einfach keine wertvollen Daten vorhanden sind. Alle persistenten (Flash) und transienten (RAM) Daten sollten ausgewertet werden, um festzustellen, ob sie wirklich erforderlich sind. Wenn nicht, sollten die Daten aus dem System entfernt werden. Falls erforderlich, sollten sie nur so kurz wie möglich aufbewahrt werden. Wenn das System über andere Komponenten verfügt, die vertrauliche Daten speichern können, wie eine Telefon- oder Cloud-Anwendung, wird empfohlen, vertrauliche Daten auf die Plattformen zu verschieben, da sie sich besser zum physischen Schutz gespeicherter Daten eignen. Darüber hinaus sollten Funk­tionen, die die Speicherung vertraulicher Daten erfordern, sorgfältig geprüft werden. Es kann vorkommen, dass sich eine gewünschte Funktion auf einem kostengünstigen Baustein nicht ausreichend sichern lässt. Dann sollte die Funktion nicht implementiert – oder das System auf einen sichereren IC aktualisiert werden.

Ebenfalls zu beachten ist, dass externe Secure-Element-ICs zwar vertrauliche Daten gut speichern können. Wird die Anwendung aber kompromittiert, kann der Angreifer alles unternehmen, was auch die Anwendung kann. Während das Speichern eines Schlüssels in einem Secure-Element verhindert, dass eine kompromittierte Anwendung den Schlüsselwert erhält, verhindert das nicht, dass die Anwendung den Schlüssel zum Signieren oder Entschlüsseln von Nachrichten verwendet. In vielen Fällen kann die Fähigkeit genauso schädlich sein wie der Zugriff auf den Schlüssel selbst. Alles, was die Anwendung mithilfe eines externen Secure-Elements ausführt, kann auch eine kompromittierte Version der Anwendung ausführen.

Schließlich ist auch zu bedenken, dass vertrauliche Daten nicht nur auf dem Bauteil selbst zugänglich sind. Der Schutz eines symmetrischen Schlüssels auf dem Bauteil bringt nichts, wenn derselbe Schlüssel per E-Mail im Entwicklungslabor oder Büro versendet oder in einem ungesicherten Source-Control-Repository gespeichert wird. Alle Nutzer der Verschlüsselung sollten darüber nachdenken, wie vertrauliche Daten – kryptografische Schlüssel, Firmware-Images oder Quellcode – in ihrem Entwicklungslabor gespeichert und abgerufen werden. Das ist entscheidend, um sicherzustellen, dass vertrauliche Informationen gut geschützt sind. Es wird dringend empfohlen, dass Entwickler gestandene Speichermechanismen wie Hardware Security Module (HSM) oder Trusted Platform Module (TPM) verwenden, Richtlinien und Verfahren für den Zugriff auf sichere Informationen einrichten, Zugriffskontrollen implementieren und korrekte Protokollierungs- und Prüfverfahren verwenden.

 

Der Autor

Josh Norem ist Systems Engineer für das Mikrocontroller- und Wireless-/Funk-IC-Angebot von Silicon Labs. Er schloss die University of Illinois (Urbana-Champaign) mit dem Bachelor of Science in Elektrotechnik ab. Nach dem Studium entwickelte er bei Texas Instruments Produkt- und Speichertests für Digital Signal Processors (DSPs). Danach war er für Advanced Micro Devices tätig und führte dort System-Level-Speed-Debugging für AMDs x86-Mikroprozessoren durch. 2006 kam er als Produkt- und Testingenieur zu Silicon Labs und hatte seitdem verschiedene technische Funktionen in der Test-, Anwendungs- und zuletzt Systemtechnik inne.