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

Der Provider läuft dort, wo die Schlüssel liegen

Aikido hat einen nordkoreanischen RAT gefunden, ausgeliefert als zwei Terraform-Provider in HashiCorps Registry. terraform init lädt einen Provider herunter, terraform plan führt ihn als lokalen Prozess aus, und der Provider liest Ihre Cloud-Zugangsdaten, denn genau dafür ist ein Provider da. Die Payload entschlüsselt sich nur für die Variablennamen eines einzigen Opfers, sodass Scanner, die das Paket gezogen haben, inerten Code sahen. Zehn der achtzehn erreichten Maschinen waren Macs. Bromure Agentic Coding gibt dieser Binary eine VM, einen gefälschten Secret Key und eine Liste von Zielen, die sie ansprechen darf.

Jede Kontrolle, die die Branche für Coding-Agenten gebaut hat, sitzt vor dem Befehl: den Plan freigeben, das Diff lesen, den Push absichern. Keine davon sitzt zwischen einer heruntergeladenen Provider-Binary und ~/.aws.

Sie fügen einer Stack einen Container hinzu. Sie bitten den Agenten um einen Docker-Provider, er schreibt vier Zeilen HCL, und Sie führen terraform init aus. Terraform löst den Provider aus der Registry auf, lädt ihn herunter, prüft seine Signatur und legt ihn in den Cache. Dann führen Sie terraform plan aus, und Terraform startet diese Binary als Prozess auf Ihrer Maschine und spricht per RPC mit ihr. Unterwegs authentifiziert sie sich an Ihrem Cloud-Konto, mit denselben Umgebungsvariablen und demselben ~/.aws-Profil, die Sie selbst benutzen.

Jeder Schritt davon ist das dokumentierte Design, und diesen Monat war er zugleich ein Auslieferungsweg für Malware.

Was Aikido gefunden hat

Am 22. September berichtete Aikido über einen in Go geschriebenen Fernzugriffstrojaner, verteilt über zwei Terraform-Provider und zwei Go-Module. The Hacker News griff es am Tag darauf auf und nannte es das erste Mal, dass Angreifer die von HashiCorp betriebene Registry zur Auslieferung einer Payload genutzt haben. Checkmarx, JFrog und SafeDep lieferten weitere Analysen.

Die Provider sind gocommunity-io/dockerd mit 222 Downloads und kreuzwenker/docker mit 1.449. Der zweite ist einen Buchstaben von kreuzwerker/docker entfernt, einem echten Provider mit 56 Millionen gemeldeten Downloads. Die Go-Module sind gocommunity.io/orderedbtree, veröffentlicht am 11. August, und gogets.dev/btreex, veröffentlicht am 8. September.

Aikido ordnet die Kampagne Graphalgo zu, das ReversingLabs im Februar dokumentiert und nordkoreanischen Akteuren zugeschrieben hat. Derselbe Signaturschlüssel taucht seit April in npm-Payloads auf. Man kommt über ein Stellenangebot an diese Pakete: Eine vorgebliche Web3-Firma spricht eine entwickelnde Person auf LinkedIn oder Facebook an und schickt eine Programmieraufgabe. Das Repository der Aufgabe ist sauber. Die Abhängigkeit, die es zieht, ist die Malware.

Ein Provider ist ein Programm, und Terraform gibt ihm die Schlüssel

npm install führt die Setup-Skripte eines Pakets aus und tritt dann ab. terraform init holt ein ganzes Programm, und dieses Programm auszuführen ist der Hauptakt.

Ein Terraform-Provider ist eine ausführbare Binary, die Terraform Core per RPC aufruft. Terraform führt sie aus, weil sie auszuführen ist, was ein Provider ist. Sie werden keinen Install-Hook zum Entfernen und kein Lifecycle-Skript zum Prüfen finden, denn Ausführung ist hier das Feature und kein Nebeneffekt des Entpackens eines Tarballs.

Die Binary läuft außerdem an der einen Stelle Ihres Netzes mit dem kürzesten Weg zur Produktion. Purple Shield beschreibt die Maschine: „Ein bösartiger Provider läuft damit auf der Maschine der Person, die Ihre Infrastruktur deployt, mit allen Zugriffen, die diese Person hat.“ Und: „ein Laptop, der terraform apply ausführt, hat für gewöhnlich Cloud-Provider-Keys, Git-Tokens, SSH-Keys und Terraform-State-Dateien in Reichweite.“

