Zurück zu allen Beiträgen
Veröffentlicht am · von Renaud Deraison

Eine Sitzung, hundert Repositories

Mandiants Bericht AI Risk and Resilience 2026 beschreibt einen Angreifer, der bei einem SaaS-Anbieter eine laufende Sitzung eines KI-Coding-Assistenten übernahm, darüber ein vergiftetes PyPI-Paket installieren ließ, GitHub-OAuth-Tokens stahl und den Shai-Hulud-Wurm über rund 100 interne Repositories verteilte. Der Bericht sagt nicht, wie die Sitzung übernommen wurde. In einem Bromure-Agentic-Coding-Workspace muss er das auch nicht: Das Token in dieser Sitzung ist ein `ghp_`-Köder, der Versuch, es hinauszuschicken, pausiert die VM, und die Pushes, die aus einer Maschine hundert Repositories machen, enden am Proxy des Hosts.

Jemand übernahm eine Coding-Sitzung, die lief, authentifiziert und vertrauenswürdig, auf der eigenen Maschine eines Entwicklers. Alles, was danach kam, bis hin zu hundert Repositories, stammte aus dem, was diese Maschine in diesem Moment bei sich trug.

Ein Entwickler bei einem Software-as-a-Service-Unternehmen fragte seinen Coding-Assistenten, welches Paket er für ein ganz gewöhnliches Problem nehmen solle. Der Assistent nannte eines. Der Entwickler sagte ja, was genau der Sinn eines Assistenten ist, und pip install erledigte den Rest.

Ein Angreifer hatte die Sitzung da längst, und er war es, der diese Empfehlung tippte.

Mandiant beschreibt den Fall im AI Risk and Resilience Report 2026, in diesem Monat veröffentlicht von der Incident-Response-Sparte von Google Cloud; The Hacker News berichtete am 16. September darüber. Der Angreifer kaperte eine aktive Coding-Assistenten-Sitzung auf der Workstation eines Entwicklers. Fünf weitere Schritte brachten ihn zu rund hundert internen Code-Repositories und zum Paket-Namensraum des Unternehmens, wo ein zweiter Mitarbeiter die vergiftete Version zog.

Die Kette, Schritt für Schritt

Eine Workstation, sechs Schrittewas der Schritt ausgibt1ein Angreifer übernimmt eine laufende Assistenten-SitzungMethode in der öffentlichen Fallstudie nicht genanntdie Sitzung2der Assistent empfiehlt vergiftete Softwareder Entwickler nimmt sie an, wie vorgesehendas Ja des Entwicklers3ein kompromittiertes PyPI-Paket installiert einen Infostealergewöhnlicher Abhängigkeitsverkehr, gewöhnliche InstallationRegistry-Vertrauen4GitHub-OAuth-Tokens werden von der Workstation gelesender Dreh- und Angelpunkt: alles Weitere braucht dasgespeicherte Tokens5Shai-Hulud verbreitet sich auf rund 100 interne RepositoriesRepository-Secrets und Produkt-Quellcode mitgenommenSchreibzugriff6ein Paket im eigenen Namensraum der Firma wird vergiftetein zweiter Mitarbeiter zieht diese Version und infiziert sichVeröffentlichungsrechte
Die sechs Schritte, die Mandiant beschreibt, und die Berechtigung, die jeder davon ausgibt. Für Schritt 1 nennt Mandiant keine Methode. In jedem Schritt danach nutzt der Angreifer eine Berechtigung, die die Maschine des Entwicklers ohnehin schon bei sich trug: das Vertrauen der Sitzung selbst, dann das des Paketmanagers, dann das von GitHub, dann die Veröffentlichungsrechte des Unternehmens.

Mandiant hält genau den Teil zurück, den man am dringendsten wissen möchte. Die öffentliche Fallstudie sagt nicht, wann der Einbruch stattfand, und sie sagt nicht, wie der Angreifer die Kontrolle über eine laufende Sitzung bekam. Gestohlene Browser-Cookies, eine bösartige Erweiterung, eine kompromittierte CLI auf einer gemeinsam genutzten Maschine: Der Bericht entscheidet sich für keines davon. Wer ihn liest, um den Patch zu finden, geht ohne einen wieder.

Die Schritte 2 bis 6 enthalten überhaupt keinen Exploit. Eine Paketinstallation funktioniert so, wie Paketinstallationen funktionieren, und Code, der als der Entwickler läuft, liest die Tokens, die auf der Platte des Entwicklers liegen. Ein git push mit gültigem Token pusht, und eine Veröffentlichung mit gültigen Veröffentlichungsrechten veröffentlicht. Ein Schritt war ein Einbruch; die anderen fünf waren Ausgeben.

Der Multiplikator sind Zugangsdaten, und er wächst weiter

