1 Punkte von GN⁺ 2024-10-18 | 1 Kommentare | Auf WhatsApp teilen
  • Durch das Zusammenspiel der WebUI-Berechtigungsgrenze von Chromium, einer Enterprise-Policy-Testfunktion und eines Fehlers in der DevTools-Erweiterungs-API konnte eine bösartige Chrome-Erweiterung mit nur geringer Nutzerinteraktion bis zur Ausführung von Shell-Befehlen gelangen
  • Der Angriffsweg bestand darin, auf chrome://policy eine undokumentierte Policy-Test-API aufzurufen, um Benutzer-Policies zu ändern, und anschließend den alternativen Browserpfad und die Argumente von Browser Switcher für Shell-Befehle zu missbrauchen
  • chrome.devtools.inspectedWindow.reload() erlaubte die Ausführung von injectedScript, und wegen verzögerter Zugriffssperren beim Wechsel zu WebUI oder wegen nach einem Crash verbliebener Page.reload-Anfragen konnte Code in einer privilegierten WebUI ausgeführt werden
  • Google stufte die Schwachstellen als P1/S1 ein und ergänzte eine loaderId-Prüfung für Page.reload, eine URL-Prüfung für inspectedWindow.reload() sowie eine Prüfung, ob Policy-Tests in WebUI-Handlern aktiviert sind
  • Die zugehörigen Schwachstellen wurden als CVE-2024-5836 und CVE-2024-6778 vergeben, beide mit CVSS 8.8 High; die endgültige Prämie betrug $20,000

Chromium WebUI und die Sandbox-Grenze

  • Chromium führt nicht vertrauenswürdigen Code in einer Sandbox aus, und auch JavaScript von Chrome-Erweiterungen darf nur innerhalb der erteilten Berechtigungen und zugänglichen APIs arbeiten
  • Schon allein mit Erweiterungsberechtigungen lassen sich Login-Daten oder der Browserverlauf stehlen, doch grundsätzlich sollte die Wirkung auf das Innere des Browsers beschränkt bleiben
  • Teile der Chromium-GUI sind als WebUI wie chrome://settings oder chrome://history implementiert
    • WebUI ist zwar in HTML, CSS und JavaScript geschrieben, besitzt aber höhere Rechte als normale Webseiten, weil sie interne Browserinformationen anzeigen und verändern muss
    • Das JavaScript-Frontend der WebUI kann über private APIs mit nativem C++-Code des Browsers kommunizieren
  • Wenn Codeausführung in einer WebUI möglich wird, kann das zu einer Umgehung der Chromium-Sandbox führen; deshalb ist es wichtig zu verhindern, dass Angreifer nicht vertrauenswürdiges JavaScript auf chrome://-Seiten ausführen
  • Wenn man zum Beispiel auf chrome://downloads einen .exe-Download anklickt, kann die ausführbare Datei geöffnet werden; deshalb prüft Chromium, ob das Öffnen der Datei tatsächlich durch eine echte Nutzereingabe ausgelöst wurde

