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

Das Repo war wirklich Microsofts

Am 5.–6. Juni 2026 schob der Miasma-Wurm credential-stehlenden Code in 73 Repositories über vier von Microsofts eigenen GitHub-Organisationen — Azure, Azure-Samples, microsoft, MicrosoftDocs — darunter Azure/functions-action, die offizielle Deploy-Action, und durabletask, ein Repo, das im Mai bereits einmal bereinigt worden war. Diesmal wartete die Nutzlast nicht auf npm install. Sie feuerte in dem Moment, in dem ein Entwickler das Repository in Claude Code, Cursor, Gemini CLI oder VS Code öffnete. Hier ist, warum das Vertrauenssignal — „es ist ein Microsoft-Repo“ — erneut die Angriffsfläche war, und was sich ändert, wenn der Agent, der es öffnet, in einer Bromure-VM pro Profil lebt, hinter einem Credential-Broker, einem Read-Write-Guardrail und einem Paket-Cooldown.

Vor einer Woche ritt der Wurm auf Red Hats npm-Scope herein und feuerte durch einen preinstall-Hook. Diese Woche ritt er auf Microsofts GitHub herein und brauchte überhaupt keine Installation. Miasma pflanzte projekt-gescopte Konfiguration in 73 Microsoft-eigene Repositories — Azure/functions-action, die offizielle Deploy-Action, darunter — und die Nutzlast führte sich aus in dem Moment, in dem ein Entwickler das Repo in Claude Code, Cursor, Gemini CLI oder VS Code öffnete. Das Klonen war nicht der Auslöser. Es in Ihrem Agenten zu öffnen war es.

Ein Entwickler, der Azure/functions-action klont, um ein fehlschlagendes Deploy zu debuggen, zögert nicht, und der Agent, auf den er ihn richtet, auch nicht. Es ist ein First-Party-Repository aus Microsofts eigener Azure-Organisation — die kanonische Quelle der GitHub-Action, die das halbe Ökosystem als Azure/functions-action@v1 referenziert. Es gibt keinen Maintainer zu prüfen, denn der Maintainer ist Microsoft. Es gibt keinen Namen, bei dem man die Augen zusammenkneifen müsste, denn der Name ist genau das, was er sein sollte. Also wird das Repo geöffnet, und der Agent liest die Konfiguration des Projekts, so wie es jedes moderne Coding-Tool beim Öffnen des Ordners tut — und eine dieser Konfigurationsdateien zeigt auf einen Befehl, und der Befehl ist ein rund 4,3 Megabyte großer Blob, der anfängt, das Dateisystem nach Schlüsseln zu durchsuchen.

Wir schrieben die npm-Hälfte genau dieser Kampagne auf vor sieben Tagen. Miasma ist derselbe Wurm — eine Variante des „Mini-Shai-Hulud“-Codes, den TeamPCP Mitte Mai öffentlich freigab — und die unbequeme Tatsache, die er demonstriert, ist dieselbe, eine Drehung weiter geschraubt: seriöse Quelle ist keine Sicherheitskontrolle. Es fühlt sich wie eine an. Der Großteil der Supply-Chain-Ratschläge lehnt sich daran an. Und am 5. Juni kaufte es exakt nichts, gleich zweifach: der Namespace war Microsofts, und die Ausführung kam nicht von einem Paket, das Sie zu installieren wählten. Sie kam vom Öffnen eines Ordners.

Was Miasma mit Microsofts Repositories tat.

Am 5.–6. Juni 2026 deaktivierte GitHub 73 Repositories über vier Microsoft-GitHub-Organisationen, nachdem bösartige Commits in sie geschoben worden waren, laut The Hacker News und einer detaillierten Analyse von StepSecurity. Redmond Magazine berichtete am 8. Juni darüber. Die Aufschlüsselung:

  • Azure — 49 Repositories, darunter Azure/functions-action (die offizielle Functions-Deployment-Action) und die Language-Worker für .NET, Python, Java, Go und PowerShell.
  • microsoft — 10 Repositories.
  • Azure-Samples — 13 Repositories.
  • MicrosoftDocs — 1 Repository.

