3 Sicherheitslücken im Grafik-Debugger „RenderDoc“

Die Qualys Threat Research Unit (TRU) hat drei Sicherheitslücken in „RenderDoc“ entdeckt. Renderdoc ist ein ausgesprochen nützlicher, eigenständiger Grafik-Debugger, der zu einem unverzichtbaren Tool für Entwickler geworden ist. Die MIT-lizenzierte Open-Source-Software bietet den Benutzern eine umfassende Plattform zur detaillierten Prüfung und Einzelbild-Erfassung für verschiedene Anwendungen, einschließlich Vulkan, D3D11, OpenGL & OpenGL-ES und D3D12. RenderDoc unterstützt mehrere Betriebssysteme, darunter Windows, Linux, Android und sogar die Nintendo-Switch. Qualys rät den Sicherheitsteams dringend, schnellstmöglich die Patches für die Sicherheitslücken in Renderdoc zu installieren.

Renderdoc lädt mit LD_PRELOAD die Shared-Library librenderdoc.so in die Anwendung, die debuggt werden soll, und diese Bibliothek startet sofort einen Server-Thread. Dieser Server-Thread lauscht auf allen Netzwerkschnittstellen am TCP-Port 38920 und wartet auf Client-Verbindungen. So leistungsstark und nützlich diese Funktion auch ist, hat das Qualys Forscherteam doch genau in dieser Implementierung drei Sicherheitslücken entdeckt.

 

Analyse der Sicherheitslücken

  • CVE-2023-33865: Dies ist eine Symlink-Schwachstelle, die jeder lokale Angreifer ausnutzen kann, ohne über bestimmte Rechte verfügen zu müssen. Der Angreifer könnte auf diese Weise die Rechte des RenderDoc-Benutzers erlangen. Das Spannende an dieser Schwachstelle sind die Details, die ihre Ausnutzung zu einer geistigen Herausforderung machen.
  • CVE-2023-33864: Dieser Integer-Unterlauf führt zu einem Heap-Überlauf, der es einem entfernten Angreifer ermöglichen könnte, beliebigen Code auf dem Host-Rechner auszuführen. Zur Ausnutzung dieser Schwachstelle wird eine spezielle malloc-Ausnutzungstechnik eingesetzt, die sich als verlässlich und effizient erwiesen hat, trotz aller jüngsten Sicherheitsmechanismen wie glibc-Schutz, ASLR, PIE, NX und Stack Canaries.
  • CVE-2023-33863: Dieser Integer-Überlauf führt zu einem Heap-Überlauf und könnte von einem entfernten Angreifer ausgenutzt werden, um beliebigen Code auf dem Rechner auszuführen. Wir haben noch nicht versucht, diese Lücke auszunutzen, doch die potenzielle Bedrohung für die Sicherheit darf nicht unterschätzt

Wichtig zu wissen: Renderdoc hat alle drei Schwachstellen innerhalb von nur drei Stunden behoben, nachdem sie von unserem Forscherteam gemeldet wurden. RenderDoc hat die Version 1.27 veröffentlicht, die die notwendigen Korrekturen zur Schließung dieser Sicherheitslücken umfasst.

Technische Details zu den Sicherheitslücken in RenderDoc:  Die technischen Details zu diesen Sicherheitslücken finden sich unter: https://www.qualys.com/2023/06/06/renderdoc/renderdoc.txt

 

Unter der Lupe: Eine Untersuchung von CVE-2023-33865, einer Symlink-Schwachstelle in /tmp/RenderDoc

CVE-2023-33865 ist eine Symlink-Schwachstelle, die eng mit dem Verzeichnis /tmp/Renderdoc verbunden ist. Sobald die Shared-Library librenderdoc.so mit LD_PRELOAD in die Anwendung geladen wird, die debuggt werden soll, wird die Funktion library_loaded() ausgelöst, die zwei verschiedene Aktionen ausführt.