Ein npm-Stealer muss Ihr Home-Verzeichnis nach Zugangsdaten durchsuchen. Terraform reicht sie dem Provider.

Der dokumentierte Pfad und der eine neue Zweigdie Registrykreuzwenker/dockerinitdie Maschine, die deploytTerraform Coreplan → Start, dann RPCder Provider-Prozesseine geladene Binarydie Zugangsdaten~/.aws, Tokens, Keysder Zweig, den niemand wollte, im Providersha256(containerName + networkID)Treffer → AES-Key → entschlüsseln → go run .jede andere Konfiguration, jeder ScannerHash passt nicht; nichts wird entschlüsseltdie eine adressierte Konfigurationein RAT, meldet Hostname und Hardware, wartet18 Maschinen, 725 Nachrichten: 3 Windows, 5 Linux, 10 macOS
In der Kette steckt kein Exploit. terraform init löst einen Provider aus der Registry auf und cacht die Binary; terraform plan startet sie als lokalen Prozess und spricht per RPC mit ihr; der Provider liest die umgebende Credential-Kette, und das ist das Protokoll. Die Provider von Graphalgo fügen einen Zweig hinzu: einen versteckten Einstiegspunkt, der zwei Ihrer eigenen Terraform-Variablen hasht, und nur wenn der Hash zu einem fest einkodierten Wert passt, nutzt er diesen Hash als AES-Schlüssel, um eine in einem Beispiel-Fixture versteckte Payload zu entschlüsseln und mit einem abgekoppelten 'go run' zu starten. Für jede andere Konfiguration ist der Code inert.

An eine einzige Person adressiert

Diese Provider greifen ein Ziel an und ignorieren alle anderen, die sie installieren.

Beide tragen einen versteckten Einstiegspunkt in /internal/provider/resource_docker_container_funcs.go, der nur auslöst, wenn der SHA256 der aneinandergehängten Terraform-Variablen containerName und networkID einem fest einkodierten Wert entspricht. Der Code nutzt diesen Hash dann als AES-Schlüssel, um einen Dateipfad im Paket zu entschlüsseln, entpackt, was er dort findet, entschlüsselt jede Datei und führt das Ergebnis als Go-Paket mit einem abgekoppelten go run . aus. Das Archiv versteckten sie im Beispielverzeichnis, in einer Datei namens import-resource.sqlite3.

Der Angreifer muss also die Namen kennen, die Sie zwei Variablen gegeben haben, und dieser Angreifer kannte sie: Über den Köder des Stellenangebots hat er sie erfahren. Alle Forschenden und alle Scanner, die das Paket gezogen haben, bekamen Code, der dasitzt und nichts tut. Man kann nicht erkennen, was sich nie entschlüsselt.

Die zweite Stufe ist ein Fernzugriffstrojaner. Er meldet zuerst Systeminformationen, darunter Hardware-Merkmale, Betriebssystem, Hostname und ob node auf der Maschine verfügbar ist, und wartet dann an zwei Stellen auf Anweisungen. Eine ist Slack: ein Workspace namens portfolio-devs, ein Channel namens frontend-devs und später ein zweiter Workspace mit einem Channel namens qa-announcements. Aikido zählte dort 1.240 verschlüsselte Nachrichten zurück bis zum 16. Juli. Die andere ist ein Smart Contract im Arbitrum-Sepolia-Testnet, an einer fest einkodierten Adresse, mit 1.402 Transaktionen zurück bis zum 6. August. Die Malware fragt die Blockchain alle drei Sekunden ab und Slack alle zehn.

Beide Kanäle sind gewöhnliches HTTPS zu Zielen mit gutem Ruf. Ihre Entwicklermaschinen reden den ganzen Tag mit Slack, und wenn eine davon mit einem öffentlichen Ethereum-RPC-Endpunkt spricht, wirkt das in jeder Woche normal, in der das Team etwas on-chain ausliefert. Dieselbe Gruppe nutzte einen Sepolia-Contract in dem npm-Paket, das Checkmarx vergangene Woche beschrieb, und sie verwendet ihn offen weiter, weil es offen funktioniert.