Umgehung der Enterprise-Policy-Testfunktion

  • Die Suche nach der Schwachstelle begann im Enterprise-Policy-System von Chromium
    • Dieses System dient dazu, dass Administratoren auf Geräten von Unternehmen oder Schulen bestimmte Einstellungen erzwingen können
    • Policies sind in der Regel an ein Google-Konto gebunden und werden von Google-Verwaltungsservern abgerufen
  • Policies werden in device policies und user policies unterteilt
    • Device Policies verwalten geräteweite Einstellungen auf Chrome OS
    • User Policies gelten für bestimmte Nutzer oder Browser-Instanzen und sind auf allen Plattformen verfügbar
    • Unter Linux lassen sich User Policies auf Google-Chrome-Instanzen anwenden, indem man JSON-Dateien in /etc/opt/chrome/policies ablegt, doch zum Schreiben in dieses Verzeichnis sind Root-Rechte erforderlich
  • Die aktuell auf dem Gerät angewendeten Policies lassen sich in der WebUI chrome://policy einsehen
    • Diese Seite bietet eine Liste der angewendeten Policies, Logs des Policy-Service und eine JSON-Exportfunktion
    • Normalerweise gibt es auf dieser Seite keine Möglichkeit, Policies zu bearbeiten
  • In den Chrome-Enterprise-Release-Notes zu Chrome v117 stand, dass die Seite chrome://policy/test das Testen von Policies in den Kanälen Beta, Dev und Canary erlaubt
    • Außerhalb dieser Release-Notes wurde die Funktion in der Chromium-Dokumentation nicht erwähnt
    • Für die reguläre Aktivierung war die undokumentierte Policy PolicyTestPageEnabled erforderlich
    • Ohne diese Policy wird chrome://policy/test auf chrome://policy umgeleitet

Fehlende Prüfung bei setLocalTestPolicies

  • Der JavaScript-Code von chrome://policy/test setzt Test-Policies mit sendWithPromise('setLocalTestPolicies', ...)
    • sendWithPromise() ist ein Wrapper um die private WebUI-API chrome.send()
    • Dieser Aufruf sendet eine Anfrage an eine C++-Handlerfunktion, die interne Browseraktionen ausführen kann
  • Als setLocalTestPolicies direkt aus der Konsole von chrome://policy aufgerufen wurde, stürzte der Browser zunächst ab, und im Log blieb eine Meldung zurück, dass ein Policy-Array erforderlich sei
  • Nachdem das Format des Policy-Arrays angepasst und eine User Policy wie AllowDinosaurEasterEgg übergeben wurde, ließ sich eine beliebige Policy setzen, obwohl die Funktion nicht ausdrücklich aktiviert worden war
  • Der C++-Handler HandleSetLocalTestPolicies prüfte lediglich, ob local_test_provider existierte, aber nicht, ob die Policy-Testfunktion tatsächlich erlaubt war
  • LocalTestPolicyProvider::CreateIfAllowed() ruft IsPolicyTestingEnabled(nullptr, channel) auf
    • Da das erste Argument pref_service null war, wurde die Prüfung von PolicyTestPageEnabled übersprungen
    • Übrig blieb nur die Prüfung, ob der Release-Kanal CANARY oder DEFAULT ist
  • In nicht gebrandeten Chromium-Builds wird Code unter GOOGLE_CHROME_BRANDING nicht kompiliert, sodass der Kanal auf UNKNOWN bleibt
    • Da im Enum UNKNOWN = 0 und DEFAULT = UNKNOWN gilt, besteht Chromium und darauf basierende Builds die Kanalprüfung
    • In gebrandeten stabilen Google-Chrome-Builds wird der Release-Kanal korrekt gesetzt, daher funktionierte dieser Bug dort nicht

Shell-Befehlsausführung über Browser Switcher

  • Durch das Setzen beliebiger User Policies wurde das Chrome-Enterprise-Policy-Modul Legacy Browser Support zu einem Pfad für den Sandbox-Escape
  • Legacy Browser Support ist auch als Browser Switcher bekannt und soll beim Besuch bestimmter URLs in Chromium einen alternativen Browser starten
    • Die Funktion wurde zur Unterstützung von Internet-Explorer-Nutzern entwickelt
    • Das Verhalten wird durch Policies gesteuert
  • Durch die Kombination der Policies AlternativeBrowserPath und AlternativeBrowserParameters konnte Chromium als „alternativen Browser“ beliebige Shell-Befehle ausführen
    • Diese Browser-Switcher-Policies existieren nur unter Linux, macOS und Windows
  • Ein Beispielfluss sieht so aus
    • BrowserSwitcherEnabled wird auf true gesetzt
    • BrowserSwitcherUrlList erhält example.com
    • AlternativeBrowserPath wird unter Linux auf /bin/bash gesetzt
    • AlternativeBrowserParameters wird etwa auf ["-c", "xcalc # ${url}"] gesetzt
  • Navigiert der Browser zu example.com, springt Browser Switcher an und führt einen Befehl der Form /bin/bash -c 'xcalc # https://example.com' aus
    • Der ersetzte Wert ${url} wird hinter # platziert und damit zu einem Shell-Kommentar gemacht
  • Nachdem die Policies auf chrome://policy gesetzt waren, reichte ein Aufruf von window.open("https://example.com";), um allein über JavaScript zur Ausführung beliebiger Shell-Befehle zu gelangen