Zunächst wird das Verzeichnis /tmp/Renderdoc erzeugt. Interessanterweise wird dieses Verzeichnis auch weiterverwendet, wenn es bereits existiert, unabhängig davon, ob es dem Benutzer gehört, der Renderdoc gerade verwendet (in diesem Beitrag nennen wir die Benutzerin „Alice“).

Zweitens wird eine Logdatei mit der Struktur /tmp/RenderDoc/Renderdoc_app_YYYY.MM.DD_hh.mm.ss.log geöffnet. Falls diese Logdatei noch nicht existiert, wird sie neu erzeugt, und die Daten werden in einem konsistenten, ununterbrochenen Vorgang angefügt.

Diese Aktionen bergen allerdings eine erhebliche Sicherheitslücke. Ein Angreifer könnte Alice zuvorkommen und das Verzeichnis /tmp/Renderdoc schon vor ihr erstellen. Dieses Verzeichnis kann mit zahllosen Symlinks nach dem Muster /tmp/RenderDoc/RenderDoc_app_YYYY.MM.DD_hh.mm.ss.log gefüllt werden, die jeweils auf eine beliebige Datei im System verweisen. Wenn Alice dann Renderdoc ausführt, wird diese verknüpfte Datei erzeugt (falls sie noch nicht existiert) und in sie geschrieben, und zwar mit den Rechten von Alice.

Der Angreifer kann mit dieser Strategie beliebige Zeichenfolgen in diese Datei einfügen, indem er die Zeichenfolgen über den TCP-Port 38920 an Renderdoc sendet. Allerdings gibt es für den Angreifer eine Hürde: RenderDoc stellt jeder eingehenden Zeichenkette einen Header voran, den der Angreifer nicht manipulieren kann. Enthält die übermittelte Zeichenfolge Zeilenumbrüche, teilt Renderdoc sie in mehrere Zeilen auf, die jeweils mit dem nicht manipulierbaren Header beginnen. Die fehlende Kontrolle über den Header vereitelt Versuche des Angreifers, über Alices Standard-Punktdateien wie .profile, .bashrc, .ssh/authorized_keys usw. seine Rechte zu erweitern.

Unsere Untersuchung des Problems der nicht-manipulierbaren Header führte zu einer Lösung in zwei Schritten:

Schritt 1: Vom Symlink zum Directory Creation Exploit:

Zunächst entwickelten wir für die Symlink-Schwachstelle in Renderdoc einen Exploit zur Erstellung beliebiger Verzeichnisse. Dazu schrieben wir ‚SYSTEMD=.config/systemd‘ in die Datei .config/user-dirs.defaults in Alices Benutzerverzeichnis. Dies bewirkt, dass automatisch das Verzeichnis .config/systemd erstellt wird, wenn sich Alice das nächste Mal einloggt. Um das Problem der nicht manipulierbaren Header zu überwinden, mussten wir eine Zeichenkette schreiben, die länger als 512 Bytes war, sodass fgets() eine Zeile zurückgibt, die mit unseren Daten beginnt.

Schritt 2: Vom Symlink zum Ausführen von beliebigem Code:

Anschließend nutzten wir die Symlink-Schwachstelle zur Ausführung von beliebigem Code, indem wir in Alices Datei .config/systemd/user.conf schrieben. Wir fügten ‚DefaultEnvironment=LD_PRELOAD=/var/tmp/shell.so‘ ein, was systemd dazu veranlasst, bei Alices nächster Anmeldung unsere Shared Library mit den Rechten von Alice auszuführen. Um bei diesem Schritt das Problem der nicht manipulierbaren Header zu umgehen, verwendeten wir \r als Trennzeichen, da Renderdoc nur nach \n einen Header hinzufügt.

 

Ein näherer Blick auf CVE-2023-33864: Vom Integer-Unterlauf zum Heap-Überlauf