Die Form, auf ihre Mechanik heruntergebrochen:

  • Der Einstiegspunkt war ein zuvor kompromittierter Contributor-Account mit Commit-Zugang, genutzt, um bösartige Commits direkt in die Repositories zu schieben — markiert mit [skip ci], sodass die Änderungen an den CI/CD-Checks vorbeirutschten, die sonst gelaufen wären.
  • Der Commit pflanzte projekt-gescopte Konfiguration — die Art Datei, die ein Coding-Agent oder eine IDE liest und automatisch ausführt, wenn man den Ordner öffnet: ein Editor-Task, ein Agent-Hook, ein projektdefinierter MCP-Server. Das ist dieselbe Klasse von Vertrauensgrenze, die Adversa AIs TrustFall über Claude Code, Cursor CLI, Gemini CLI und Copilot CLI demonstrierte — alle vier führen projektdefinierte Konfiguration direkt nach dem Folder-Trust-Prompt aus.
  • Die Nutzlast — rund 4,3 MB obfuskierter Code — führte sich aus, als das Repository in Claude Code, Gemini CLI, Cursor oder VS Code geöffnet oder über ein npm test-Script ausgeführt wurde. Nicht beim bloßen Klonen. Der Agent auf den geklonten Baum zu richten ist das, was sie ausführte.
  • Bei der Ausführung durchsuchte sie den Host nach GitHub-Tokens, AWS-Schlüsseln, Azure-Service-Principals, GCP-Credentials, npm- und PyPI-Publishing-Tokens, SSH-Schlüsseln und .env-Dateien, dann nutzte sie den gestohlenen Zugang, um sich selbst weiterzucommitten — was sie zu einem Wurm macht statt zu einem Einmaleffekt.

Ein Detail ist es wert, sich daran aufzuhalten: Azure/durabletask war unter den getroffenen Repositories — und es war im Mai bereits kompromittiert worden, in der TeamPCP-Kampagne, und bereinigt. Ein Repo, das einmal behoben wurde, wurde fünf Wochen später neu vergiftet. Bereinigung ist kein Zustand, den man erreicht und behält; es ist ein Zustand, aus dem man in dem Moment wieder herausfällt, in dem ein weiteres Credential in der Kette übernommen wird.

Es lohnt sich, ebenso präzise zu sein, was nicht geschah. Microsofts Unternehmensnetzwerk wurde nicht kompromittiert. Azure, der Cloud-Dienst, wurde nicht kompromittiert. Keine Kundendaten und kein Produktivsystem wurden berührt. Das war ein Angriff auf Quellcode-Repositories — und seine am breitesten spürbare Folge hatte mit der Malware überhaupt nichts zu tun: in dem Augenblick, in dem GitHub Azure/functions-action deaktivierte, hörte jede Pipeline auf Erden, die Azure/functions-action@v1 referenzierte, auf aufzulösen. Microsoft war der prominenteste Träger. Die tatsächlich Übernommenen waren die Entwickler, die die vergifteten Repos zwischen dem 3. und 5. Juni in einem Agenten öffneten und denen die Credentials von ihren eigenen Maschinen gewischt wurden.

