- 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://policyeine 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 voninjectedScript, und wegen verzögerter Zugriffssperren beim Wechsel zu WebUI oder wegen nach einem Crash verbliebenerPage.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ürPage.reload, eine URL-Prüfung fürinspectedWindow.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://settingsoderchrome://historyimplementiert- 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://downloadseinen.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/policiesablegt, doch zum Schreiben in dieses Verzeichnis sind Root-Rechte erforderlich
- Die aktuell auf dem Gerät angewendeten Policies lassen sich in der WebUI
chrome://policyeinsehen- 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/testdas 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
PolicyTestPageEnablederforderlich - Ohne diese Policy wird
chrome://policy/testaufchrome://policyumgeleitet
Fehlende Prüfung bei setLocalTestPolicies
- Der JavaScript-Code von
chrome://policy/testsetzt Test-Policies mitsendWithPromise('setLocalTestPolicies', ...)sendWithPromise()ist ein Wrapper um die private WebUI-APIchrome.send()- Dieser Aufruf sendet eine Anfrage an eine C++-Handlerfunktion, die interne Browseraktionen ausführen kann
- Als
setLocalTestPoliciesdirekt aus der Konsole vonchrome://policyaufgerufen 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
HandleSetLocalTestPoliciesprüfte lediglich, oblocal_test_providerexistierte, aber nicht, ob die Policy-Testfunktion tatsächlich erlaubt war LocalTestPolicyProvider::CreateIfAllowed()ruftIsPolicyTestingEnabled(nullptr, channel)auf- Da das erste Argument
pref_servicenullwar, wurde die Prüfung vonPolicyTestPageEnabledübersprungen - Übrig blieb nur die Prüfung, ob der Release-Kanal
CANARYoderDEFAULTist
- Da das erste Argument
- In nicht gebrandeten Chromium-Builds wird Code unter
GOOGLE_CHROME_BRANDINGnicht kompiliert, sodass der Kanal aufUNKNOWNbleibt- Da im Enum
UNKNOWN = 0undDEFAULT = UNKNOWNgilt, 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
- Da im Enum
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
AlternativeBrowserPathundAlternativeBrowserParameterskonnte 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
BrowserSwitcherEnabledwird auftruegesetztBrowserSwitcherUrlListerhältexample.comAlternativeBrowserPathwird unter Linux auf/bin/bashgesetztAlternativeBrowserParameterswird 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
- Der ersetzte Wert
- Nachdem die Policies auf
chrome://policygesetzt waren, reichte ein Aufruf vonwindow.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://policyhä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.scriptingchrome.tabsin Manifest v2chrome.debuggerchrome.devtools.inspectedWindow
- Untersucht wurde
chrome.devtools.inspectedWindow, weil es vergleichsweise weniger abgesichert wirkte- Erweiterungen, die die API
chrome.devtoolsverwenden, müssen im Manifest ein Felddevtools_pagehaben - Wenn der Nutzer DevTools öffnet, wird diese Seite als iframe geladen, und darin kann die API
chrome.devtoolsverwendet werden
- Erweiterungen, die die API
- 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 ArgumentinjectedScriptübergeben wird - Als
inspectedWindow.reload()auf einer von WebUI geöffnetenabout:blank-Seite aufgerufen wurde, war die Ausführung von JavaScript auf einer privilegierten Seite möglichabout:blankist als URL selbst nicht besonders, übernimmt aber die Berechtigungen und die Origin der Seite, die es geöffnet hat- Eine von
chrome://settingsgeöffneteabout:blank-Seite war daher eine privilegierte Seite mit der Originchrome://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, weilchrome://policykeinabout:blank-Popup öffnet - Allerdings führte
inspectedWindow.reload()auch dann JavaScript inchrome://settingsaus, wenninspectedWindow.eval()scheiterte- Das zeigte, dass
eval()eine eigene Prüfung ähnlich einer Origin-Kontrolle hatte,reload()jedoch keine gleichwertige Prüfung
- Das zeigte, dass
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 zuchrome://policyund 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 einePage.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.reloadblieb aber als Ausnahme besteheninspectedWindow.reload()sendet intern einePage.reload-Anfrage und war daher von dieser Ausnahme betroffen- Der damalige Patch verhinderte nicht, dass
Page.reloadSkript 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 schnelleredebugger-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 aufchrome://policyausgeführt - Das Payload rief
sendWithPromise("setLocalTestPolicies", policy)auf, um Benutzer-Policies zu setzen - Es setzte
BrowserSwitcherEnabled,BrowserSwitcherUrlList,AlternativeBrowserPathundAlternativeBrowserParameters - Anschließend wurde Browser Switcher per
window.open()oder Navigation ausgelöst, um Shell-Befehle auszuführen
- Über die Schwachstelle in
- Das PoC verwendete je nach Betriebssystem Befehle zum Starten des Taschenrechners
- Windows:
C:\Windows\System32\cmd.exeundcalc.exe - Linux:
/bin/bashundxcalc - macOS:
/bin/bashundopen -na Calculator
- Windows:
- 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
- Google bestätigte die Schwachstellen nach der Meldung schnell und stufte sie als P1/S1 ein
- P1/S1 steht für hohe Priorität und hohe Schwere
- In den folgenden Wochen wurden drei zentrale Fixes umgesetzt
Page.reload-Befehl um den ParameterloaderIderweitert undloaderID-Prüfung auf Renderer-Seite- Damit ist der Befehl nur noch für eine einzelne Origin gültig und funktioniert nicht mehr, wenn er unbeabsichtigt eine privilegierte Seite erreicht
- URL-Prüfung in der Funktion
inspectedWindow.reload()- Damit verlässt man sich nicht mehr nur auf den Entzug des API-Zugriffs für Erweiterungen
- Prüfung im WebUI-Handler, ob Test-Policies aktiviert sind
- Dadurch werden Test-Policies vollständig blockiert
- Die Schwachstelle rund um die Race Condition wurde als CVE-2024-5836 vergeben
- Der CVSS-Schweregrad beträgt 8.8 High
- Die Schwachstelle rund um den Crash der untersuchten Seite wurde als CVE-2024-6778 vergeben
- Auch diese Schwachstelle erhielt einen CVSS-Schweregrad von 8.8
- Nachdem die Fixes in den Release-Branch gemergt worden waren, entschied das Chrome-VRP-Panel über die Prämie; die endgültige Auszahlung betrug $20,000
Zeitplan und Materialien
- Die Timeline sieht wie folgt aus
-
- April: Test-Policies-Bug entdeckt
-
- April: Race-Condition-Bug in
inspectedWindow.reload()entdeckt
- April: Race-Condition-Bug in
-
- Mai: Bug an Google gemeldet
-
- Mai: Von Google als P1/S1 eingestuft
-
- Mai: Bug im Zusammenhang mit dem Crash der untersuchten Seite entdeckt und Meldung aktualisiert
-
- Mai: Google bat darum, für die einzelnen Teile der Kette separate Bug-Reports einzureichen
-
- Juli: Bug-Report als fixed markiert
-
- Juli: Zur Prämienentscheidung an das Chrome-VRP-Panel weitergeleitet
-
- Juli: Das VRP-Panel setzte die Prämie auf $20,000 fest
-
- 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.reloadfunktionierte 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
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 anAlternativeBrowserParametersübergeben werden muss?Ein Schüler mit Interesse an Programmierung, Webentwicklung und Cybersicherheit – wirklich beeindruckend
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
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-3172Allerdings 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-internalsentfernt, weil es zu komplex sei, und dannchrome://policymit halb fertiger JSON-Bearbeitungsunterstützung hinzugefügt