Umgehung über die DevTools-Erweiterungs-API

  • Allein die vorigen Schritte waren wenig praktikabel, weil das Opfer bösartigen Code in die Browser-Konsole von chrome://policy hätte einfügen müssen
  • Ein automatischer Ausführungsweg wurde über eine bösartige Chrome-Erweiterung gefunden
    • Erweiterungen können JavaScript in Seiten einfügen, sollten es aber auf privilegierten WebUI-Seiten nicht ausführen können
  • Es gab vier zentrale APIs, über die Erweiterungen JavaScript auf Seiten ausführen konnten
    • chrome.scripting
    • chrome.tabs in Manifest v2
    • chrome.debugger
    • chrome.devtools.inspectedWindow
  • Untersucht wurde chrome.devtools.inspectedWindow, weil es vergleichsweise weniger abgesichert wirkte
    • Erweiterungen, die die API chrome.devtools verwenden, müssen im Manifest ein Feld devtools_page haben
    • Wenn der Nutzer DevTools öffnet, wird diese Seite als iframe geladen, und darin kann die API chrome.devtools verwendet werden
  • In einem früheren Bug-Report von David Erceg gab es bereits einen Fall, in dem chrome.devtools.inspectedWindow.eval() zu Codeausführung in WebUI führte
    • Normalerweise sollte die DevTools-API deaktiviert werden, sobald die untersuchte Seite zu einer WebUI navigiert
    • Der Kern der Umgehung bestand darin, eine eval-Anfrage zu senden, bevor Chrome die API deaktiviert, sodass sie erst auf der WebUI-Seite ankommt

inspectedWindow.reload() und die Besonderheit von about:blank

  • Auch chrome.devtools.inspectedWindow.reload() kann JavaScript auf der untersuchten Seite ausführen, wenn das Argument injectedScript übergeben wird
  • Als inspectedWindow.reload() auf einer von WebUI geöffneten about:blank-Seite aufgerufen wurde, war die Ausführung von JavaScript auf einer privilegierten Seite möglich
    • about:blank ist als URL selbst nicht besonders, übernimmt aber die Berechtigungen und die Origin der Seite, die es geöffnet hat
    • Eine von chrome://settings geöffnete about:blank-Seite war daher eine privilegierte Seite mit der Origin chrome://settings
  • Der Code zum Deaktivieren der DevTools-API prüfte nur die URL des Untersuchungsziels, nicht dessen Origin
    • Die URL konnte harmlos aussehen, obwohl die Origin privilegiert war
  • Allein der about:blank-Pfad ließ sich nicht direkt in die Exploit-Kette einbauen, weil chrome://policy kein about:blank-Popup öffnet
  • Allerdings führte inspectedWindow.reload() auch dann JavaScript in chrome://settings aus, wenn inspectedWindow.eval() scheiterte
    • Das zeigte, dass eval() eine eigene Prüfung ähnlich einer Origin-Kontrolle hatte, reload() jedoch keine gleichwertige Prüfung