ENTWICKLER-LAPTOP — Host-Secrets sichtbar für alles, was der Agent beim Öffnen ausführtMICROSOFTS EIGENES GITHUBkompromittierter ContributorAccount → push [skip ci]Azure/functions-actionAzure/durabletask (erneut)+ Projekt-Config gepflanztENTWICKLER KLONTgit clone …/functions-actionnoch läuft nichtsFirst-Party-Repo ⇒kein Grund zu zögernIM CODING-AGENTEN GEÖFFNET — Nutzlast feuertClaude Code · Cursor · Gemini CLI · VS CodeÖffnen → liest Projekt-ConfigAgent-Hook / Task / MCP-Server↳ führt 4,3-MB-Nutzlast aus(Klonen allein lief NICHT)HOST-DATEISYSTEM & ENV — alles echt, alles lesbar durch die Nutzlast$GITHUB_TOKEN, gh hosts.ymlPush-Zugang (echt)~/.aws, Azure-Service-PrincipalsCloud-Schlüssel (echt)~/.npmrc, PyPI-Tokenhier publishen (echt)~/.ssh/id_ed25519, .envSchlüssel + CI-Secrets (echt)tar | encrypt | exfiltrate→ von deiner Maschine geerntetSELBST-PROPAGATIONgestohlener GitHub-Zugangins nächste Repo committendieselbe Config pflanzen73 Repos, 4 Orgsdurabletask: zweimal getroffen
Miasma auf einer Entwicklermaschine, die ein betroffenes Microsoft-Repo in einem Coding-Agenten öffnet. Ein zuvor kompromittierter Contributor-Account schiebt einen [skip ci]-Commit, der projekt-gescopte Konfiguration pflanzt — einen Agent-Hook, einen Editor-Task, eine MCP-Server-Definition. Der Entwickler klont das Repo (nichts läuft) und öffnet es in Claude Code, Cursor, Gemini CLI oder VS Code. Beim Öffnen des Ordners liest der Agent diese Konfiguration und führt sie aus — eine 4,3-MB-Nutzlast, die den Host nach GitHub-Tokens, AWS-Schlüsseln, Azure-Service-Principals, npm/PyPI-Tokens, SSH-Schlüsseln und .env-Dateien durchsucht, sie verschlüsselt, hinausschickt und den gestohlenen GitHub-Zugang nutzt, um sich selbst ins nächste Repo zu committen. Kein Typosquat, kein gefälschter Maintainer, kein npm install. Das Vertrauenssignal ist der Microsoft-Org-Name, und die Ausführung kommt vom Öffnen eines Ordners.

Dasselbe Repo, geöffnet innerhalb von Bromure Agentic Coding.

Bromure Agentic Coding lässt Ihren Coding-Agenten innerhalb einer Linux-VM pro Profil laufen — eigener Kernel, eigenes Dateisystem, eigener Netzwerk-Stack, auf Apples Virtualization-Framework. Ein Profil ist ein kohärenter Arbeits-Scope: dieser Kunde, dieses interne Produkt, dieses Open-Source-Repo, das Sie zum Debuggen eines Deploys geklont haben. Sie klonen Azure/functions-action in dieses Profil und öffnen es dort drin mit Ihrem Agenten. Der Auslöser beim Öffnen des Ordners feuert genau wie konstruiert. Die Nutzlast läuft. Und dann geht sie auf einem Host, den sie nicht erreichen kann, auf die Suche nach Schlüsseln.

Denn die Credentials sind nicht im Profil. Die VM wird mit Stubs ausgeliefert — gefälschten Tokens, die für git, gh, aws, kubectl, npm und alles andere, das einen Authorization-Header erwartet, echt aussehen. Ein Proxy auf Ihrem Mac sitzt vor jeder Verbindung, die die Sandbox verlässt, erkennt den Stub und tauscht ihn auf dem Draht gegen das echte Secret aus, während die Anfrage hinausgeht (die Sandbox, die den Schlüssel hielt führt durch den Mechanismus). Der echte GitHub-PAT, der echte AWS-Schlüssel, das echte Azure-Principal — keines von ihnen berührt eine Datei, eine Umgebungsvariable oder eine Speicherseite, die die VM lesen kann. SSH-Schlüssel verlassen den macOS-Keychain überhaupt nicht; nur der ssh-agent-Socket wird hineingeleitet, so wie OpenSSH es immer beabsichtigt hat.

