Ausbruch aus Message Brokern
Adam Goldschmidt
5. August 2020
0 Min. LesezeitKürzlich habe ich zwei Schwachstellen in Apache Airflow gemeldet – einer Open-Source-Bibliothek, mit der Entwickler Workflows programmatisch erstellen, planen und überwachen können. Beide Schwachstellen ermöglichen es Angreifern, den Geltungsbereich zu ändern und Berechtigungen für eine andere Maschine zu erlangen. Bei beiden muss der Angreifer vor dem Angriff Zugriff auf den Message Broker erhalten.
In diesem Blogbeitrag möchte ich Ihnen zeigen, warum Message Broker nicht vertrauenswürdig sind und wie ich Apache Airflow ausnutzen konnte, um Berechtigungen auf eigentlich geschützten Maschinen zu erlangen. Bevor wir uns damit befassen, sehen wir uns jedoch zunächst die Grundlagen von Message Brokern an.
Was ist ein Message Broker?
Ein Message Broker ist eine Software, die es Diensten ermöglicht, miteinander zu kommunizieren und Informationen auszutauschen. Das mag einer API ähneln, doch ein Message Broker setzt dafür in der Regel eine Queue ein, in die verschiedene Dienste schreiben oder aus der sie lesen können. So können diese Dienste asynchron miteinander kommunizieren – auch wenn sie in unterschiedlichen Programmiersprachen geschrieben oder auf verschiedenen Plattformen implementiert wurden.
Message Broker können als Brücke zwischen Anwendungen dienen. Sender können so Nachrichten veröffentlichen, ohne zu wissen, wo sich die Empfänger befinden oder wie viele es gibt. Wie oben erwähnt, übernimmt das eine Komponente namens Message Queue, die Nachrichten speichert, bis ein konsumierender Dienst sie verarbeitet. Diese Queues ermöglichen auch asynchrone Programmierung: Da die Queues für die Zustellung der Nachrichten verantwortlich sind, kann der Sender andere Aufgaben erledigen.
Anwendungsfälle von Message Brokern
Message Broker kommen in der Softwareentwicklung häufig zum Einsatz. Sie sind nützlich, wenn eine zuverlässige Kommunikation zwischen mehreren Diensten, eine garantierte Nachrichtenzustellung oder asynchrone Funktionen erforderlich sind.
Die Anwendungsfälle von Message Brokern sind vielfältig. Hier versuche ich, die gängigsten zu benennen:
Zahlungsabwicklung: Zahlungen müssen genau einmal gesendet werden. Werden diese Transaktionen über Message Broker abgewickelt, gehen Zahlungsinformationen weder verloren noch werden sie doppelt verarbeitet. Außerdem lässt sich der Empfang nachweisen.
Asynchrone Aufgaben: Aufwendige Verarbeitung lässt sich von einer Live-Benutzeranfrage entkoppeln. So erfolgt die Antwort sofort und der Benutzer wird nicht blockiert.
Nachrichten an ein oder mehrere Ziele weiterleiten: Es ist einfacher und besser wartbar, Nachrichten an eine einzige Quelle zu veröffentlichen, aus der mehrere Dienste lesen können.
Nachdem Sie nun wissen, was Message Broker sind und wofür sie verwendet werden, sehen wir uns an, warum und wie sie angreifbar sein können.
Zu viel Vertrauen in Message Broker
Jeder Entwickler weiß, dass Datenbanken kompromittiert werden können. Üblicherweise ergreifen wir zusätzliche Sicherheitsmaßnahmen – etwa das Verschlüsseln von Passwörtern –, damit Angreifer es schwerer haben, selbst wenn die Datenbank irgendwie kompromittiert wurde.
Logischerweise sollte das bei Message Brokern genauso sein, oder? Die übertragenen Daten erreichen schließlich verschiedene Maschinen und sollten besonders sorgfältig behandelt werden. Dazu gehört nicht nur, vertrauliche Informationen zu verschlüsseln, sondern auch die Maschinen zu schützen, die mit ihnen interagieren.
Leider ist das nicht der Fall. Unser Sicherheitsteam entdeckte in freier Wildbahn einige Beispiele für den unsicheren Einsatz von Message Brokern – darunter Apache Airflow. Dabei wird den Message Brokern zu viel Vertrauen entgegengebracht. So werden beispielsweise Befehle in ihnen gespeichert und anschließend ohne Bereinigung ausgeführt. Das kann zu Befehlsinjektionen oder zur Remote-Code-Ausführung auf den Maschinen führen, die mit den Brokern kommunizieren.
Apache Airflow ausnutzen
Was ist Apache Airflow?
Wie oben kurz erwähnt, ist Apache Airflow ein umfassendes Workflow-Managementsystem. Mit Airflow werden Workflows als DAGs (gerichtete azyklische Graphen) entworfen und dargestellt. Jeder Schritt des DAGs ist als bestimmte Aufgabe definiert. Die Code-first-Plattform ermöglicht es Ihnen, Workflows schnell und effizient weiterzuentwickeln.
Airflow enthält einen Task-Scheduler, der für die Planung und Ausführung der DAGs zuständig ist. Der Scheduler verwendet eine Komponente namens Executor, um die DAGs auszuführen.
Eine Zero-Day-Schwachstelle in Airflow finden
Alles begann damit, dass ich nach Deserialisierungsschwachstellen in Open-Source-Software suchte, die Celery als Abhängigkeit verwendet – eine verteilte Python-Task-Queue, die einen Message Broker implementiert.
Ich fand heraus, dass Airflow Celery (als Executor) standardmäßig mit pickle verwendet. Außerdem wusste ich, dass es eine bekannte Schwachstelle im Python-Modul pickle gibt, das Celery früher standardmäßig verwendete.
Das folgende Diagramm zeigt grob, wie Airflow mit Celery funktioniert:

Der Airflow-Scheduler verwendet Celery als Executor. Dieser speichert die Aufgaben und führt sie nach einem Zeitplan aus.
Celery verwendet den Message Broker (Redis, RabbitMQ), um die Aufgaben zu speichern. Anschließend lesen die Worker die Aufgaben aus dem Message Broker und führen sie aus.
Die Airflow-Celery-Worker deserialisieren pickle-Daten, die im Message Broker gespeichert sind. Das bedeutet: Wenn ich Zugriff auf den Message Broker erlange, kann ich durch einen Deserialisierungsangriff Remote-Code-Ausführung in den Workern erreichen. Diese Schwachstelle erhielt die Kennung CVE-2020-11982.
Moment, was ist ein Deserialisierungsangriff?
Bei der Serialisierung wird ein Objekt in eine Bytefolge umgewandelt, die auf einem Datenträger oder in einer Datenbank gespeichert oder über Datenströme gesendet werden kann. Der umgekehrte Vorgang, bei dem aus einer Bytefolge ein Objekt erstellt wird, heißt Deserialisierung. Serialisierung wird häufig für die Kommunikation (zum Austausch von Objekten zwischen mehreren Hosts) und für die Persistenz (zum Speichern des Objektzustands in einer Datei oder Datenbank) verwendet.
Bei einem Deserialisierungsangriff deserialisiert die Anwendung Daten, ohne ausreichend zu überprüfen, ob die resultierenden Daten sicher sind. Dadurch kann der Angreifer den Zustand oder den Ausführungsablauf kontrollieren. Ich sage nicht, dass Message Broker keine serialisierten Daten speichern dürfen – es gibt viele Anwendungsfälle, in denen das erforderlich ist. Bei der Umsetzung eines solchen Entwurfs sollte man dies jedoch berücksichtigen und für diese Fälle zusätzliche Sicherheitsmaßnahmen in Betracht ziehen.
Wenn Sie mehr über Deserialisierungsangriffe erfahren möchten, lesen Sie diesen Artikel.
Airflow dazu bringen, meine Aufgaben mit pickle zu deserialisieren
Zunächst erstellte ich eine neue Airflow-Aufgabe und untersuchte ihre Struktur. Ich fügte einen DAG (eine Sammlung von Aufgaben) hinzu, wies ihn einer Queue namens „test“ zu, stellte Redis als Celery-Broker ein und startete die Queue:
airflow worker -q test
Ich untersuchte die Struktur der Redis-Nachrichten und entdeckte, dass die Nachrichten in einer Redis-Hash-Tabelle namens unacked gespeichert werden. Die Werte in dieser Hash-Tabelle haben folgende Struktur:
Interessant sind die Werte body, content-type und content-encoding. Der decodierte Wert von body lautet:
Beachten Sie, dass hier die eigentlichen Befehle gespeichert sind! Das wird für die zweite Schwachstelle wichtig. Außerdem fand ich eine Menge namens unacked_index. Ich nahm an, dass ihre Elemente mit den Schlüsseln der Hash-Tabelle übereinstimmen, und überprüfte das. Zuerst rief ich die Schlüssel der Hash-Tabelle ab:
Anschließend ließ ich mir alle Elemente von unacked_index anzeigen:
Wie Sie sehen, sind es tatsächlich dieselben Werte, nur in einer anderen Reihenfolge. Zu diesem Zeitpunkt war ich ziemlich sicher, dass unacked_index die IDs der auszuführenden Aufgaben speichert und die Hash-Tabelle die Aufgaben den jeweiligen IDs zuordnet. Um eine neue benutzerdefinierte Aufgabe hinzuzufügen, musste ich also einen beliebigen Wert zu unacked_index hinzufügen und anschließend in der Hash-Tabelle ein neues Element mit demselben Wert als Schlüssel und einer schädlichen Payload als Wert erstellen.
Für einen Deserialisierungsangriff brauchte ich eine schädliche Payload. Ich erstellte schnell ein Python-Skript, das eine Payload ausgibt, die beim Deserialisieren mit pickle eine neue Datei namens malicious erstellt:
Mehr über das Ausnutzen von pickle in Python erfahren Sie hier. Um die Queue dazu zu bringen, den schädlichen Wert abzurufen und mit pickle zu deserialisieren, musste ich content-type auf application/x-python-serialize (pickle) und content-encoding auf binary setzen. So sieht ein PoC zum Hinzufügen einer schädlichen Aufgabe aus:
Wie oben beschrieben, ging ich folgendermaßen vor:
Ich fügte
unacked_indexeinen beliebigen Wert als Aufgaben-ID hinzu.Ich fügte
unackedein neues Element hinzu: die zuvor erstellte ID als Schlüssel und eine schädliche Payload als Wert. Die schädliche Payload enthielt die Base64-kodierte Payload als Wert vonbody.
Die folgende Abbildung veranschaulicht den Vorgang:

