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

Eingeloggt sein genügte

Ein Entwickler, der Plattenplatz aufräumte, fand 700 MB in einem versteckten Verzeichnis und zog an dem Faden: ZCode von Z.ai hatte seinen gesamten Arbeitsbaum in ein Tar gepackt, 42.411 Dateien, davon 86,6 % .git, es mit einem Schlüssel verschlüsselt, den nur Z.ai besitzt, und es direkt in einen Alibaba-Cloud-Bucket geschickt. Kein Angreifer, keine CVE, keine Malware. Der Auslöser war, eingeloggt zu sein. Der Client verschlüsselte das Archiv, also konnte kein Scanner es lesen, und die einzigen Fakten, die übrig blieben, waren das Ziel und die Größe. Das sind zwei der Felder, die Bromure Agentic Coding für jede Anfrage eines Workspace notiert, auf einem Draht, den das Werkzeug in der VM nicht auswählen darf.

Ihr Coding-Werkzeug muss nicht kompromittiert sein, um Ihren Quellcode irgendwohin zu schicken. Es braucht nur eine Voreinstellung. Dem Anbieter zu vertrauen ist nicht die Frage. Fragen Sie, ob Sie es merken würden.

Sie räumen Platz auf einer vollen Platte. Ein verstecktes Verzeichnis in Ihrem Home-Ordner belegt mehr als 700 Megabyte, und Sie erinnern sich nicht, etwas in dieser Größe installiert zu haben. Darin, unter einem Ordner namens checkpoints, liegt eine einzige 313-MB-Datei mit der Endung .enc und daneben ein Metadatensatz, in dem failureCount: 564 steht.

Fünfhundertvierundsechzig Versuche für etwas, um das Sie nie gebeten haben.

Siebenhundert Megabyte und ein Faden zum Ziehen

Dort begann der chinesische Entwickler, der als ferstar bekannt ist, am 18. September, und die Reverse-Engineering-Aufschreibung dessen, was er fand, lohnt sich in Gänze. Das Verzeichnis gehörte zu ZCode, dem Desktop-KI-Coding-Client von Z.ai, der Firma hinter den GLM-Modellen. CSO Online und InfoWorld übernehmen beide seine Zusammenfassung des Verhaltens wortwörtlich:

„Immer wenn Sie eingeloggt sind, packt ZCode Ihren gesamten Workspace still zusammen — komplette .git-Historie, LFS-Asset-Cache, Reflogs und globale App-Konfigurationen —, verschlüsselt ihn und lädt ihn direkt zu Aliyun OSS hoch.“

Der Auslöser sitzt im ersten Teilsatz. Keine Frage, die Sie gestellt haben, nicht die Indexierungsfunktion, die Sie eingeschaltet haben: ein gültiges Sitzungstoken, auf das der Capture-Prozess wartet, und sonst nichts. Eine einzige Sitzung verzeichnete in seinen Logs 62 Capture-Events. Als er das Archiv löschte, baute der Client innerhalb einer halben Stunde ein neues und erhöhte den Fehlerzähler.

Was in der Kiste war

Der Snapshot, den er auseinandernahm, enthielt 42.411 Dateien, 345 MB vor der Verschlüsselung und 313 MB danach. Woraus diese 345 MB bestanden:

  • .git/lfs/: 196,1 MB, 56,8 %
  • .git/objects/: 102,2 MB, 29,6 %
  • .git/logs/: 0,6 MB, 0,2 %
  • alles andere, also echter Quellcode und echte Dokumentation: 46,2 MB, 13,4 %

86,6 % des Uploads waren .git. Das Archiv trug jedes Objekt der Historie des Repositorys, jede große Binärdatei, die der LFS-Cache heruntergeladen hatte, und das Reflog, das jeden Branch auflistet, auf dem Sie waren, und jeden Commit, den Sie gemacht und dann zurückgenommen haben. Wer Ihren Arbeitsbaum liest, erfährt, was Sie ausliefern; wer Ihr .git liest, erfährt, was Sie versucht haben, was Sie zurückgerollt haben und wer es committet hat.