Gehen Sie also Miasmas Durchsuchung durch diese Grenze. Die Nutzlast liest die Umgebung nach $GITHUB_TOKEN und findet einen Stub. Sie liest ~/.aws und findet nichts. Sie liest ~/.npmrc und findet kein Publish-Token. Sie liest ~/.ssh und findet keine Schlüsseldatei — da ist ein hineingeleiteter Socket, kein privater Schlüssel auf der Disk. Der 4,3-MB-Blob läuft bis zum Ende durch, genau wie geschrieben. Er exfiltriert nur eine Box, die nie Ihre Schlüssel hielt, auf der falschen Seite einer hardware-erzwungenen Grenze von allem, was zählt.

BROMURE-VM PRO PROFILAgent öffnet functions-actionProjekt-Config → Nutzlast feuertläuft, aber nur im GastWAS DIE NUTZLAST SIEHT$GITHUB_TOKENstub_7f3a…~/.aws, ~/.npmrcStub / abwesend~/.ssh-Schlüsseldateinur Socketpropagieren: git push (selbst)braucht Write → fragt ProxyHost-Schlüssel exfil: nichts zu holenAbhängigkeit nötig?npm install → Fetch verlässt VMdurch Bromures ProxyPROXY · DEIN MACCREDENTIAL-BROKERechter PAT / Schlüssel hierStub → echt, auf dem DrahtWert betritt nie die VMGUARDRAIL: READ/WRITEpush = mutieren → PROMPTnennt Verb + ZielSUPPLY CHAINOSV + socket.dev-ScanCooldown: < 2 Tage gehaltenInstall-Scripts entferntERGEBNISHost-Schlüssel:Stubs geerntet,echte unberührtPush zum Propagieren:pausiert, du sagst neinfrisches böses Paket:erreicht die VM nieExplosionsradius = 1 Profil
Drei Schichten, in der Reihenfolge, in der Miasma auf sie trifft. (1) Supply Chain: der Proxy scannt jeden Paket-Fetch gegen OSV und socket.dev und stellt Releases unter Quarantäne, die jünger sind als der Cooldown — sodass der Agent nicht einmal eine frische bösartige Abhängigkeit ziehen kann, während das Ökosystem noch aufholt. (2) Credential-Brokering: die VM hält nur Stubs; die echten Secrets sitzen auf dem Host hinter dem Proxy, auf dem Draht eingetauscht, sodass die Host-Durchsuchung der Nutzlast Platzhalter findet. (3) Guardrails: der Propagationsschritt des Wurms — ein git push, um sich selbst weiterzucommitten — ist ein zustandsändernder Aufruf, und ein Read-Write-Guardrail stoppt ihn auf dem Draht und fragt nach, mit Nennung von Verb und Ziel. Jede Schicht wird unterhalb des Agenten erzwungen, an der VM-Grenze, die der Agent nicht umgehen kann.

Der Propagationsschritt ist ein Write, und Writes bekommen einen Prompt.

Schlüssel zu stehlen ist nur die Hälfte von Miasma. Die andere Hälfte ist Verbreitung: er nutzte gestohlenen GitHub-Zugang, um sich selbst ins nächste Repository zu committen, und das ist es, was eine Handvoll vergifteter Repos in 73 verwandelte. Selbst in einem Profil, das legitim Push-Zugang hat — sagen wir, Sie klonten functions-action genau, weil Sie beabsichtigen, einen PR dagegen zu öffnen — muss der Propagationsschritt des Wurms immer noch durch den Proxy hinausgehen, und genau dort treffen ihn die Guardrails.

Guardrails lesen die Operation, nicht nur die Verbindung — sie unterscheiden einen Read von einem Write. Ein git fetch ist ein Read; ein git push ist ein Write. Setzen Sie das GitHub-Credential eines Profils auf bei Write fragen, und in dem Moment, in dem der Agent nach einem zustandsändernden Aufruf greift — dem git-receive-pack, das der Wurm braucht, um seine Config zurückzucommitten, einem DELETE gegen eine API, einem Terminate* an EC2 — stoppt Bromure ihn auf dem Draht und blendet auf Ihrem Mac einen Prompt ein, der das Verb, das Ziel und das Profil nennt. Der Grant, den Sie geben, ist zeitlich begrenzt: fünfzehn Minuten für ein Release, einmalig für die unheimlichen, niemals, wenn die Anfrage keinen Sinn ergibt. Reads unterbrechen Sie nie; der Agent fetcht und greppt und liest den ganzen Tag. Es ist die Mutation, die pausiert.