Aikido holte die Opferliste aus dem Slack des Angreifers selbst: 18 eindeutige Hostnamen über 725 Check-in-Nachrichten. Drei Windows, fünf Linux, zehn macOS.

Was sich in einem Bromure-Workspace ändert

Die Kampagne war auf Zugangsdaten aus. In Bromure Agentic Coding enthält die ~/.aws/config der VM nicht Ihren geheimen Zugangsschlüssel. Sie verweist auf einen credential-process-Helfer, der über einen virtio-Socket Ihre echte Access Key ID zusammen mit einem gefälschten 40-Zeichen-Secret ausgibt. Das Handbuch listet die Werkzeuge, die das ohne jede Konfiguration übernehmen: jedes AWS-SDK, die aws-CLI, boto3 und terraform. Der Provider liest die Credential-Kette so, wie er es erwartet, bekommt ein wohlgeformtes Profil und signiert seine Anfragen mit einem Secret, das nichts wert ist.

Der Host repariert diese Signatur auf dem Weg nach draußen. Sein Resigner erkennt Verkehr an *.amazonaws.com, streift die zum Scheitern verurteilte SigV4-Signatur des Gasts ab und signiert mit dem echten Secret neu, bevor die Anfrage Ihren Mac verlässt. Ihr terraform plan funktioniert. AWS beantwortet eine gestohlene Kopie der Zugangsdaten mit InvalidSignatureException.

Dann versucht das Fake, hinauszugelangen. Der Proxy durchsucht Header und Body jeder ausgehenden Anfrage nach den Platzhalterwerten des Workspace selbst, mit einem Aho-Corasick-Automaten, der günstig genug ist, um auf allen zu laufen. Schicken Sie ein Fake an einen Host, für den es nicht geprägt wurde, etwa an einen Slack-Webhook, antwortet der Proxy mit HTTP 451 und leitet kein einziges Byte weiter. Die VM pausiert. Eine rote Zeile Credential brokering landet in der Security Timeline, und Bromure markiert den Workspace als kompromittiert, so dass Ihr nächster Start anbietet, dessen Disk und Home zu löschen und Ihre Tokens, SSH-Keys und Einstellungen zu behalten.

Das C2 muss immer noch aus der Maschine heraus. Egress ist ein geordneter Regelsatz aus Allow- und Deny-Regeln, von oben nach unten ausgewertet, der in default allow oder default deny endet. Ein Workspace, der Infrastruktur deployt, braucht registry.terraform.io, releases.hashicorp.com und Ihre Cloud-API, womit portfolio-devs.slack.com und ein Arbitrum-RPC-Endpunkt nicht auf der Liste stehen. Der Host setzt die Regeln zweimal durch: Der virtuelle Switch prüft Ziel-IP und per DNS mitgelesenen Hostnamen, über TCP und UDP, und der Proxy prüft erneut den TLS-Servernamen. Der Switch leitet Port 80 und Port 443 in den Proxy um, ohne jede Umgebungsvariable und ohne etwas, das der Gast abschalten könnte. Jedes Urteil wird zu einer Firewall-Zeile in der Security Timeline.

Auf der Maschine, die deploytder Provider-Prozess, als Sie ausgeführtliest ~/.aws → echter Secret Keyliest Git-Tokens, SSH-Keys, tfstateIhr Home-Verzeichnis ist der Radiusausgehend, ungefragtportfolio-devs.slack.com · alle 10sArbitrum Sepolia RPC · alle 3sbeide Ziele haben einen guten Rufes gibt keine Anomalie zum Alarmierendanach: alles rotieren, was erreichbar warIn einem Bromure Agentic Coding Workspaceder Provider-Prozess, im Gastliest ~/.aws → Access Key ID + Fake-SecretUbuntu 24.04, unter dem Hypervisoreine Platte, ersetzbar per Menüpunktausgehend, geprüft an Ihrem Regelsatzslack.com → keine Regel → default denySepolia RPC → keine Regel → default denyund wenn das Fake den Geltungsbereich verlässtHTTP 451, kein Byte weiter, VM pausiertdanach: wipen und starten, nichts rotieren
Dieselbe Provider-Binary, zwei Maschinen. Auf einem Laptop läuft sie als Sie, liest einen echten Secret Key aus der Credential-Kette und erreicht Slack und einen öffentlichen RPC-Endpunkt, weil niemand eine Regel dagegen geschrieben hat. In einem Bromure-Workspace läuft sie in einem Ubuntu-Gast unter dem Hypervisor, findet eine Access Key ID mit einem gefälschten Secret und trifft auf dem Weg hinaus auf einen geordneten Egress-Regelsatz; trägt sie das Fake irgendwohin, wofür es nicht geprägt wurde, kommt HTTP 451 zurück und die VM pausiert. AWS-Aufrufe, die dort wirklich hingehören, werden auf dem Host neu signiert, terraform funktioniert also weiter, und zerstörerische AWS-Aktionen halten für einen Dialog an, der auf dem Mac erscheint.