Von der Race Condition zur stabilen Crash-basierten Methode

  • Die erste Exploit-Kette rief inspectedWindow.reload() wiederholt auf und zielte auf das kurze Zeitfenster direkt nach der Navigation der untersuchten Seite zu einer WebUI, aber vor der Deaktivierung der API durch die DevTools-Seite
    • Voraussetzung war, dass die untersuchte Seite und die DevTools-Seite in unterschiedlichen Prozessen liefen
    • Traf eine reload()-Anfrage im Moment zwischen Navigation zu chrome://policy und API-Deaktivierung ein, wurde Code in der WebUI ausgeführt
  • Das funktionierte, war aber unzuverlässig
    • Nach Feinabstimmung lag die Erfolgsquote bei etwa 70%
    • Die Schwachstelle war schwerwiegend, aber die Instabilität konnte die Einstufung reduzieren
  • Danach wurde – ähnlich wie in David Ercegs früherem Ansatz – geprüft, ob sich das Verhalten verbliebener Debugger-Anfragen nach einem Tab-Crash auch auf inspectedWindow.reload() anwenden ließ
  • Wurde zweimal hintereinander eine debugger-Anweisung ausgelöst, stürzte der Tab ab, und eine Page.reload-Anfrage blieb in der Warteschlange und konnte nach der Navigation zur WebUI ausgeführt werden
    • Dadurch war keine Race Condition mehr nötig, und der Angriff funktionierte 100% reliable
  • In einem früheren Patch hatte Google zwar dafür gesorgt, dass ausstehende Debugger-Anfragen nach einem Crash verworfen werden, Page.reload blieb aber als Ausnahme bestehen
    • inspectedWindow.reload() sendet intern eine Page.reload-Anfrage und war daher von dieser Ausnahme betroffen
    • Der damalige Patch verhinderte nicht, dass Page.reload Skript ausführen kann
  • Ein Tab-Crash ließ sich außer über die debugger-Methode auch durch künstlichen Speichermangel auslösen, im finalen PoC wurde aber der schnellere debugger-Crash verwendet

Finale Exploit-Kette und Nutzerinteraktion

  • Das finale PoC lief in folgender Reihenfolge ab
    • Über die Schwachstelle in chrome.devtools.inspectedWindow.reload() wurde ein JavaScript-Payload auf chrome://policy ausgeführt
    • Das Payload rief sendWithPromise("setLocalTestPolicies", policy) auf, um Benutzer-Policies zu setzen
    • Es setzte BrowserSwitcherEnabled, BrowserSwitcherUrlList, AlternativeBrowserPath und AlternativeBrowserParameters
    • Anschließend wurde Browser Switcher per window.open() oder Navigation ausgelöst, um Shell-Befehle auszuführen
  • Das PoC verwendete je nach Betriebssystem Befehle zum Starten des Taschenrechners
    • Windows: C:\Windows\System32\cmd.exe und calc.exe
    • Linux: /bin/bash und xcalc
    • macOS: /bin/bash und open -na Calculator
  • Die notwendige Nutzerinteraktion beschränkte sich im Wesentlichen darauf, DevTools öffnen zu lassen
    • Der Hinweis „extension install error“ im Beispielbildschirm diente dazu, Nutzer zum Öffnen von DevTools zu verleiten
    • Sobald DevTools geöffnet waren, begann die Kette, die zum Sandbox-Escape führte

Googles Fixes und CVE-Vergabe

Zeitplan und Materialien

  • Die Timeline sieht wie folgt aus
      1. April: Test-Policies-Bug entdeckt
      1. April: Race-Condition-Bug in inspectedWindow.reload() entdeckt
      1. Mai: Bug an Google gemeldet
      1. Mai: Von Google als P1/S1 eingestuft
      1. Mai: Bug im Zusammenhang mit dem Crash der untersuchten Seite entdeckt und Meldung aktualisiert
      1. Mai: Google bat darum, für die einzelnen Teile der Kette separate Bug-Reports einzureichen
      1. Juli: Bug-Report als fixed markiert
      1. Juli: Zur Prämienentscheidung an das Chrome-VRP-Panel weitergeleitet
      1. Juli: Das VRP-Panel setzte die Prämie auf $20,000 fest
      1. Oktober: Vollständiger Bug-Report veröffentlicht
  • Der zugehörige ursprüngliche Bug-Report ist unter crbug.com/338248595 einsehbar
  • PoCs zu den einzelnen Teilen der Schwachstellen wurden im GitHub-Repository veröffentlicht
  • Der Bug in inspectedWindow.reload funktionierte bis zurück zu Chrome v45
  • Wenn undokumentierte, unfertige und unsichere Funktionen an alle Nutzer ausgeliefert werden, können sich selbst einfache Fehler zu Schwachstellen mit hoher Schwere kombinieren

