Der Verzweigungsprädiktor ist eine wichtige Komponente moderner CPU-Architekturen, die die Leistung verbessern soll, indem sie die Richtung von Verzweigungsanweisungen (z. B. if-else-Anweisungen) spekuliert, bevor sie ausgeführt werden. Diese Spekulation ermöglicht es der CPU, Anweisungen entlang des vorhergesagten Pfads vorab abzurufen und auszuführen, wodurch die wahrgenommene Latenz verringert und der Gesamtdurchsatz verbessert wird. Diese Leistungsoptimierung führt jedoch zu potenziellen Schwachstellen, die bei CPU-Timing-Angriffen ausgenutzt werden können, insbesondere im Zusammenhang mit dem Durchsickern vertraulicher Informationen.
Die Verzweigungsvorhersage funktioniert, indem ein Verlauf der Verzweigungsergebnisse gespeichert wird und dieser Verlauf zur Vorhersage zukünftiger Verzweigungen verwendet wird. Wenn ein Verzweigungsbefehl angetroffen wird, verwendet der Prädiktor diese historischen Daten, um zu erraten, ob der Verzweigungsbefehl ausgeführt wird oder nicht. Wenn die Vorhersage richtig ist, setzt die CPU die Ausführung ohne Unterbrechung fort. Wenn sie falsch ist, muss die CPU einen Rollback durchführen und den richtigen Pfad ausführen, was zu Leistungseinbußen führt. Diese Einbußen sind zwar gering, können aber von Angreifern gemessen und ausgenutzt werden.
Angreifer können den Verzweigungsprädiktor manipulieren, um einen messbaren Zeitunterschied zwischen korrekt und falsch vorhergesagten Verzweigungen zu erzeugen. Dieser Unterschied kann genutzt werden, um auf den Ausführungspfad eines Programms zu schließen, was wiederum vertrauliche Informationen preisgeben kann. Eines der bekanntesten Beispiele für einen solchen Angriff ist die Spectre-Sicherheitslücke, die spekulative Ausführung und Verzweigungsvorhersage nutzt, um auf nicht autorisierte Speicherorte zuzugreifen.
Bei einem typischen Spectre-Angriff trainiert der Angreifer zunächst den Verzweigungsprädiktor, einem bestimmten Muster zu folgen. In dieser Trainingsphase wird eine Folge von Verzweigungsanweisungen ausgeführt, die den Prädiktor dazu konditionieren, eine bestimmte Vorhersage zu treffen. Sobald der Prädiktor trainiert ist, führt der Angreifer ein Opfercodesegment aus, das einen von geheimen Daten abhängigen Verzweigungszweig enthält. Wenn der Prädiktor auf Grundlage des Trainings des Angreifers eine falsche Vorhersage trifft, führt die CPU spekulativ Anweisungen aus, die auf der Grundlage der geheimen Daten auf den Speicher zugreifen. Obwohl diese spekulativen Anweisungen letztendlich verworfen werden, hinterlassen sie Spuren im Cache der CPU.
Der Angreifer kann dann die Zugriffszeiten auf verschiedene Speicherorte messen, um festzustellen, auf welche Daten spekulativ zugegriffen wurde. Diese Technik, bekannt als Cache-Timing-Angriff, ermöglicht es dem Angreifer, anhand der beobachteten Zeitunterschiede auf die geheimen Daten zu schließen. Die wichtigsten Schritte bei einem solchen Angriff sind:
1. Trainieren des Branch-Predictors: Der Angreifer führt eine kontrollierte Folge von Anweisungen aus, die den Zustand des Verzweigungsprädiktors beeinflussen. Wenn beispielsweise ein Verzweigungsbefehl wiederholt mit einem konsistenten Ergebnis (z. B. immer ausgeführt) ausgeführt wird, wird der Prädiktor so konditioniert, dass er bei zukünftigen Ausführungen dieses Ergebnis erwartet.
2. Auslösen einer spekulativen Ausführung: Der Angreifer führt den Opfercode mit einer Verzweigungsanweisung aus, die von geheimen Daten abhängig ist. Aufgrund des vorherigen Trainings des Angreifers führt der Verzweigungsprädiktor spekulativ den falschen Pfad aus, was einen Speicherzugriff basierend auf den geheimen Daten beinhaltet.
3. Messen der Cache-Zugriffszeiten: Nach der spekulativen Ausführung misst der Angreifer die Zeit, die zum Zugriff auf bestimmte Speicherorte benötigt wird. Schnellere Zugriffszeiten deuten darauf hin, dass die Daten im Cache vorhanden sind, was bedeutet, dass spekulativ darauf zugegriffen wurde. Durch die Analyse dieser Zeitangaben kann der Angreifer auf die geheimen Daten schließen.
Um dies anhand eines konkreten Beispiels zu veranschaulichen, betrachten wir ein Szenario, in dem die geheimen Daten den Index eines Array-Zugriffs innerhalb eines Zweigs bestimmen. Der Angreifer trainiert zunächst den Verzweigungsprädiktor, um eine bestimmte Verzweigungsrichtung anzunehmen. Wenn der Opfercode ausgeführt wird, führt der Verzweigungsprädiktor den Array-Zugriff spekulativ basierend auf der trainierten Richtung aus. Wenn die Spekulation den Zugriff auf ein bestimmtes Array-Element beinhaltet, wird die entsprechende Cache-Zeile geladen. Der Angreifer kann dann eine Reihe zeitgesteuerter Speicherzugriffe durchführen, um zu bestimmen, welche Cache-Zeilen geladen sind, und so auf den geheimen Index schließen.
Um solche Angriffe abzuwehren, sind mehrere Strategien erforderlich. Hardwarebasierte Lösungen umfassen die Verbesserung der Isolierung zwischen spekulativen und nicht spekulativen Ausführungspfaden und die Gewährleistung, dass die spekulative Ausführung keine Auswirkungen auf gemeinsam genutzte Ressourcen wie den Cache hat. Softwarebasierte Lösungen umfassen Techniken wie das Einfügen von „Fence“-Anweisungen, um die spekulative Ausführung über bestimmte Punkte im Code hinaus zu verhindern, oder die Verwendung von Programmierpraktiken in konstanter Zeit, um sicherzustellen, dass die Ausführungszeit nicht von geheimen Daten abhängt.
Die Komplexität und Raffinesse von Timing-Angriffen auf Basis von Branch Predictors unterstreichen die Notwendigkeit fortlaufender Forschung und Entwicklung im Bereich der Hardware- und Softwaresicherheit. Da sich CPU-Architekturen ständig weiterentwickeln, müssen auch die Strategien zum Schutz vor diesen und anderen Formen von Side-Channel-Angriffen weiterentwickelt werden.
Weitere aktuelle Fragen und Antworten zu CPU-Timing-Angriffe:
- Welche Herausforderungen und Kompromisse sind mit der Implementierung von Hardware- und Software-Maßnahmen zum Schutz vor Timing-Angriffen bei gleichzeitiger Aufrechterhaltung der Systemleistung verbunden?
- Wie kann die Programmierung in konstanter Zeit dazu beitragen, das Risiko von Timing-Angriffen bei kryptografischen Algorithmen zu verringern?
- Was ist spekulative Ausführung und wie trägt sie zur Anfälligkeit moderner Prozessoren für Timing-Angriffe wie Spectre bei?
- Wie nutzen Timing-Angriffe Abweichungen in der Ausführungszeit aus, um an vertrauliche Informationen eines Systems zu gelangen?
- Was ist ein Timing-Angriff?