Sie geben die Löschungen frei

Noch eine Kontrolle sitzt zwischen dem Provider und Ihrem Konto, und sie ist die entscheidende, wenn der Angreifer Ihre Infrastruktur will statt Ihrer Secrets.

AWS-Zugangsdaten tragen eine Schreibrichtlinie, und neue Workspaces stehen standardmäßig auf Prompt before write. Der Proxy klassifiziert jeden Aufruf anhand des Aktionsnamens im Header X-Amz-Target oder im Parameter Action=: Get*, List* und Describe* sind Lesevorgänge und gehen kommentarlos durch, während Delete*, Terminate*, Revoke* und Verwandte als zerstörerisch gelten. Ein Schreibvorgang hält für einen Dialog mit dem Titel Allow write on "<scope>" from workspace "<name>"? an, der die Operation wörtlich zeigt statt einer Zusammenfassung, mit Allow for 15 minutes, Allow once, Allow for the rest of the session und Don't allow. Lehnen Sie ab, bekommt der Gast eine AccessDeniedException, die der Agent meldet und über die er hinweggeht.

Sie sind es, der auf diesen Dialog klickt, auf Ihrem Mac. Ein Prozess in der VM erreicht ihn nicht, und wer am anderen Ende des Slack-Channels sitzt, ebenso wenig.

Behandeln Sie die Toolchain als Ausführer

terraform init und terraform plan sind Herunterladen-und-Ausführen. Fragen Sie, was ein Provider im Inneren ausführt, denn ausführen wird er.

Lassen Sie kein lebendes Secret in Reichweite

Geben Sie dem Provider eine Credential-Kette, die ihn zufriedenstellt, und ein Secret, das nichts wert ist. Der Host signiert, jenseits der Grenze, begrenzt auf das Ziel, zu dem der Schlüssel gehört.

Benennen Sie zuerst die Ziele

Ein Deploy-Workspace braucht eine kurze, stabile Liste: die Registry, den Release-Host, Ihre Cloud-API. Schreiben Sie sie einmal, setzen Sie nicht passenden Verkehr auf Deny, und ein toter Briefkasten hat nichts mehr abzufragen.

Lassen Sie das Unumkehrbare fragen

Lassen Sie Lesevorgänge still laufen. Sorgen Sie dafür, dass Terminate* Sie einen Dialog kostet, auf einem Bildschirm, den die VM nicht erreicht.

Hier hat niemand eine Schwachstelle gefunden

HashiCorps Registry lieferte das Paket, nach dem sie gefragt wurde. Terraform prüfte die Prüfsumme, die es bekam, und führte den Provider so aus, wie es jeden Provider ausführt. Keine Sandbox brach, kein Agent las eine vergiftete Anweisung, und kein Modell verhielt sich falsch.

Zwei Provider mit zusammen 1.671 Downloads erreichten mindestens 18 Maschinen, die meisten davon Macs, und saßen dort und fragten alle drei Sekunden einen Smart Contract ab. Dokumentiertes Verhalten trug die ganze Kette. Der eine neue Teil, eine Payload, die verschlüsselt bleibt, solange Ihre Variablen nicht ganz bestimmt heißen, existiert genau dafür, dass nichts sie findet.

Entscheiden Sie diese Dinge also vor der Sitzung, denn während ihr werden Sie sie nicht erwischen. Man kann keinen Provider scannen, der seine Payload nicht entschlüsselt hat, und Sie werden die Exfiltration nicht aus dem Verkehr zu Slack herausklauben. Führen Sie das Deployment dort aus, wo die Schlüssel Attrappen sind, die Ziele eine Liste sind, die Sie geschrieben haben, und die Maschine, die übernommen wird, eine ist, die Sie wegwerfen.