Shai-Hulud ist der selbstreplizierende Supply-Chain-Wurm, der seit 2025 Maintainer-Konten frisst, und er taucht in einer Geschichte wie dieser als Payload auf, weil Schritt 5 genau das ist, wofür seine Autoren ihn gebaut haben. GitGuardian hat im August eine neuere Variante auseinandergenommen und fand sie beim Absuchen von 469 Fundorten für Zugangsdaten, gegenüber 189 in früheren Builds: Entwicklungsumgebungen, CI/CD-Werkzeuge, Cloud-Konfiguration, Konfigurationen von KI-Werkzeugen, Paketmanager-Konfigurationsdateien, Shell-History, .env-Dateien, IDE-Einstellungen, CLI-Caches. Der Wurm liest einen Aktenschrank, statt nach einer Lücke zu jagen.

GitGuardians Analyse fasst die Kategorie in einem Satz zusammen:

„Angreifer haben aufgehört zu versuchen, Vertrauensbeziehungen zu brechen, und begonnen, die Zugangsdaten zu benutzen, die diese Beziehungen ohnehin funktionieren lassen.“

Aus einer Workstation wurden hundert Repositories, weil sie Berechtigungen im Wert von hundert Repositories bei sich trug. Diese Zahl kam aus einem Inventar, und Sie entscheiden, was in das Inventar hineinkommt.

Die Sitzung trug Köder bei sich

Bromure Agentic Coding führt den Agenten in einer virtuellen Linux-Maschine auf Ihrem Mac aus, und diese Maschine trägt keine echten Geheimnisse. Sie schalten dafür nichts ein. So speichert ein Workspace Zugangsdaten, von vornherein.

Ihr echtes GitHub-Token bleibt verschlüsselt auf dem Host. Die VM bekommt eine strukturerhaltende Fälschung: ghp_ gefolgt von 36 Zeichen, 40 insgesamt, sodass die Präfix- und Längenprüfungen von gh selbst anstandslos bestehen. Bromure exportiert sie als GH_TOKEN und schreibt sie in ~/.git-credentials und in die gh-Konfiguration, und sie sind die einzigen GitHub-Zugangsdaten in dieser Maschine überhaupt. Ein Proxy auf dem Host setzt den echten Wert erst auf der Leitung ein, nachdem die Anfrage die VM verlassen hat, und nur, wenn das Ziel zu dem Host passt, für den die Zugangsdaten geprägt wurden. Jede Fälschung ist deterministisch, aus dem echten Wert plus einem installationseigenen 32-Byte-Salt über HKDF-SHA256 abgeleitet, sodass ein Client, der den Fingerabdruck seines eigenen Schlüssels bildet, zwischen Sitzungen keine scheinbare Rotation sieht.

Lassen Sie Schritt 4 dagegen laufen. Der Infostealer kommt an, durchsucht die 469 Fundorte, findet die Umgebungsvariable, findet ~/.git-credentials, findet die gh-Konfiguration und verschickt, was er gefunden hat. Jeder Lesevorgang gelingt, und jede Datei liegt da, wo der Wurm sie erwartet hat. Der Angreifer endet mit einer vierzig Zeichen langen Zeichenkette, die sich nirgends authentifiziert, außer sie läuft noch einmal durch genau einen bestimmten Mac.

Die Schritte 5 und 6 brauchen beide, dass Schritt 4 ein funktionierendes Token hervorgebracht hat, und Schritt 4 hat einen Köder hervorgebracht.

Der Diebstahl ist zugleich der Alarm

Ein gefälschtes Token hat genau ein legitimes Ziel. Keine harmlose Anfrage trägt einen für github.com geprägten ghp_-Köder zu irgendeinem anderen Host, also durchsucht der Proxy jede ausgehende Anfrage, Header und Body, nach jeder Fälschung, die ihren eigenen Geltungsbereich verlässt. Er lässt einen Aho-Corasick-Automaten laufen, billig genug, um ihn auf den gesamten Verkehr zu richten.

Wenn der Proxy eine findet, tut er mehr, als sie zu protokollieren:

  • er weist die Anfrage mit HTTP 451 ab, und kein Byte davon erreicht das Ziel;
  • Bromure pausiert die VM auf der Stelle;
  • eine Warnung bietet Ihnen Herunterfahren, Für die Untersuchung sichern (Platte, Home und geteilte Ordner zuerst für die Forensik exportieren) oder Fortfahren auf eigenes Risiko;
  • die Security Timeline bekommt eine rote Zeile Credential brokering;
  • Bromure markiert den Workspace als kompromittiert, sodass der nächste Start das Festplatten-Image und das persistente Home löscht. Ihre Tokens, SSH-Schlüssel und Workspace-Einstellungen überleben dieses Löschen.