Ein Login-Token, vier Schritte, kein AngreiferIhr Rechner, eingeloggtArbeitsverzeichnis.git/lfs, .git/objects, .git/logsCode, Docs, globale App-Configs42.411 Dateien · 345 MBZwei Schalter in EinstellungenErfahrungsoptimierung → TrainingSnapshot-Indexierung → Server-Indexkeiner steuert das Verpacken1 · zcode.z.ailiefert eine OSS-Signaturund einen RSA-Public-Keydie private Hälfte bleibt am Server2 · lokal verpackt, versiegelttar.gz → AES-256-CTRKey ← RSA-OAEP-SHA256313 MB, für Sie unlesbar3 · POST an Alibaba Cloud OSSBucket zcode-proddirekt — nicht über Z.ai-Serverein Objekt pro Capture-Event4 · OSS-Callbackmeldet dem Backend den Eingangder Snapshot ist registriert undauf ihrer Seite indexierbarWas es auslösteEin gültiges Login-Token. Kein Prompt, keine Frage, nicht die Indexierungsfunktion.62 Capture-Events in einer einzigen Sitzung. Löschen Sie das Archiv, und es wird in dreißig Minuten neu gebaut.failureCount: 564
Wie das Archiv die Maschine verließ. Der ZCode-Client fragte bei zcode.z.ai Upload-Credentials an und erhielt eine OSS-Formularsignatur samt einem öffentlichen RSA-Schlüssel; er packte das Arbeitsverzeichnis in ein Tar, verschlüsselte das Archiv lokal mit AES-256-CTR unter einem Schlüssel, der mit jenem öffentlichen Schlüssel eingehüllt war, und schickte das Ergebnis direkt in den Objektspeicher von Alibaba Cloud, vorbei an Z.ais eigenen Anwendungsservern, mit einem OSS-Callback, der dem Backend meldete, dass das Objekt angekommen war. Der passende private Schlüssel erreicht den Client nie, also kann der Nutzer sein eigenes Archiv nicht entschlüsseln. Ein gültiges Login-Token ist der Auslöser, nicht irgendeine Nutzung des Assistenten. Eine Sitzung verzeichnete 62 Capture-Events, und das Löschen der Datei führte innerhalb von etwa dreißig Minuten zu einem neuen Packlauf.

Beide Schalter regelten ihre Seite

ZCode kam mit zwei Einstellungen, die ein sorgfältiger Entwickler gefunden und abgeschaltet hätte: eine für Erfahrungsoptimierung, eine für die Indexierung von Repository-Snapshots. Beide taten etwas Echtes, und keine tat, was ihr Name nahelegt.

Die erste regelte, ob Z.ai befugt war, die Daten zu nutzen. Die zweite regelte, ob der Server den Snapshot indexierte, sobald er angekommen war. Das Packen und der Upload liefen unabhängig davon, weil der Capture-Sidecar startet, sobald der Token-Provider ihm ein gültiges JWT überreicht, ohne irgendeine Nutzereinstellung irgendwo auf dem Weg. Schalten Sie beide Schalter ab, und der Client packte Ihr Repository weiterhin, verschlüsselte es weiterhin, schickte es weiterhin in den Bucket und versprach Diskretion über das, was danach geschah.

Der Bug eines Anbieters, aber die Form wiederholt sich überall. Eine Kontrolle, die in dem Programm wohnt, das sie einschränkt, ist eine Bitte. Sie läuft auf demselben Code, der falsch sein müsste, damit Sie sie brauchen.

Ein Snapshot: 42.411 Dateien, 345 MB vor der Verschlüsselung.git/lfs — 196,1 MB.git/objects — 102,2 MBCode — 46,2 MB56,8 %29,6 %13,4 %86,6 % von allem, was die Maschine verließ, war die Repository-Historie — Objekte, LFS-Cache, ReflogsAllein das Reflog listet jeden Branch, auf dem Sie waren, und jeden Commit, den Sie gemacht und verworfen haben.Globale App-Konfigurationsdateien reisten mit.
345 MB, und nur 46 davon waren Code. Der Snapshot, den ferstar wiederherstellte, enthielt 42.411 Dateien: 196,1 MB Git-LFS-Cache, 102,2 MB Git-Objekte und 0,6 MB Reflogs, 86,6 % der Nutzlast, gegen 46,2 MB echten Quellcode und echte Dokumentation. Ein Werkzeug, das Ihr Arbeitsverzeichnis hochlädt, lädt alles hoch, was Sie in diesem Repository jemals geschrieben haben, einschließlich der Branches, die Sie aufgegeben haben.

Nichts auf dem Draht konnte es lesen

Der Client verschlüsselte jedes Archiv mit AES-256-CTR, bevor es die Maschine verließ, und hüllte den symmetrischen Schlüssel mit einem öffentlichen RSA-OAEP-SHA256-Schlüssel ein, den ihm zcode.z.ai überreichte. Der passende private Schlüssel kommt nie herunter. ferstar konnte sein eigenes Archiv nicht öffnen; die eine Partei, die es konnte, war die mit dem Bucket.

Das schließt jede Verteidigung aus, die Sie besitzen und die dadurch funktioniert, dass sie Bytes liest. Ein Secret-Scanner findet in diesem Tarball keinen AWS-Key, und Deep Packet Inspection sieht eine TLS-Sitzung zu einem Storage-Endpunkt, die Chiffrat in Chiffrat verpackt trägt.