Ich startete die Test-Queue – und voilà! Auf dem Worker, der die Queue ausführte, wurde eine neue Datei namens malicious erstellt. Der Geltungsbereich wurde vollständig verändert. Die Auswirkungen dieser Schwachstelle erkläre ich, nachdem wir uns die nächste Schwachstelle angesehen haben!
Schwachstelle durch Befehlsinjektion
Nun zur zweiten Schwachstelle: einer Befehlsinjektion mit der Kennung CVE-2020-11981.
Bei der Untersuchung des Airflow-Quellcodes stellte ich fest, dass die Befehle aus dem Message Broker ohne Bereinigung ausgeführt werden. Nachdem ich zunächst – wie üblich, wenn man erfolgreich eine Schwachstelle gefunden hat – „YAY!“ gerufen hatte, kehrte ich zu Redis zurück und fand heraus, dass ich die folgende Payload in den Schlüssel body einschleusen kann:
Nach derselben Logik wie zuvor folgt hier ein Proof of Concept. content-type und content-encoding müssen diesmal nicht geändert werden, da wir keine pickle-Operationen benötigen:
Anschließend fand ich heraus, dass sich diese Payload auch über das RabbitMQ-Dashboard in RabbitMQ einschleusen lässt. RabbitMQ verwendet nicht dieselbe Struktur wie Redis, daher genügt eine einfache JSON-Liste ohne Base64-Kodierung.
Die Auswirkungen dieser Schwachstelle ähneln denen der vorherigen: vollständige Kontrolle über Worker-Maschinen. Stellen Sie sich zur Veranschaulichung vor, dass der Angreifer bis zur Ausnutzung dieser Schwachstelle nur Zugriff auf eine einzige Maschine hatte – den Message Broker. Bei manchen Installationen ist es außerdem möglich, über eine API Nachrichten in den Message Broker einzuschleusen, ohne überhaupt Zugriff auf die Maschine zu erlangen, auf der der Message Broker läuft.
Nach der vollständigen Übernahme der Worker-Maschine kann es möglich sein, Geheimnisse offenzulegen, einen Denial-of-Service-Angriff auszuführen und sogar Zugriff auf weitere Maschinen in derselben Infrastruktur zu erlangen. Schwachstellen wie diese, mit denen Angreifer den Umfang und die Berechtigungen ihres Angriffs innerhalb eines kompromittierten Netzwerks ausweiten können, machen mitunter den Unterschied zwischen einem geringfügigen Sicherheitsvorfall und einem umfassenden Sicherheitsvorfall aus.
Behebung

Wenn Sie sich dafür entscheiden, Befehle in Ihrem Message Broker zu speichern, könnte die Behebung wie in der obigen Abbildung aussehen. Selbst wenn ein Angreifer den Message Broker kontrolliert, führt der Worker so nur bekannte Befehle aus und bereinigt unerwartete Eingaben. Apache entschied sich für diese Behebungsmethode, da die Paketlogik voraussetzt, dass die Befehle im Message Broker gespeichert werden.
Was die Infrastruktursicherheit betrifft, sollten Sie Ihren Message Broker mit geeigneten Sicherheitsmaßnahmen wie einer ordnungsgemäßen Authentifizierung und TLS absichern. So wird es Angreifern erschwert, überhaupt Zugriff darauf zu erlangen.
Fazit
Diese beiden Schwachstellen zeigen, wie ich allein durch Zugriff auf den Message Broker Code und Befehle auf dem Queue-Server (oder Worker) ausführen konnte. Wichtig ist dabei, dass Redis in seiner Standardkonfiguration kein Passwort erfordert.
Das Apache-Team hat diese Schwachstellen schnell anerkannt und behoben. Das Snyk Security Team stuft sie zwar nicht als besonders kritisch ein, da für beide zunächst Zugriff auf die Infrastruktur erforderlich ist. Dennoch sind sie gefährlich. Es ist nicht ungewöhnlich, dass für die Ausnutzung einer Schwachstelle zunächst Berechtigungen oder weitere Schwachstellen – also eine Art Angriffskette – erforderlich sind.
Die wichtigste Erkenntnis, die Sie aus diesem Beitrag mitnehmen sollten, lautet: Vertrauen Sie Ihren Message-Brokern nicht blind. Denken Sie über Design und Architektur der Broker nach und überlegen Sie, wie Sie die Risiken bei ihrem Einsatz minimieren können.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.