Sie schalten davon nichts ein; der Detektor läuft immer. Der Exfiltrationsversuch in Schritt 4 ist das, was die Sitzung stoppt, und so endet der Vorfall mit einer pausierten VM und einem Timeline-Eintrag statt mit hundert Repositories und einer Fallstudie.

Hundert Repositories sind hundert Pushes

Angenommen, Sie wollen Gürtel und Hosenträger. Schritt 5 ist ein Wurm, der in Repositories schreibt, und der Host klassifiziert Schreibvorgänge.

Die Leitplanken eines Workspace tragen eine Schreibrichtlinie pro Dienst. Für GitHub liest diese Richtlinie git-over-HTTPS ebenso wie die REST-API: Ein git push kommt als git-receive-pack an und zählt als Schreibvorgang, während ein git fetch als git-upload-pack ankommt und als Lesevorgang zählt. Nur Lesen weist die Pushes ab. Vor dem Schreiben fragen, die Vorgabe für neue Workspaces, hält jeden einzelnen für einen Dialog auf Host-Seite an, der die Operation benennt. Fetches gehen unter beiden durch, der Agent arbeitet also weiter.

Der Proxy trifft diese Entscheidung auf dem Host, außerhalb der VM. Ein Agent, dessen Sitzung jemand anderem gehört, kann die Richtlinie weder abschalten noch umgehen, weil die Richtlinie nicht auf der Seite des Agenten läuft. Ein Wurm, der hundert Repositories will, sammelt hundert Ablehnungen ein, oder fragt Sie hundertmal.

Und das Paket musste immer noch ankommen

Schritt 3 ist die Stelle, an der Mandiant konkrete Ratschläge gibt: von der KI empfohlene Drittabhängigkeiten gegen kryptografische Prüfsummen und Allowlists validieren und den Abhängigkeitsverkehr über Repositories leiten, die Sie kontrollieren.

In einem Workspace läuft jeder Paketabruf ohnehin schon über den Host, denn der Proxy ist der einzige Weg der VM ins Netz. Bromure fängt PyPI unter pypi.org und files.pythonhosted.org ab und wendet dort die Richtlinie des Hosts an. Die pip.conf in der VM kann verschärfen, was der Proxy geliefert hat; lockern kann sie es nicht. Drei Schichten sprechen für Python-Pakete:

  • die Altersschranke, standardmäßig an mit einem Minimum von zwei Tagen, die Versionen abweist, die jünger sind als die Grenze, nach der Überlegung, dass ein frisch veröffentlichtes Release dasjenige ist, das am wahrscheinlichsten gerade gekapert wurde. PEP-503-Indizes tragen keine Zeitstempel, also schlägt Bromure den Veröffentlichungszeitpunkt bei Bedarf nach;
  • die OSV-Prüfung gegen api.osv.dev, kostenlos und ohne Schlüssel, die jede Version blockiert, zu der eine Sicherheitsmeldung ab der von Ihnen gewählten Schwere vorliegt;
  • socket.devs Filter für kompromittierte Pakete, der bei Malware, bekannter Malware, Typosquatting und bösartigen Installationsskripten anschlägt.

Eine Blockade kommt als HTTP 451 zurück, dessen Body mit Bromure Supply-Chain Security blocked this request: beginnt, gefolgt vom Grund, und pip gibt das wörtlich aus. Sie sehen, warum die Installation fehlgeschlagen ist, und der Agent sieht es auch, der oft von selbst eine ältere Version festnageln kann. Diese ältere Version ist genau die, auf die ihn die Altersschranke zusteuern wollte.

Eine gewöhnliche WorkstationSchritt 3: das Paket installiert sichdie Registry liefert die vergiftete Versionnichts auf dem Weg beurteilt die HerkunftSchritt 4: die Tokens werden gelesenechte OAuth-Tokens: Env, Config, Historydie Exfiltration liefert gültige ZugangsdatenSchritt 5: der Wurm pushtein gültiges Token pusht, also pusht esrund 100 Repositories, plus der NamensraumIn einem Bromure-WorkspaceSchritt 3: der Abruf wird zuerst geprüftAlter, OSV, socket.dev, auf dem Host451, bevor ein Byte die VM erreichtSchritt 4: dasselbe Lesen gelingtalle 469 Fundorte enthalten ghp_-Ködereinen hinausschicken: 451, und die VM pausiertSchritt 5: der Push wird eingestuftgit-receive-pack abgewiesen oder abgefragtgit fetch geht durch; die Arbeit läuft weiter
Dieselben drei tragenden Schritte, auf zwei Maschinen. Links eine gewöhnliche Workstation: echte Tokens auf der Platte, ein Diebstahl, der funktioniert, und Pushes, die durchgehen. Rechts ein Bromure-Agentic-Coding-Workspace: Der Host weist die vergiftete Version ab, bevor sie die VM erreicht, derselbe Diebstahl liefert einen Köder und pausiert die Maschine, und der Host klassifiziert die Pushes, wo eine gekaperte Sitzung keine Stimme hat.