Wenn ein Client eine Verbindung zum Server-Thread von librenderdoc.so am TCP-Port 38920 herstellt, sendet der Client zunächst ein Handshake-Paket, das einen „client-name“-String enthält. Der Server verarbeitet diesen String mit einer Reihe von Vorgängen. Zunächst weist der Server einen 64 KByte großen Zwischenpuffer zu und liest das Handshake-Paket des Clients in diesen ein. Anschließend extrahiert der Server die Länge des client-name-Strings (bezeichnet als ‚len‘) aus diesem Zwischenpuffer und weist einen String Buffer mit der entsprechenden Größe zu.

Je nach Länge des Client-Namens wählt der Server eine von zwei Vorgehensweisen. Bei Client-Namen, die länger als 10 MB sind, liest der Server den Namen direkt in den String Buffer ein. Andernfalls liest er den Client-Namen in den Zwischenpuffer ein und kopiert ihn dann in den String Buffer. Genauer betrachtet, läuft der Lesevorgang allerdings folgendermaßen ab: ReadLargeBuffer() liest alles bis auf die letzten 128 Bytes des Client-Namens direkt in den String Buffer und die letzten 128 Bytes in den Zwischenspeicher ein. Diese Bytes werden dann in den String Buffer kopiert.

Ein Fehler tritt bei der Funktion ReadFromExternal() auf, die fälschlicherweise annimmt, dass m_InputSize (die insgesamt gelesenen Bytes) nie größer sein wird als m_BufferSize (die Größe des Zwischenpuffers). Im Fall von ReadLargeBuffer() ist jedoch m_InputSize größer als 10 MB, während m_BufferSize nur 64 KB beträgt, was zu einem Unterlauf bei der Berechnung von bufSize führt. Dieser Fehler bewirkt, dass die an recv() übergebene Größe die Größe des Ziel-Buffers bei weitem übersteigt. Diese Fehlfunktion gibt einem Angreifer die Möglichkeit, den String- oder Zwischenpuffer zum Überlaufen zu bringen.

Bei der Ausnutzung von CVE-2023-33864 machten wir uns eine Heap-Überlaufschwachstelle im Multi-Threaded-TCP-Server von librenderdoc.so zunutze. Zunächst weist die glibc malloc-Funktion bei der Erstellung des Server-Threads einen neuen, 64 MB großen Speicher als „Heap“ zu. Anschließend bauen wir sieben aufeinanderfolgende Verbindungen auf, wobei jede einen neuen Thread mit zugewiesenem Stack-Speicher erzeugt.

Diese Speicher werden nicht freigegeben, sodass sich die zufallsbasierte Lücke zwischen dem Heap und den Bibliotheken füllt. Anschließend stellen wir eine Verbindung zum Server her und senden ein Handshake-Paket mit einem 16 MByte großen client-name-String, um das Heap-Layout des Servers zu manipulieren. Dies führt zu einem Pufferüberlauffehler, da wir den Zwischenpuffer und die Header der angrenzenden Speicherstücke überschreiben.

Wenn der Server den Zwischenspeicher sowie ein bestimmtes Speicherstück freigibt, wird die Funktion munmap_chunk() ausgelöst, die es uns ermöglicht, mit munmap() das Mapping für einen beliebigen Speicherblock entsprechend diesem Stück aufzuheben. Dies erzeugt ein Loch von genau 8 MB+4 KB im Heap des Servers. Anschließend wird ein neuer Client-Thread erzeugt, dessen Stack genau dem von uns erzeugten Loch zugewiesen wird. Danach bauen wir eine langlebige Verbindung auf und senden einen 14 MB großen client-name-String, der das Ende des Client-Namens mit Daten aus dem Client-Stack überschreibt. Und schließlich stellen wir eine weitere Verbindung her und senden einen 9 MB großen String, um den Client-Stack mit Daten zu überschreiben, die wir vollständig unter Kontrolle haben, was zu einem klassischen „Stack Smashing“-Angriff führt. Mit diesem Verfahren werden Speicherzuweisung, Pufferüberlauf und die Funktion munmap() geschickt manipuliert, um die Schwachstellen des Servers auszunutzen. Dies gelingt trotz moderner glibc-, malloc-, ASLR-, PIE-, NX- und Stack-Canary-Schutzmechanismen.

 