Zwei Fakten überleben: wohin es ging und wie groß es war. Ein 313-MB-POST an einen Objektspeicher-Host in einer Region, in die Ihr Team nicht deployt, fällt auf — vorausgesetzt, jemand notiert, wohin Ihre Werkzeuge sich verbinden und wie viel sie senden. Niemand tat es, und deshalb brauchte es zum Finden Plattenplatz-Archäologie und eine Woche Reverse Engineering statt eines Blicks in ein Log.

Wo der Ausgang liegt

Bromure Agentic Coding setzt den Agenten und seine Toolchain in eine Ubuntu-VM auf Ihrem Mac und gibt dieser VM genau einen Weg ins Internet: einen Man-in-the-Middle-Proxy, der auf dem Host läuft, außerhalb des Guests, wo kein Prozess in der VM ihn umkonfigurieren kann.

Das verlangt von der Software nichts. Der virtuelle Netzwerk-Switch leitet die Port-80- und Port-443-Flüsse der VM transparent in den Host-Proxy um, ohne nötige Umgebungsvariablen und ohne etwas, das der Guest abschalten könnte, standardmäßig aktiv für jeden Workspace. Ein Werkzeug, das HTTPS_PROXY ignoriert, seinen eigenen Socket öffnet und direkt mit einem Storage-Endpunkt spricht, geht trotzdem hindurch, weil das Abfangen im Switch wohnt, an dem der Guest hängt, und nicht im Guest.

Vier Dinge passieren dann mit einem 313-MB-POST, um den niemand gebeten hat.

Jede Anfrage hinterlässt einen Eintrag

Jede Anfrage durch den Proxy hinterlässt einen Trace-Eintrag: Zeitstempel, Host, Port, Methode, Pfad, Status, Latenz und Request-Bytes, gemessen vor jedem Credential-Tausch. Sie müssen kein Archiv entschlüsseln, um einen repository-großen Upload an einen Host zu bemerken, auf den Sie nie deployt haben: Größe und Ziel sind Spalten. Sortieren Sie nach der einen, filtern Sie nach der anderen.

Es verlässt den Mac nie

Die Egress-Firewall pro Workspace ist eine geordnete Regeltabelle (Aktion, Protokoll, Host oder CIDR, Ports, HTTP-Methoden), die mit default allow oder default deny endet. Betreiben Sie einen Workspace im Allowlist-Stil, und der Upload hat keinen Ort, wohin er könnte. Zwei Stellen setzen die Regeln durch: der Switch prüft Ziel-IP und per DNS mitgelesenen Hostnamen, der Proxy prüft den TLS-Servernamen noch einmal, und Ihre Änderungen erreichen laufende Sitzungen sofort.

Jedes Urteil ist eine Zeile

Jede Firewall-Entscheidung landet in der Security Timeline, also zeigt sich ein Werkzeug, das es immer wieder versucht, sagen wir 564 Mal, als wiederholte Ablehnung gegen einen benannten Host und nicht als langsam volllaufende Platte. Das Muster, dessen Rekonstruktion eine Woche dauerte, ist genau das, was dieses Fenster anzeigen soll.

Die mitgepackten Configs sind Attrappen

„Globale App-Konfigurationen“ ist die Wendung, die Sie in ferstars Satz am meisten beunruhigen sollte. Innerhalb eines Bromure-Workspace existieren ~/.git-credentials, ~/.docker/config.json, ~/.kube/config und ~/.aws/config und funktionieren, und jeder Wert darin ist eine strukturerhaltende Fälschung. Keine Datei, keine Umgebungsvariable und kein Prozess in der VM hält jemals einen echten API-Key, ein echtes OAuth-Token, ein echtes AWS-Secret oder einen echten SSH-Private-Key.

Den Rest macht der Zuschnitt. Ein Workspace ist ein Projekt oder eine Credential-Grenze; zwei Workspaces teilen nie eine VM, und das Home des Guests ist eine eigene Image-Datei. Was ein Werkzeug innerhalb eines Workspace zusammenpackt, ist dieser Workspace, und das lässt die anderen vierzig Repositorys auf Ihrem Mac, das ~/.aws/config Ihres Hosts und Ihre SSH-Keys außen vor. Die Keys erreichen überhaupt keinen Guest: das Signieren passiert auf dem Host über eine vsock-Brücke, und die privaten Bytes bleiben dort.

