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.
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.
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.