Mandiants drei Kontrollen, und wo sie wohnen

Der Bericht schließt die Fallstudie mit drei Empfehlungen ab. Jede benennt einen Ort, an dem ein Bromure-Workspace die Grenze bereits zieht.

Von der KI empfohlene Abhängigkeiten validieren

Mandiant verlangt Prüfsummen und Allowlists für alles, was der Assistent vorschlägt. Ihr Workspace wendet seine Richtlinie auf die Antwort der Registry an, bevor die VM sie sieht. Der Proxy streicht zu frische Versionen aus der Versionsliste, sodass sie aus Sicht des Agenten noch gar nicht existieren, und er weist eine festgenagelte, zu frische Version ab und zitiert das echte Alter des Pakets im Fehler. Die Schlüssel für die Reputationsdienste bleiben auf dem Host, außerhalb der Maschine, die die Installation ausführt.

Rohe Schlüssel und langlebige Tokens fernhalten

Das ist die Grenze auf der Leitung, als Ratschlag formuliert. Keine Datei, keine Umgebungsvariable und kein Prozess in der VM trägt einen echten API-Schlüssel, ein echtes OAuth-Token, ein echtes AWS-Secret oder einen echten privaten SSH-Schlüssel. Bromure hält die OAuth-Tokens des Abos verschlüsselt auf dem Host und erneuert sie dort etwa fünf Minuten vor dem Ablauf, sodass der Gast kein Refresh-Token bei sich trägt. Der Kanal zwischen beiden läuft in eine Richtung: Der Host schreibt Fälschungen hinein, und die VM hat keinen Aufruf, mit dem sie ein echtes Token zurückverlangen könnte.

Abhängigkeitsverkehr über etwas leiten, das Sie kontrollieren

Der Proxy ist der einzige Weg der VM nach draußen. Jede HTTPS-Anfrage reist über einen virtuellen Socket zum Host, der das TLS terminiert, die Anfrage inspiziert und sie über den eigenen TLS-Stack von macOS neu absendet. Genau dieser eine Pfad macht die anderen beiden Kontrollen durchsetzbar statt nur empfehlend. Kein Paketabruf kann die Prüfungen überspringen, und keine Zugangsdaten können ungetauscht hinaus.

Und den Explosionsradius absichtlich klein halten

Hundert Repositories fielen, weil eine Maschine Berechtigungen für hundert Repositories trug. Schneiden Sie Workspaces einen pro Projekt zu, oder einen pro Zugangsdaten-Grenze. Jeder bildet auf genau eine VM ab, keine zwei teilen sich eine, und jeder trägt die Zugangsdaten, die die Arbeit in diesem Workspace braucht. Das Inventar einer einzelnen Maschine stellen Sie in einem Einstellungsbereich ein.

Der Teil, den man nicht patchen kann

Mandiant fasst die Kategorie in einen Satz, der eher die Form als den Vorfall beschreibt: „eine vergiftete Datenquelle, eine Modell-Abhängigkeit oder ein Erweiterungs-Hook kann einen vertrauenswürdigen Agenten in einen Kanal für interne Aufklärung, laterale Bewegung oder autonomen Ausbruch aus einer Sandbox verwandeln.“

Drei verschiedene Einstiegspunkte, ein Satz, und er gilt für alle drei. Der Einstiegspunkt bestimmt, wie ein Angreifer ankommt; der Inhalt der Maschine eines Entwicklers bestimmt, wie weit er kommt. Dieser Angreifer kam über eine gekaperte Sitzung herein. Der nächste kommt über etwas anderes herein, und der Bericht nennt Ihnen keine Tür, die Sie abschließen könnten.

Bauen Sie für den Schritt nach dem Einstieg. Nehmen Sie an, die Sitzung gehört bereits jemand anderem, was die Annahme ist, zu der die Fallstudie Sie zwingt, indem sie Schritt 1 nie erklärt, und fragen Sie dann, was diese Sitzung erreichen kann.

Eine Version dieses Vorfalls endet bei Schritt 3, mit einer 451 im Terminal und einer Zeile im Security Log. Eine andere endet bei Schritt 4, mit einer pausierten VM und einer roten Zeile in der Timeline, die festhält, dass Zugangsdaten hinauswollten. Keine der beiden Versionen erreicht Schritt 5, und bei Schritt 5 lagen die hundert Repositories.

Setzen Sie den Agenten auf eine Maschine, die Köder bei sich trägt. Installieren Sie Bromure Agentic Coding, und lassen Sie jemanden die Sitzung nehmen.