1 Kommentare

 
GN⁺ 2024-10-18
Meinungen auf Hacker News
  • Es hieß, die Seiten-URL werde durch ${url} ersetzt, und wenn man sie hinter ein # setzt, werde sie zu einem Kommentar, sodass der Befehl nicht kaputtgeht. Gibt es in dieser Richtlinie irgendeine Validierungslogik, die prüft, dass die URL irgendwo an AlternativeBrowserParameters übergeben werden muss?

  • Ein Schüler mit Interesse an Programmierung, Webentwicklung und Cybersicherheit – wirklich beeindruckend

    • Erstaunliches technisches Talent, Ausdauer sowie hervorragende Dokumentations- und Kommunikationsfähigkeiten
      Auch die Berufsethik, den Responsible-Disclosure-Prozess einzuhalten, ist großartig; wirkt wie jemand, der es noch weit bringen wird
  • Großartiger Artikel und großartige Arbeit; es fühlte sich an, als würde man gemeinsam verfolgen, wie mit jeder weiteren Entdeckung die Spannung steigt
    Die Belohnung hat er sich definitiv verdient

  • Die Verkettung der Schwachstellen ist sauber, und der Artikel ist hervorragend. Mir gefiel auch, wie der verwundbare Code in seine Funktionsweise zerlegt wurde
    Ein einfacher Trick wie „Drücken Sie F12, um es erneut zu versuchen“ bringt mich jedes Mal zum Staunen; wirklich herrlich verspielt

    • Ich wohne in Missouri, und als ich früher einmal F12 gedrückt habe, wollte mich der Gouverneur verhaften lassen
  • Das erinnert mich daran, wie ich früher mit derselben API die crosh-Shell von Chrome OS debuggt habe, um OS-Schutzmechanismen zu umgehen und auf Entwicklergeräten sogar Root-Zugriff zu bekommen. Das war CVE-2014-3172
    Allerdings musste der Autor dieses Artikels deutlich schwierigere Hürden umgehen; wirklich hervorragende Arbeit

  • Es ist zu spät in der Nacht, um tief darin einzusteigen, was bei der WebUI-Validierung kaputtging, aber ich finde es gut, dass er es bis zum Ende verfolgt und herausgefunden hat
    Es ist eine ziemlich übliche Haltung, den Toolchains der Dinge, die wir ausliefern, mit Skepsis und Misstrauen zu begegnen. Gleichzeitig vertrauen wir den magisch bequemen Entwicklungswerkzeugen großer Unternehmen wie Google oder Microsoft viel zu sehr. Letztlich will man eben seinen Code schreiben und testen, statt sich Sorgen darüber zu machen, was in Chromium oder VSCode verborgen ist

  • Einer der besten Artikel, die ich gelesen habe
    Wirklich kluge Spurensuche

  • Der Aufwand, sich durch den Browser-Code zu wühlen und bis hierher zu kommen, ist enorm; der Artikel ist außerdem sehr interessant und detailliert

  • Ein Schüler, wow, wirklich unglaublich

  • Das Chromium-Projekt hat chrome://net-internals entfernt, weil es zu komplex sei, und dann chrome://policy mit halb fertiger JSON-Bearbeitungsunterstützung hinzugefügt