CVE-2023-30534: Unsichere Deserialisierung in Cacti Versionen älter als 1.2.25
Übersicht
Wir haben zwei Fälle von unsicherer Deserialisierung in Cacti Versionen älter als 1.2.25 entdeckt, die unter CVE-2023-30534 geführt werden. Eine unsichere Deserialisierung lässt sich zwar remote steuern, kann aber aufgrund der eingeschränkten Ausführungsumgebung nicht als Exploit missbraucht werden (siehe Abschnitt „Exploit-Versuche“).
Für diejenigen, die Cacti noch nicht kennen: Cacti ist eine Open-Source-Lösung für die Verwaltung von Netzwerken und Erstellung von Diagrammen, die die Datenspeicher- und Diagrammfunktionen von RRDtool nutzt, um Geräte zu erkennen, Diagramme zu erstellen und vieles mehr. Obwohl einige Instanzen extern zugänglich sind, dient Cacti in erster Linie der Überwachung interner Netzwerksysteme.
Sehen wir uns die technischen Hintergründe aber einmal genauer an: Unter Serialisierung versteht man die Umwandlung komplexer Datenstrukturen wie denen von PHP- oder Java-Objekten in ein Format, das sich leichter über ein Netzwerk übertragen lässt. Bei der Deserialisierung werden diese Daten wieder in eine komplexe Datenstruktur zurückverwandelt. Von unsicherer Deserialisierung ist die Rede, wenn nutzergesteuerte Daten deserialisiert werden und Angreifern dadurch die Möglichkeit zum Instanziieren beliebiger Objekte, zum Lesen und Schreiben von Dateien und sogar zur Codeausführung aus der Ferne gegeben wird.
Hinweis: In Cacti Version 1.2.25 (und 1.3.0) wurden 17 weitere Schwachstellen behoben, darunter kritische und hochgefährliche Schwachstellen. Administratoren sollten also nach Möglichkeit ein Upgrade durchführen.
Betroffene Versionen
Wir wurden erstmals in Cacti Version 1.2.10 auf die unsichere Deserialisierung aufmerksam. Allerdings sind alle Versionen älter als 1.2.25 von der Schwachstelle betroffen. In der parallel zu Cacti Version 1.2.5 veröffentlichten Version 1.3.0 wurde die unsichere Deserialisierung auch behoben.
Schwachstellenanalyse: unsichere Deserialisierung
Jeder Fall von unsicherer Deserialisierung ist darauf zurückzuführen, dass die Funktion unserialize
verwendet wurde, ohne dass die Nutzereingaben ordnungsgemäß bereinigt wurden. Cacti bietet eine Funktion zur „sicheren Deserialisierung“, die versucht, den Inhalt des Strings zu bereinigen und zu überprüfen, ob er nur bestimmte Werte enthält, bevor sie „unserialize“ aufruft. Diese Funktion wurde aber in den genannten Fällen nicht verwendet.
Hinweis: Die in Codebausteinen verwendeten Ellipsen („…“) stehen für nicht referenzierten Code, der der Übersichtlichkeit halber entfernt wurde.
managers.php
Der von der Schwachstelle betroffene Code befindet sich in der Datei managers.php
, genauer gesagt in der Funktion „form_actions“. Das nachfolgende Code-Snippet stammt aus Cacti Version 1.2.10:
function form_actions() { global $manager_actions, $manager_notification_actions; if (isset_request_var('selected_items')) { if (isset_request_var('action_receivers')) { ... ... ... } elseif (isset_request_var('action_receiver_notifications')) { get_filter_request_var('id'); $selected_items = unserialize(stripslashes(get_nfilter_request_var('selected_items'))); ... } ...}
Indem wir die POST-Anfragevariable action_receiver_notifications
setzen, können wir den Codefluss so steuern, dass er in der Funktion form_actions
auf den Call unserialize
stößt. Um form_actions
aufzurufen, müssen wir die Variable „action“ auf „actions“ setzen. Dadurch stellen wir sicher, dass wir den richtigen Fall in der Switch-Anweisung treffen (siehe unten):
switch (get_request_var('action')) { case 'save': form_save(); break; case 'actions': form_actions(); break; ... ...
Um die anfällige Funktion unserialize
anzugreifen, sendet ein authentifizierter Nutzer wie folgt eine POST-Anfrage, die ein URL-kodiertes serialisiertes PHP-Objekt in der Variablen „selected_items“ enthält:
Wenn wir ein ungültiges Objekt (z. B. ein fehlerhaftes Objekt oder eine Klasse, die Cacti nicht kennt) als Wert für die Variable „selected_items“ angeben, sehen wir in den Logs, dass Cacti die Payload nicht deserialisieren kann:
An dieser Stelle können wir den Codefluss steuern, um „unserialize“ für ein nutzergesteuertes, serialisiertes Objekt in managers.php aufzurufen.
graphs_new.php
Der anfällige Code befindet sich in der Datei graphs_new.php
, genauer gesagt in der Funktion host_new_graphs_save
. Das nachfolgende Code-Snippet stammt aus Cacti Version 1.2.10:
function host_new_graphs_save($host_id) { $selected_graphs_array = unserialize(stripslashes(get_nfilter_request_var('selected_graphs_array')));
$values = array();
Diese Funktion deserialisiert den nutzergesteuerten Parameter selected_graphs_array
. Durch die vorherige Ausführung von stripslashes
werden lediglich zusätzliche Backslashes entfernt, die in Strings während der Vorverarbeitung benötigt werden (z. B. \” zu “).
Um zu dieser Funktion zu gelangen, stellt der Nutzer eine Anfrage an graphs_new.php
mit zwei Parametern: action=save
und save_component_new_graphs=1
. Dadurch wird die anfällige Funktion, wie im folgenden Code-Snippet dargestellt, aufgerufen:
switch (get_request_var('action')) { case 'save': form_save(); break; case 'query_reload': ... ... ...function form_save() { ... ... ... if (isset_request_var('save_component_new_graphs')) { host_new_graphs_save(get_filter_request_var('host_id'));
header('Location: graphs_new.php?host_id=' . get_filter_request_var('host_id') . &header=false'); }}
Um die anfällige Funktion unserialize
anzugreifen, sendet ein authentifizierter Nutzer wie folgt eine POST-Anfrage, die ein URL-kodiertes serialisiertes PHP-Objekt in der Variablen selected_graphs_array
enthält:
Wenn wir ein ungültiges Objekt (z. B. ein fehlerhaftes Objekt oder eine Klasse, die Cacti nicht kennt) als Wert für die Variable selected_graphs_array
angeben, sehen wir in den Logs, dass Cacti versucht, die Payload zu deserialisieren:
An dieser Stelle können wir den Codefluss steuern, um „unserialize“ für ein nutzergesteuertes, serialisiertes Objekt in graphs_new.php
aufzurufen.
Exploit-Versuche
Magische Methoden in PHP
Die Ausnutzung einer PHP Deserialisierungsschwachstelle bedarf des Zugriffs auf Objekte, bei denen „magische Methoden“ implementiert wurden. Magische Methoden werden bei Objekten beim Erstellen oder Zerstören von Objekten sowie in anderen Situationen automatisch aufgerufen. Beispiele für diese Methoden sind __construct
, __unserialize
, __destruct
, __wake
und andere. Wir benötigen Zugriff auf Objekte, die diese Funktionen verwenden, da einige von ihnen automatisch aufgerufen werden, wenn unsere Payload zu einem Objekt deserialisiert wird. Unter einem „Gadget“ versteht man in diesem Zusammenhang ein Objekt, in dem eine dieser Methoden implementiert ist und das wir in einer Payload verwenden können. Durch die Verwendung von verschachtelten Objekten in Payloads kombinieren wir diese Gadgets zu einer „Gadget Chain“, um eine gewünschte Aktion wie das Lesen oder Schreiben einer Datei bzw. die Ausführung von Code durchzuführen.
Suchen nach Gadget Chains
Im Laufe der Zeit sind zahlreiche Gadget Chains in beliebten Bibliotheken aufgetaucht. Das Tool PHPGCC verfügt über eine Liste bekannter Gadget Chains, die es zur Erstellung von Payloads für unsichere Deserialisierungsschwachstellen verwenden kann. Unter den PHP-Anbietern von Cacti ist eine verwendbare Gadget Chain namens PHPSecLib bekannt. Cacti benötigt allerdings keines der notwendigen Gadgets (TripleDES, AES und SSH1), um die Gadget Chain PHPSecLib in einer Payload erfolgreich nutzen zu können.
Neben der Nutzung vorhandener Gadget Chains können Sie auch neue erstellen, wenn das Projekt über Objekte mit verwendbaren magischen Methoden verfügt. Sie können sich zum Beispiel die Implementierungen der __construct-Methode von Cacti ansehen, indem Sie im Quellcode überprüfen, ob einige von ihnen verwendbar sind. Nach Durchsicht der implementierten magischen Methoden in den PHP-Objekten von Cacti wurde keine gefunden, die in einer neuen Gadget Chain verwendbar wäre.
Die Tatsache, dass keine verwendbare Gadget Chain gefunden werden kann, führt dazu, dass sich die Schwachstelle nicht ausnutzen lässt.
Nuclei Template für CVE-2023-30534
Um Ihnen die Aufdeckung dieser Sicherheitslücke zu erleichtern und Sie bei der Behebung der Schwachstelle zu unterstützen, haben wir ein Nuclei Template erstellt, mit dem Sie Ihren Code auf CVE-2023-30534 testen können. Diese Vorlage erfordert eine Authentifizierung und übergibt ein ungültiges PHP-Objekt, um eine Log-Meldung zu erzeugen, die belegt, dass „unserialize“ für das Objekt aufgerufen wurde. Anschließend prüft das Template, ob der Aufruf von „unserialize“ erfolgreich war, indem es die Logs nach einer Fehlermeldung durchsucht und sicherstellt, dass die Funktion „unserialize“ für unsere Payload aufgerufen wurde. Das lokal ausgeführte Template sieht wie folgt aus:
Cacti Administratoren können die Sicherheitslücke durch ein Upgrade auf Version 1.2.5 oder höher (z. B. 1.3.0) beheben. Diese Schwachstelle lässt sich in einer Standardinstallation von Cacti nicht ausnutzen. Allerdings empfehlen wir Ihnen, die Sicherheitshinweise von Cacti zu befolgen, um weitere Einzelheiten zur Behebung zu erfahren.
Hinweis: In Cacti Version 1.2.25 (und 1.3.0) wurden 17 weitere Schwachstellen behoben, darunter kritische und hochgefährliche Schwachstellen. Administratoren sollten also schnellstmöglich ein Upgrade durchführen.