Das ist der Unterschied zwischen „der Agent hat ein Token“ und „der Agent kann mit dem Token machen, was er will“. Miasmas ganzer Verbreitungsmechanismus ist ein Write, von dem der Agent Ihnen nie sagte, dass er ihn machte — und ein Write, von dem der Agent Ihnen nie sagte, dass er ihn machte, ist genau das, wofür der Read-Write-Prompt gebaut ist. Der Push, der den Wurm propagiert, wird zu einem Dialogfeld, bei dem Sie auf Nicht erlauben klicken, genauso wie „der Agent löschte die Produktionsdatenbank“ aufhört, ein Postmortem zu sein und zu einem Prompt wird, den Sie ablehnten.

Die Version war Stunden alt, und Bromure lässt Pakete altern.

Es gibt einen zweiten Weg, auf dem Miasma — und die breitere Mini-Shai-Hulud-Linie — einen Entwickler erreicht: nicht durch ein Repo, das Sie öffnen, sondern durch ein frisch vergiftetes Paket, das der Agent während seiner Arbeit installiert. Die Red-Hat-Hälfte dieser Kampagne war genau das, ein preinstall-Hook auf 32 Paketen in einem vertrauenswürdigen Scope. Und das brutale Detail jener Vorfälle ist das Timing: eine kompromittierte Version wird typischerweise innerhalb von Stunden erwischt und zurückgezogen — aber das sind genau die Stunden, in denen ein autonomer Agent, der unbeaufsichtigt läuft, sie ziehen könnte.

Bromures Supply-Chain-Schicht verwandelt denselben Grenz-Proxy in einen Scanning-Checkpoint, und sie tut die zwei Dinge, die gegen eine taggleiche Kompromittierung tatsächlich zählen:

  • Sie erzwingt einen Scan jedes Fetches gegen socket.dev wie auch gegen OSV. OSV erwischt bekannte CVEs oberhalb der Schweregradschwelle, die Sie setzen. socket.dev erwischt, was die Vulnerability-Datenbanken noch nicht aufgeholt haben — bösartige Install-Scripts, verhaltensbasierte Malware, Typosquats, die gerade veröffentlichte Kompromittierung. Ein markiertes Release wird blockiert, bevor das Tarball je in der VM landet. Entscheidend ist, dass der Scan unterhalb des Agenten läuft, am Proxy: wie auch immer der Agent seine eigene Config umschreibt, um Sie zu umgehen, der Fetch verlässt ihn trotzdem durch die Grenze, die er nicht überqueren kann.
  • Sie erzwingt einen Cooldown. Bromure stellt jedes Release unter Quarantäne, das in den letzten zwei Tagen veröffentlicht wurde — einstellbar — sodass eine vor einer Stunde hochgeladene Version in diesem Profil schlicht nicht installierbar ist, während das Ökosystem aufholt. Gegen einen Wurm, dessen ganzes Zeitfenster die Lücke zwischen publish und yank ist, ist ein Cooldown keine Heuristik darüber, ob ein Paket schlecht aussieht. Es ist eine Weigerung, der Erste zu sein, der es herausfindet. Kombinieren Sie es mit dem Install-Script-Stripping, das Bromure im Flug erledigt — postinstall-Hooks aus dem Tarball ziehen und den Metadaten-Hash fixen, sodass die Installation weiterhin verifiziert — und das Paket, das doch landet, landet inert.

Für Miasma im Speziellen ist der Repo-Open-Vektor die Schlagzeile. Aber dieselbe Kampagne verbreitet sich auch durch Pakete, und der Cooldown ist die Kontrolle, die die npm-Seite davon ausgehungert hätte: ein frisches @redhat-cloud-services-Release, oder eine frisch vergiftete transitive Abhängigkeit, gezogen während des Debuggens jenes Microsoft-Repos, sitzt in Quarantäne durch genau die Stunden, in denen es gefährlich ist.