Das Werkzeug auf dem MacHome-Verzeichnis~/work/* : jedes Repo, jedes .git~/.aws/config, ~/.docker, ~/.ssh : echtDas Werkzeug wählt, was es liest und wohin.Das Werkzeug setzt seine eigenen Regeln durch.313 MB, verschlüsselt, an einen BucketKein Eintrag zum Ziel. Kein Eintrag zur Größe.Wie Sie es erfahrenDie Platte läuft voll, und Sie reverse-engineeren.Das Werkzeug im WorkspaceWorkspace-VM — Ubuntu-Guest, ein Projekt/home/ubuntu — dieses Repo, dieser Checkout~/.aws/config, ~/.docker — PlatzhalterDas Werkzeug wählt weiter, was es liest und sendet.Die Route wählt es nicht.:80 und :443 zum Host-Proxy umgeleitetHost · Methode · Pfad · Request-Bytes → Tracedeny web *.aliyuncs.com · default denyAm Draht abgelehnt und in die Security Timeline geschrieben.
Dasselbe Werkzeug, zwei Platzierungen. Lassen Sie es direkt auf dem Mac laufen, und es sitzt neben jedem Repository, das Sie haben, neben jeder Credential-Datei in Ihrem Home-Verzeichnis und neben einer unvermittelten Route ins Internet; der Upload ist eine TLS-Sitzung wie jede andere, und das Archiv ist verschlüsselt, also hilft nichts, was Inhalte liest. Lassen Sie es in einem Bromure-Workspace laufen, und seine Port-80- und Port-443-Flüsse werden in einen host-seitigen Proxy umgeleitet, den es nicht umkonfigurieren kann: der Proxy notiert Ziel und Byte-Zahl, die Egress-Regeln entscheiden, ob die Anfrage überhaupt hinausgeht, und die Konfigurationsdateien, die es einpackte, enthalten Platzhalter, deren echte Werte die VM nie betreten haben.

Was Z.ai danach tat

Z.ai entschuldigte sich an dem Tag, an dem ferstar veröffentlichte. Das Unternehmen schaltete den Upload-Workflow in ZCode v3.14.0 ab, das laut InfoWorld am 19. September ausgeliefert wurde und einen Fix für ein anomales Repository-Upload-Problem auflistete, und holte die Sicherheitsfirma NSFOCUS für ein Audit des Ergebnisses. NSFOCUS bestätigte, dass „alle Datenobjekte im Alibaba-Cloud-OSS-Bucket zcode-prod sowie der Bucket selbst gelöscht wurden“, und Z.ai sagt, nach dem Audit sei „kein funktionaler Pfad identifiziert worden, der die Erzeugung lokaler Repository-Snapshots oder die Übertragung lokaler Dateien nach außen auslösen könnte“, und der Code sei „nie für Modelltraining verwendet worden“. Dann legten sie die Sache offen: Client, Backend, gemeinsame UI, Agent CLI und Runtime, Apache-2.0, auf GitHub. Das übertrifft den Branchenstandard, und es bedeutet, dass der nächste Mensch, der nachsehen will, keinen Disassembler braucht.

Cris Thomas von Semgrep, in der CSO-Berichterstattung zitiert, benannte die Kategorie: „Das ist nicht wirklich ein KI-Modell-Problem, es ist ein altmodisches Problem der Sicherheitsarchitektur.“ Er will, dass Anbieter offenlegen, was die Maschine verlässt, wohin es geht und wie lange es aufbewahrt wird, „mit den minimalen Berechtigungen standardmäßig aktiv, nicht den maximalen“. Kein Kunde kann das von außen erzwingen.

Die Voreinstellung ist das Bedrohungsmodell

Hier hat niemand jemanden angegriffen. Niemand hat ein Credential gestohlen, ein Paket vergiftet oder einem Modell etwas eingeredet. Ein Produkt tat, was seine Autoren es zu tun gebaut hatten, und was sie es zu tun gebaut hatten, war, Ihr Repository irgendwohin zu schicken, wo Sie es nicht lesen konnten. Der Vorfall ist eine Voreinstellung, und die Voreinstellungen wählen Leute, deren Interessen neben Ihren herlaufen.

Das ist das Argument dafür, die Grenze außerhalb des Werkzeugs zu ziehen. Sie müssen nicht glauben, dass Ihr Coding-Assistent feindlich ist; Sie müssen nur bemerken, dass Sie nicht auditieren können, was Sie nicht sehen, und dass der Einstellungsbereich des Anbieters ein seltsamer Ort für den Schalter ist. Lassen Sie den Agenten und seine Toolchain dort laufen, wo jede Verbindung, die sie öffnen, einen Draht kreuzt, der Ihnen gehört, wo der Eintrag über diese Kreuzung auf Ihrem Mac geschrieben wird, und wo die Regel, die einen 313-MB-Upload stoppt, eine Zeile ist, die Sie getippt haben, statt ein Kästchen, das jemand anders respektiert hat.

Dann ist „was schickt mein Coding-Werkzeug eigentlich?“ ein Fenster, das Sie an einem Dienstagnachmittag öffnen, und kein Forschungsprojekt.