1-Klick-Diebstahl von GitHub-Tokens über einen VSCode-Bug
(blog.ammaraskar.com)- github.dev verwendet ein von github.com übergebenes OAuth-Token, um im browserbasierten VSCode Dateien anzuzeigen, PRs zu erstellen und Commits auszuführen. Da dieses Token nicht auf ein bestimmtes Repository beschränkt ist, kann es alle Repositories lesen und beschreiben, auf die der Nutzer Zugriff hat.
- VSCode-Webviews werden per
vscode-webview://...-iframeisoliert. Für die UX von Tastaturkürzeln leitet VSCode jedochkeydown-Ereignisse aus dem Webview alsdid-keydown-Nachrichten an das Hauptfenster weiter, wodurch nicht vertrauenswürdige Skripte Ereignisse senden können, als kämen sie von echten Nutzereingaben. - Beliebige Texteingabe funktioniert wegen des HTML-
<input>nicht, aber durch die Kombination aus dem StandardkürzelCtrl+Shift+A, einer Benachrichtigung zur Installation empfohlener Erweiterungen, local workspace extensions und benutzerdefinierten Keybindings lässt sich der Befehl zur Erweiterungsinstallation ausführen. - Das PoC führt JavaScript in einer Markdown-Zelle eines Jupyter notebook aus, akzeptiert die Benachrichtigung zur Installation einer empfohlenen Erweiterung, installiert anschließend über ein neues Keybinding eine ausgewählte Erweiterung und zeigt dann das GitHub-API-Token sowie eine Liste privater Repositories an.
- Desktop-VSCode ist von derselben Schwachstelle betroffen, allerdings muss ein Angreifer dort das Klonen eines Repositories und das Öffnen des Notebooks herbeiführen. Nutzer von github.dev können sich schützen, indem sie die Websitedaten löschen, sodass der anfängliche Bestätigungsdialog erneut erscheint.
Überblick über die Schwachstelle
- github.dev öffnet ein leichtgewichtiges VSCode, das im Browser läuft, wenn man bei einer zugänglichen GitHub-Repository-URL
github.comingithub.devändert oder einen Menüeintrag anklickt. - Dieses browserbasierte VSCode kann Repository-Dateien anzeigen, auch private Repositories öffnen sowie PRs absenden und Commits erzeugen.
- github.com übermittelt per POST ein OAuth-Token an github.dev, mit dem dieses im Namen des Nutzers mit GitHub interagieren kann. Das Token ist nicht auf das konkrete Repository beschränkt, mit dem der Nutzer gerade interagiert hat.
- Ein Angreifer kann so allein durch einen Linkklick ein GitHub-Token mit Lese- und Schreibrechten stehlen, einschließlich des Zugriffs auf private Repositories.
Problem bei Webview-Isolierung und Weiterleitung von Tastatureingaben
- VSCode-Webviews isolieren die JavaScript-Ausführung mithilfe eines
<iframe>mit anderer Origin als das Hauptfenster von VSCode. - Die Ausgabe eines Jupyter-Notebooks wird in einem
<iframe>mit der Originvscode-webview://...gerendert, während das Hauptfenster von Electron die Originvscode-file://...verwendet. - Durch diese Isolierung kann ein Notebook trotz HTML-Darstellung oder JavaScript-basierten interaktiven Widgets innerhalb des
iframeweder die Node.js-APIs von Electron noch die VSCode-API aufrufen. - Funktionen, bei denen Hauptfenster und Webview zusammenarbeiten müssen, wie die Markdown-Vorschau, tauschen Nachrichten über die Window.postMessage() API aus.
- Damit Tastenkürzel wie
Ctrl+Shift+Pauch funktionieren, wenn der Fokus im Webview liegt, leitet VSCodedid-keydown-Ereignisse an das Hauptfenster weiter. - Nicht vertrauenswürdige Skripte im Webview können selbst
keydown-Ereignisse auslösen und sich so als echte Tastatureingaben des Nutzers ausgeben.
Angriffskette
- Mit
Ctrl+Shift+Plässt sich zwar die Command Palette öffnen, aber das Einschleusen beliebiger Zeichenketten funktioniert nicht, weil die Command Palette ein HTML-<input>verwendet. - Eingaben wie Pfeiltasten und
Enter, die perkeydownverarbeitet werden, lassen sich jedoch nutzen, ebenso die Standard-Tastenkürzel von VSCode. Ctrl+Shift+Aist das Standard-Keybinding für „Notifications: Accept Notification Primary Action“ und betätigt die primäre Schaltfläche der letzten VSCode-Benachrichtigung.- Wenn in
.vscode/extensions.jsonempfohlene Erweiterungen hinterlegt sind, zeigt VSCode eine Installationsbenachrichtigung an. Das Publisher-Trust-System von VSCode 1.97 blendet bei neuen Publishern jedoch zusätzlich einen separaten Vertrauensdialog ein. - Zwar kann man mit
Tabzwischen Schaltflächen wechseln, aber die Verarbeitung vonEnterfür die Schaltfläche „Trust Publisher & Install“ ist an daskeydownder Schaltfläche selbst gebunden, wodurch sich die Installation auf diesem Weg nur schwer abschließen lässt. - Local workspace extensions erlauben die direkte Installation von Erweiterungen aus
.vscode/extensionsinnerhalb eines vertrauenswürdigen Workspace; github.dev bzw. Web-Workspaces gelten immer als vertrauenswürdig. - Beim direkten Ausführen einer lokalen Workspace-Erweiterung tritt jedoch ein CSP-Fehler auf, weil der Extension Worker Erweiterungen erwartet, die von
vscode-cdn.netstammen. - Stattdessen lässt sich in der
package.jsoneiner lokalen Workspace-Erweiterung ein benutzerdefiniertes Keybinding ergänzen, dasworkbench.extensions.installExtensionmit dem KontextskipPublisherTrustaufruft.
Funktionsweise des PoC und Auswirkungen
- Erforderlich ist ein Repository mit einem Jupyter notebook und einer local workspace extension.
- Die Markdown-Zelle des Notebooks kann über das
onerror-Attribut eines Bildes JavaScript ausführen. - Die Payload wartet, bis VSCode die Benachrichtigung zur Installation einer empfohlenen Erweiterung anzeigt, und sendet dann ein
Ctrl+Shift+A-Ereignis, um die primäre Aktion der Benachrichtigung zu bestätigen. - Danach wartet sie, bis die Erweiterung installiert und aktiviert ist und das benutzerdefinierte Keybinding verfügbar wird, und löst dann per
Ctrl+F1die Installation der ausgewählten Erweiterung aus. - Die im PoC installierte Erweiterung holt das GitHub-API-Token, fragt
https://api.github.com/user/reposab, um die zugänglichen privaten Repositories zu erhalten, und gibt anschließend Token und Repository-Liste in einer Infobox aus. - Nach Ausführung des PoC sollten die Daten von github.dev gelöscht oder die PoC-Erweiterung entfernt werden; andernfalls bleibt sie auf allen github.dev-Seiten aktiv.
- Dieselbe Schwachstelle existiert auch in Desktop-VSCode, allerdings muss der Angreifer das Opfer dazu bringen, das Repository zu klonen und ein Notebook mit der Webview-Skript-Payload zu öffnen.
- Falls das vom Opfer geöffnete Webview eine weitere XSS enthält, kann dies auf dem Desktop praktisch zu vollständiger Remote Code Execution führen.
Schutz- und Abschwächungsfaktoren
- Wenn jemand github.dev noch nie zuvor genutzt hat, erscheint beim ersten Aufruf ein Dialog, der angeklickt werden muss und dadurch eine Gelegenheit bietet, die Angriffsseite zu verlassen.
- Wenn man Cookies und lokale Websitedaten von github.dev löscht, kann dieser anfängliche Dialog erneut erscheinen.
- In Chrome kann man über das Symbol in der URL-Leiste zu Cookies and site data > Manage on-device site data wechseln und dort die Daten der betreffenden Domain löschen.
- Wenn der github.dev-Dialog bereits bestätigt wurde und der lokale Speicher des Browsers seitdem nicht gelöscht wurde, fehlt github.dev ein Schutz wie ein CSRF-Token, sodass praktisch jeder Link im Internet auf einen Angriff umleiten kann.
- VSCode verlässt sich nicht nur auf
iframe-Isolierung, sondern verwendet zusätzlich eine strikte Content Security Policy sowie DOMPurify. - Da in der Markdown-Vorschau auf der Erweiterungsseite
script-src 'none'verwendet wird, wird die Ausführung beliebigen JavaScripts verhindert. Dadurch wird die größere Auswirkung blockiert, dass ein bloßer Erweiterungslink auf dem Desktop zu 1-Klick-RCE führen könnte.
Hintergrund der Offenlegung und Zeitplan
- Das MSRC hatte frühere VSCode-Bugmeldungen stillschweigend behoben, ohne Credits zu vergeben, und sie als ohne Sicherheitsauswirkung markiert.
- Auch ein aktueller Bericht von Starlabs über einen VSCode-XSS-Bug wurde als nicht preiswürdig und mit niedriger Schwere eingestuft.
- Das VSCode-Team brauchte möglicherweise mehr Zeit, um das Gleichgewicht zwischen UI/UX und Sicherheit zu finden, doch eine vollständige Offenlegung wurde gewählt, weil Zeit und Aufwand von Sicherheitsforschern nicht als selbstverständlich behandelt werden sollten.
- Eine Stunde vor der Veröffentlichung am 2. Juni 2026 wurde ein bestehender Kontakt auf GitHubs Sicherheitsseite über die geplante Offenlegung informiert.
- Noch am selben Tag wurde die Schwachstelle veröffentlicht und auch im VSCode-Issue-Tracker registriert.
1 Kommentare
Hacker-News-Kommentare
Eine gute Zusammenfassung; im Großen und Ganzen ist schon die Tatsache bedauerlich, dass der im Web eingebettete VSCode-Editor bei GitHub angemeldet ist.
Unabhängig davon, ob es Defense in Depth gibt, entsteht allein durch diese Ursünde eine große Angriffsfläche. Das ist ähnlich, als würde man ein GitHub-API-Token mit allen Rechten im Klartext auf der Workstation liegen lassen, damit ein bösartiges NPM-Paket es finden kann.
Idealerweise sollte eine Browser-IDE mit temporären, repository-spezifisch begrenzten Berechtigungen oder Tokens laufen, die nur Pull/Push für das jeweilige Repository erlauben, und überhaupt keine github.com-Websession haben. Wenn man die komplette GitHub-Web-UI braucht, geht man zurück zu github.com; github.dev sollte eher ein Dienst für ein einzelnes Repository sein.
Allerdings ist das für Nutzer unbequem, schwer umzusetzen und wahrscheinlich im github.dev-Tooling historisch tief verankert.
github.dev sollte diesen Ansatz ernsthaft prüfen.
[1] https://orca.security/resources/blog/hacking-github-codespac...
Noch schlimmer ist, dass selbst Entwickler das anscheinend kaum interessiert.
keydown-Handler weitergereicht wird.Auf dem Desktop wäre es besser, wenn Electron das direkt abfängt und diese Funktion entfernt wird; im Web sollte sie standardmäßig deaktiviert sein.
Ich weiß auch nicht genau, ob andere Git-Hoster ähnliche Funktionen haben.
Ehrlich gesagt sollten LLM-Agenten auch so arbeiten. Ein LLM direkt pushen zu lassen, wirkt leichtsinnig.
Dieser Angriff ist besonders heikel, weil VSCode-Erweiterungen mit demselben Vertrauensniveau wie der Editor selbst laufen und die meisten Entwickler dutzende Erweiterungen installiert haben, deren Berechtigungen sie nie geprüft haben.
Wenn eine bösartige oder kompromittierte Erweiterung still GitHub-Tokens exfiltriert, ist das ohne Netzwerküberwachung schwer zu erkennen; das ist ein gutes Argument dafür, Erweiterungen in isolierten Profilen auszuführen.
Der beste Weg ist, GitHub zu verlassen, auf selbst gehostetes internes GitLab/Forgejo zu wechseln und GitHub komplett zu blockieren.
Ich habe vor Kurzem etwas Ähnliches erlebt. Mein GitHub-Token und mein Cloudflare-Token wurden gestohlen.
Selbst wenn man Sicherheit ernst nimmt, wird man irgendwann getroffen, wenn genug Zeit vergeht. Das Beste ist Trennung und Kontrolle des Schadensradius.
Vertraue niemandem und nichts, nutze OrbStack und arbeite immer unter der Annahme, dass Tokens irgendwann leaken werden.
Mein Workflow wurde komplett unterbrochen, aber glücklicherweise schienen die Leute, die die Tokens mitgenommen haben, eher Spam-Bots zu sein. Sie haben massenhaft gefälschte Spam-Seiten erstellt und versucht, Kryptowährungen zu minen.
Das stärkste Gefühl, das bleibt, ist das Empfinden, verletzt worden zu sein. Passt alle auf euch auf.
Der Teil, dass es eine schreckliche Erfahrung gewesen sei, den VSCode-Bug an MSRC zu melden und dann stillschweigend gefixt zu werden, ist sehr typisch für MSRC. Offenbar haben sie erkannt, dass Forscher ohnehin kostenlos melden, also sehen sie keinen Grund, etwas zu ändern.
Ich kenne die Details dieses Falls nicht, aber ich habe früher Bug-Bounty-Programme über Bountysource und HackerOne betrieben. Manchmal landet ein Bericht schon beim Entwicklungsteam, bevor das Security-Team ihn vollständig bewertet hat.
An diesem Punkt kann ein Entwickler ihn stillschweigend beheben. Manchmal aus Sorge — rational oder nicht —, dass ein sicherheitsrelevanter Bug schlecht auf ihn zurückfällt oder Beförderungschancen beeinträchtigt. Wenn das passiert, ist die Schwachstelle oft schon verschwunden, wenn das Security-Team sie reproduzieren will.
Aus Sicht von MSRC sieht es dann nur so aus, als würden die gelieferten Reproduktionsschritte nicht mehr funktionieren. Interne Bug-Historie oder ob jemand bereits gepatcht hat, sehen sie nicht. Deshalb wird der Bericht als ungültig geschlossen, selbst wenn der ursprüngliche Fund berechtigt war.
Danke dafür, dass du die Zeit für diesen Exploit quasi gespendet hast und damit auf die nötigen Verbesserungen bei der Sicherheitsreaktion von VS Code aufmerksam machst. Du hättest einfach aufgeben können, hilfst aber weiterhin.
Ich verstehe nicht so recht, warum nicht mehr Entwickler Neovim ausprobieren.
Vielleicht ist es Geschmackssache, aber ich mag eine kleine Konfiguration, bei der man nachvollziehen kann, was installiert ist und was läuft. Wenn VSCode, Browser-IDE, Erweiterungen, Sync, Tokens und beliebige Plugins zusammenkommen, wird es schwer zu erkennen, was worauf Zugriff hat.
Das war eine Funktion der offiziellen Python-Erweiterung von Microsoft, und in anderer Hinsicht war sie noch die einzig halbwegs brauchbare Erweiterung, aber sie installierte Typdefinitionen für andere Bibliotheksversionen als die, die mein Projekt verwendete. Dass dabei völlig selbstverständlich ungeprüfter Third-Party-Code ausgeführt wurde, wirkte auf mich sehr beunruhigend, und es sah nicht so aus, als könnte man das per Einstellung abschalten.
Ich würde gern sagen, dass ich „seitdem nie zurückgeblickt habe“, aber ehrlich gesagt hat Neovim in den letzten ein bis zwei Jahren angefangen, meine Konfiguration bei fast jedem Upgrade regelmäßig kaputtzumachen. Es gab schon länger Anzeichen, dass das irgendwann passieren könnte. Technisch gesehen sind zwar 10 Jahre vergangen, aber nvim hat noch immer keine erste stabile Version veröffentlicht, daher kann man ihm die Instabilität nicht wirklich vorwerfen, aber es ist etwas, das man im Hinterkopf behalten sollte.
Ich überlege, wieder zum unveränderten Vim zurückzukehren. Ich würde viele Komfortfunktionen verlieren, aber ich hätte gern weniger Fälle, in denen ich während der Arbeit kaputte Features debuggen muss.
Man muss nicht erst einen Haufen Plugins installieren oder so etwas wie SpaceVim verwenden. Vielleicht lohnt es sich, mal reinzuschauen.
Es dauert, bis man sich an nvim gewöhnt, aber wenn man es einmal ist, ist es schneller. Trotzdem erklärt das, warum viele Leute in ihrer Komfortzone bleiben.
Die öffentliche Offenlegung war die richtige Entscheidung. Es gibt viel zu viele Leute, die mit dem MSRC unzufrieden sind, und wie bei der Nightmare-Eclipse-Situation beginnt das Ganze jetzt überzukochen.
Wenn sich solche Veröffentlichungen häufen, schaut das MSRC vielleicht irgendwann in den Spiegel und erkennt, dass es selbst das Problem ist. Wahrscheinlich ist das nicht, aber hoffen kann man ja.
Trotzdem finde ich, man hätte es wenigstens versuchen oder mehrere Tage vor der Veröffentlichung Bescheid geben sollen. Man weiß nie, was daraus geworden wäre.
Der Artikel war sehr gut, aber beim letzten Teil war ich etwas verwirrt. Ich möchte nur prüfen, ob ich es richtig verstanden habe.
Der Autor sagte, dass man wegen des neuen Vertrauenssystems für Publisher nicht allein mit dem Shortcut-Trick direkt eine bösartige Erweiterung installieren könne und dass man das mit einer lokalen Workspace-Erweiterung ohne Publisher-Prüfung umgehen könne, dies aber durch die CSP blockiert werde.
Die Lösung scheint darin zu bestehen, eine lokale Workspace-Erweiterung zu installieren, die den Shortcut „Erweiterung ohne Publisher-Verifizierung installieren“ bindet.
Daher frage ich mich: 1) Bedeutet das, dass zwei Erweiterungen nötig sind? Die erste ist eine lokale Erweiterung, die nur das Keybinding setzt, und die zweite ist die eigentliche bösartige Erweiterung, die wegen der CSP nicht lokal sein muss und in der Praxis auch nicht lokal sein kann? Und 2) blockiert die CSP nur das JS lokaler Erweiterungen, aber nicht
package.jsonoder die Möglichkeit, Shortcuts hinzuzufügen?Man könnte für die direkteste Ausführung
my-extension/extension.jseinfügen, aber das wird von der CSP blockiert. Allerdings blockiert diescript-src-CSP nur Skripte, das Laden vonpackage.jsonist also erlaubt. Daher wird genau das genutzt, um ein Keybinding beizusteuern.Die MSRC-Situation ist wirklich kaum zu glauben.
Es gibt vielleicht bessere Materialien, aber ich finde dieses Video von The Primeagen ist ein guter Einstieg:
https://www.youtube.com/watch?v=9kxx5xp5nTQ
Zu der Stelle „Die einzige Möglichkeit, dieses Verhalten zu erlauben, besteht darin, dass zwei Webseiten unterschiedlicher Herkunft über die API
Window.postMessage()zusammenarbeiten“ habe ich einen kleinen nitpick.Kommunikation wäre auch möglich, indem ein iframe oder das übergeordnete Fenster die Eigenschaft
location.anchorverändert.