Das Pufferüberlauf-Dilemma bei librenderdoc.so: Analyse von CVE-2023-33863

Wir untersuchten eine Anomalie im Server von librenderdoc.so, der am TCP-Port 38920 arbeitet. Wenn ein Client versucht, eine exakt 0xffffffff Byte lange Zeichenfolge (UINT32_MAX) zu senden, wird eine unerwartete Folge von Ereignissen ausgelöst.

Der Server verändert diese uint32_t Länge zu einer vorzeichenbehafteten Ganzzahl. Dies bewirkt eine unbeabsichtigte Vorzeichenerweiterung von 0xffffffff int zu 0xffffffffffffffff size_t (SIZE_MAX) in der Funktion resize(), da das Argument ein size_t Integer (64-Bit-Ganzzahl auf amd64) ist.

Diese Folge von Umwandlungen führt dazu, dass die Funktion reserve() einen Puffer für die Zeichenkette zuweist und zur String-Größe 1 (für einen Nullterminator) hinzufügt. Dies bewirkt einen Integer-Überlauf der SIZE_MAX Größe dieser Zeichenkette auf 0, und es wird ein Puffer mit minimaler Größe zugewiesen. Dieser Puffer ist deutlich kleiner, als es die lange Zeichenfolge des Clients erfordert, sodass der Client diesen Heap-Puffer mit bis zu UINT32_MAX-Bytes überfluten kann. Wir haben allerdings nicht versucht, diese Sicherheitslücke auszunutzen.

In der heutigen, von eskalierenden Cyberbedrohungen geprägten Sicherheitslandschaft ist es dringlicher denn je, digitale Assets zuverlässig zu schützen. Wenn Sie Software wie RenderDoc verwenden, sollten Sie unbedingt Schutzmechanismen wie Firewalls für bestimmte TCP- und UDP-Ports einrichten, die das Programm für den Betrieb verwendet. Bei RenderDoc gilt dies für die TCP- und UDP-Ports im Bereich 38920-38927 sowie die TCP- und UDP-Ports Nummer 39920. Mit einer angemessenen Absicherung dieser Ports können die Anwender die Gefahr unbefugter Zugriffe oder böswilliger Cyber-Aktivitäten erheblich verringern. Es ist wichtig, sich bewusst zu machen, dass in der modernen vernetzten Welt proaktive Verteidigungsstrategien keine Option sind, sondern eine absolute Notwendigkeit.

 

Fazit

Bei der Entwicklung und dem Debuggen von Grafiksoftware spielt RenderDoc eine zentrale Rolle. Doch wie bei jeder komplexen Software können auch hier Schwachstellen auftreten. Die oben beschriebenen drei Sicherheitslücken sind eine Mahnung, dass in unserer digitalen Welt ständige Wachsamkeit erforderlich ist. Solche Schwachstellen zu verstehen ist der erste Schritt zur Verstärkung der Abwehr, und unser Ziel ist es, die möglichen Wege zur Schwachstellenbehebung und Resilienz aufzeigen. Mit zunehmender digitaler Integration werden auch robuste Sicherheitspraktiken immer wichtiger. Es reicht nicht aus, nur die bekannten Sicherheitslücken zu kennen und zu beheben – wir müssen auch die potenziellen Bedrohungen proaktiv ermitteln. Hier kommt ein effektives Schwachstellenmanagement-Programm ins Spiel, das dabei hilft, Sicherheitslücken in den Systemen zu finden und zu beheben, bevor sie ausgenutzt werden. Ein solcher proaktiver Ansatz und die Kenntnis der bestehenden Schwachstellen können die Abwehr eines Unternehmens stärken und Vertrauen in die digitale Landschaft schaffen.

Von Saeed Abbasi, Manager, Vulnerability Signatures, #Qualys