Wo dich das nicht rettet.

Ein Push, den du genehmigst, ist ein Push, der passiert.

Das Read-Write-Guardrail erwischt den Write, von dem der Agent dir nichts sagte. Es liest das Diff nicht. Wenn du legitim nach functions-action pushst und den Prompt genehmigst, leitet Bromure den Push weiter — einschließlich, im Prinzip, eines vergifteten Workflows, den du im Diff nicht bemerkt hast. Lies, was du genehmigst. Der Session-Trace sagt dir, welche Diffs zu lesen sind.

Der Cooldown ist ein Fenster, keine Mauer.

Zwei Tage sind auf die beobachtete Publish-zu-Yank-Lücke abgestimmt, aber ein geduldiger Angreifer kann länger auf einer kompromittierten Version sitzen als der Cooldown und am dritten Tag immer noch installierbar sein. Der Cooldown hungert taggleiche Würmer aus; er bürgt nicht für ein Paket, das bloß alt geworden ist. socket.dev und OSV müssen weiterhin ihren Teil tun.

Das Profil ist langlebig, also bleibt Persistenz bestehen.

Ein Bromure-Profil ist keine Wegwerf-Disk. Eine Nutzlast, die sich selbst in einen Startup-Pfad innerhalb des Profils schreibt, kann in die nächste Session in diesem Profil überleben. Was sie aufwacht vorfindet, ist ein Gast ohne Host-Schlüssel und ein Broker, der nur kurzlebige, geprompete, scope-limitierte Tokens spricht — Präsenz in einer Box, in der nichts drin ist — aber dennoch Präsenz.

Scope den Broker mit Absicht.

Wenn ein Profil heute bereitgestellt ist, um in ein Repo zu pushen, und dieses Profil heute Miasma ausführt, geht ein genehmigter Write durch. Broker-Grants funktionieren, weil sie schmal sind. Ein Profil, das ein Repo nur lesen muss, sollte es nicht schreiben können; ein Profil, das nie veröffentlicht, sollte kein Publish-Token halten. Isolation begrenzt die Explosion; Scoping entscheidet, wie groß sie je hätte sein können.

Das nächste vertrauenswürdige Repo ist irgendwo schon geklont.

Die Lehre des TanStack-Wurms war, dass die Lockfile und die Signatur keine Verteidigungen sind. Die Lehre des Red-Hat-Scopes war, dass es auch der Publisher nicht ist. Microsoft fügt das nächste Korollar hinzu: auch nicht das Repository, und der Auslöser muss nicht einmal eine Installation sein, die du wähltest — er kann ein Ordner sein, den dein Agent öffnete. Das Azure/functions-action-Repo tat nichts falsch, indem es vertrauenswürdig war. Vertrauenswürdig zu sein ist der ganze Zweck einer kanonischen First-Party-Action, und es ist genau das, was es des Vergiftens würdig machte — zweimal, im Fall von durabletask.

Das kannst du nicht beheben, indem du sorgfältiger vertraust, denn das Vertrauen war nie fehlplatziert. Du behebst es, indem du die Dinge so arrangierst, dass „welches Repo ist das“ und „welcher Scope hat das veröffentlicht“ aufhören, die Fragen zu sein, von denen deine Keychain abhängt. Bromure Agentic Coding ist die Konfiguration, in der der Agent das Repo innerhalb einer VM pro Profil öffnet, die echten Credentials auf dem Host hinter einem Broker bleiben, jeder Write, den der Agent macht, an einem Prompt vorbei muss, und ein Paket nicht installiert werden kann, bis es einen Cooldown überlebt hat. Das Schlimmste, was ein vergiftetes Ordner-Öffnen anrichten kann, ist, eine Box zu exfiltrieren, die nie deine Schlüssel hielt. Es ist kostenlos, Open Source und heute ausgeliefert.