Verketten und kompromittieren: C++-Coroutinen anfällig für Code-Reuse-Angriffe trotz CFI
Mit einem neuartigen Code-Reuse-Angriff haben die beiden CISPA-Forscher Marcos Sanchez Bajo und Professor Dr. Christian Rossow gezeigt, dass alle bestehenden Implementierungen von C++-Coroutinen ausgenutzt werden können, um moderne CFI-Schutzmaßnahmen in Linux und Windows zu umgehen. Der Angriff namens „Coroutine Frame-Oriented Programming“ (CFOP) führt zu einer Beschädigung des Heap-Speichers, wodurch die Angreifenden Daten manipulieren und die vollständige Kontrolle über Anwendungen übernehmen können. Coroutinen sind eine relativ rezente Ergänzung in C++, aber bereits in mehr als 130 beliebten GitHub-Repositorys vorhanden. „Sie werden verwendet, um Funktionen anzuhalten und wiederaufzunehmen“, erklärt Bajo, „was für die asynchrone Programmierung, wie beispielsweise in Servern, Datenbanken und Webbrowsern, sehr nützlich ist.“
C++-Coroutinen verketten, um den Heap-Speicher zu beschädigen
Mithilfe von Coroutinen können beispielsweise Generatoren erstellt werden, die eine Folge von Elementen erzeugen, wie etwa die Fibonacci-Folge. In der Fibonacci-Folge ist jede neue Zahl die Summe der beiden vorangegangenen Zahlen. Nach jeder neuen Zahl in der Folge wird die Coroutine angehalten, bis sie dazu aufgerufen wird, die nächste Zahl zu generieren. Bei CFOP werden ganze C++-Coroutinen und andere vorhandene Funktionen dazu genutzt, einen Code-Reuse-Angriff zu erstellen, wie Bajo erklärt: „Bei Code-Reuse-Angriffen verwenden Angreifende in der Regel Code-Schnipsel, die ohnehin schon zur Anwendung gehören, sodass kein neuer Code eingeschleust wird. Aus diesen Code-Schnipseln bilden sie dann Ketten, um den Ausführungsfluss des Programms zu manipulieren. Die Umgehung von CFI-Schutzmaßnahmen ist allerdings etwas schwieriger. Anstatt nur Code-Schnipsel zu nehmen und Ketten zu bilden, muss man vollständige Coroutine-Funktionen nehmen und sie auf intelligente Weise miteinander verbinden.“ Sobald die CFI-Schutzmaßnahmen auf diese Weise durch das Hijacking einer Coroutine-Funktion umgangen sind, kann jede andere vorhandene Funktion einem Code-Reuse-Angriff unterzogen werden.
Control Flow Integrity (CFI) versagt beim Schutz von C++-Coroutinen
CFI-Mechanismen wurden zum Schutz vor Code-Reuse-Angriffen eingeführt und sollen sicherstellen, dass der korrekte Programmablauf eingehalten wird. Programmiersprachen entwickeln sich jedoch dynamisch weiter, während CFI-Mechanismen nur die zum Zeitpunkt ihrer Erstellung vorhandenen Programmierparadigmen schützen. Bajo betont: „Das Hauptproblem bei CFI ist, dass es eine statische Abwehr ist, d. h. es deckt nur die Möglichkeiten einer Programmiersprache in ihrer bestehenden Form ab. Wenn zu einem späteren Zeitpunkt neue Funktionen in die Programmiersprache eingeführt werden, kann CFI diese nicht erkennen. Es kann nicht mit ihnen umgehen, da es auf einer älteren Version der Programmiersprache basiert.“ In ihrer Studie fanden Bajo und Rossow heraus, dass nur 7 von 15 ursprünglich betrachteten CFI-Mechanismen mit Coroutinen kompatibel waren. Von diesen 7 boten nur 2 (IBT und Control Flow Guard) einen teilweisen Schutz vor der Ausnutzung von Coroutinen, während die übrigen 5 gar keinen Schutz boten. „Letztendlich“, fasst Bajo zusammen, „waren wir in der Lage, alle zu umgehen. Mit CFOP kann man weiterhin all das tun, was vor CFI möglich war.“
Das Patchen von CFOP ist ein strukturelles Problem
Die Tatsache, dass C++-Coroutinen sich zunehmender Beliebtheit erfreuen, verstärkt das Potenzial von CFOP. Bajo erklärt: „Coroutinen wurden 2020 in C++ eingeführt und werden seitdem immer häufiger von Entwickler:innen verwendet. Leider haben wir festgestellt, dass Coroutinen bestimmte Strukturen im Speicher aufweisen, auf die Angreifende abzielen können. Soweit wir wissen, wurde dies bisher aber noch nicht in der Praxis ausgenutzt.“ Im Wesentlichen ist CFOP möglich, weil die drei wichtigsten Compiler C++-Coroutinen auf eine Art und Weise implementieren, die sie strukturell anfällig macht. Bajo fährt fort: „Diese Angriffstechnik zu entschärfen, ist nicht so einfach wie das Patchen eines Codes – es handelt sich um ein strukturelles Problem, und man muss die interne Funktionsweise der Anwendung überdenken.“ Bajo und Rossow haben erfolgreiche Implementierungsalternativen für C++-Coroutinen entwickelt und diese Abhilfemaßnahmen im November 2024 an Clang/LLVM, GCC und MVSC gemeldet. Die CISPA-Forschung zu CFOP wird am 07. August 2025 auf der „Black Hat USA“-Konferenz in Las Vegas vorgestellt.
Weitere Informationen:
Weitere Informationen zu "Coroutine Frame-Oriented Programming (CFOP)" sind verfügbar unter https://syssec.cispa.io/coroutine-cfop/
Wissenschaftlicher Kontakt:
Marcos Sanchez Bajo und Prof. Dr. Christian Rossow
CISPA Helmholtz-Zentrum für Informationssicherheit
Stuhlsatzenhaus 5
66123 Saarbrücken
marcos.sanchez-bajo@cispa.de / rossow@cispa.de
Akademische Publikation:
Sanchez Bajo, Marcos; Rossow, Christian (2025) “Await() a Second: Evading Control Flow Integrity by Hijacking C++ Coroutines” In: 34th Usenix Security Symposium (USENIX-Security), 13-15 Aug 2025, Seattle, WA, USA. https://doi.org/10.60882/cispa.28718642.v1