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

Die Wordlist kannte Ihr Home-Verzeichnis bereits

F5 Labs zählte in einem Monat 807 Angriffe und 32.000 Ereignisse gegen exponierte Vite-Entwicklungsserver, mit einem Query-String-Trick, der an Vites eigener Deny-List vorbeiläuft. Die Scanner suchen nicht Ihre Anwendung. Sie fragen nach /home/ubuntu/.aws/credentials mit absolutem Pfad, und nach /proc/self/environ, und nach terraform.tfstate. In einem Workspace von Bromure Agentic Coding zeigt dieser Port auf einen privaten Switch, und die Datei, die die Wordlist will, enthält keinen Schlüssel.

Dieser Angreifer schickt Ihnen kein Paket und kein Archiv. Ihr eigener Entwicklungsserver lauscht, und ein Fremder fragt ihn nach Dateien mit Namen. Welche Maschine antwortet, entscheidet darüber, was er bekommt.

Sie sagen dem Agenten, er soll das Frontend zum Laufen bringen, damit Sie es ansehen können. Er tippt npm run dev, bindet den Port, lädt die Seite, und Sie gehen zurück zum Diff. Irgendwo in dieser Schleife, in einer docker-compose.yml oder in einem --host-Flag, das Sie vor Wochen ergänzt haben, damit die Vorschau vom Handy aus erreichbar ist, hat sich der Server an alle Interfaces gebunden statt an Loopback. Zwölf Minuten später fragt etwas, das sich Googlebot nennt, Ihren Entwicklungsserver nach /@fs/../.env?raw?? und bekommt ein HTTP 200.

F5 Labs veröffentlichte die Zahlen am 11. September. Über einen einzigen Monat Sensordaten hinweg, den August 2026, verzeichneten ihre Honeypots 807 nach Sitzung gruppierte Angriffe und rund 32.000 Rohereignisse gegen exponierte Vite-Entwicklungsserver, gegenüber einer Grundlinie von 1.732 Ereignissen in den drei Monaten davor. Das ist das Achtzehnfache des Verkehrs, von Monat zu Monat, wegen eines einzigen Fehlers. BleepingComputer berichtete am 14. September darüber.

Ein Query-String, der an der Deny-List vorbeigeht

Vite liefert Dateien aus dem Dateisystem des Hosts über eine interne Route namens /@fs/ aus, über die der Entwicklungsserver Ihrem Editor ein Modul reicht, das außerhalb des Projektstamms liegt. Weil diese Route überallhin reichen kann, liefert Vite eine Deny-List mit, server.fs.deny, um die offensichtlichen Ziele zu blockieren: .env-Dateien, Zertifikate, privaten Quellcode.

CVE-2026-39364, veröffentlicht am 7. April, lässt einen Angreifer diese Deny-List überspringen, indem er die Anfrage mit einem Query-String schmückt. F5 beschreibt den Mechanismus:

Der Server verarbeitet die Anfrage, normalisiert den Pfad und entfernt oder missdeutet den Query-String während der Zugriffsprüfung, sodass die Prüfung server.fs.deny nicht ausgelöst wird.

Der Server erzwingt die Deny-List-Filterung nicht und liefert die Zieldatei mit einer HTTP-200-Antwort aus.

Der Fehler betrifft Vite 7.1.0 bis 7.3.2 sowie 8.x vor 8.0.5. F5 fing diese Anfrageformen in freier Wildbahn ab, jede kurz genug für eine Zeile: GET /@fs/.env?raw??, GET /@fs/../.env?raw??, GET /@fs/..%252f..%252f..%252f..%252froot/.env?raw?? und GET /@fs/..%252f..%252f..%252f..%252fproc/self/environ?raw??. Andere Varianten im selben Werkzeugkasten nutzen ?import&raw, ?import&url&inline, ?inline&import und ?raw?import.

Diese Parametertricks tragen eigene CVE-Nummern: CVE-2025-30208, CVE-2025-31125 und CVE-2024-45811, drei ältere @fs-Umgehungen, die noch immer in denselben Scannern geladen sind. Wer die Scan-Flotte betreibt, hat nicht für einen neuen Fehler umgerüstet. Er hat einer Datei, die er schon hatte, eine weitere Zeile angehängt.

eine Anfrage, eine DateiScannerGET /@fs/../.env?raw??User-Agent: Googlebot/2.1vite-EntwicklungsserverPfad zum Ausliefern normalisiert/@fs/ → Lesen von der Platteserver.fs.denyQuery-String falsch behandelt, Prüfung fällt ausDatei gelesen und geliefertkeine Auth, keine Sitzung, keine LogzeileHTTP 200der Inhalt der Datei,im Rumpf der Antwortschon in denselben ScannernCVE-2025-30208 · ?raw??CVE-2025-31125 · ?inline&importCVE-2024-45811 · ?import&raw
Die Umgehung in einem einzigen Austausch. Vites Route /@fs/ soll Projektdateien ausliefern, und server.fs.deny soll sie von Geheimnissen fernhalten. Hängt man einen Query-String an, dessen verbreitete Form ?raw?? ist, wird der Pfad zum Ausliefern normalisiert, bei der Zugriffsprüfung aber falsch behandelt, also bleibt die Deny-List still und die Datei kommt mit einer 200 zurück. Path Traversal in derselben Anfrage reicht aus dem Projekt hinaus, und so liest ein Frontend-Entwicklungsserver am Ende /proc/self/environ.

Die Wordlist ist eine Karte der Maschine einer Entwicklerin

Lesen Sie, wonach die Scanner fragen, und achten Sie darauf, wie wenig davon Ihre Anwendung betrifft.

Sie fragen nach .env, .env.local, .env.production, .env.development und .env.staging. Sie fragen nach terraform.tfstate, terraform.tfvars, .terraform/terraform.tfstate, serverless.yml und .serverless/serverless-state.json. Sie fragen nach .azure/credentials und .azure/accessTokens.json. Sie fragen nach /etc/passwd, /proc/1/environ, /proc/self/cwd/.env und /proc/self/environ. Dieser letzte Pfad enthält den Umgebungsblock des Entwicklungsserver-Prozesses selbst, dort wo ein von Ihrer Shell exportierter API_KEY landet.

Und dann fragen sie nach AWS-Zugangsdaten, indem sie die Liste der Home-Verzeichnisse durchgehen, unter denen der Prozess einer Entwicklerin laufen könnte:

/root/.aws/credentials, /home/ec2-user/.aws/credentials, /home/ubuntu/.aws/credentials, /home/node/.aws/credentials, /home/www-data/.aws/credentials, /home/admin/.aws/credentials, /home/debian/.aws/credentials, /var/www/.aws/credentials, /usr/src/app/.aws/credentials, /app/.aws/credentials. Dann .aws/config, .aws/credentials.backup, .aws/credentials.bak, .aws/sso/cache/, rootkey.csv, aws-exports.js und amplifyconfiguration.json.

Diese Pfade inventarisieren eine Entwicklermaschine, aufgezählt nach Benutzernamen, und Vite ist nur die Tür. Der Fehler ist nebensächlich; drei ältere Fehler in derselben Route fahren in denselben Anfragen mit. Die Betreiber wetten darauf, dass ein Prozess, der auf einer routbaren Adresse lauscht, unter einem Benutzer läuft, in dessen Home-Verzeichnis echte Schlüssel liegen.

Der Verkehr ist so gekleidet, dass er einen flüchtigen Blick in die Logs übersteht. F5 verzeichnete gefälschte User-Agent-Header im Wechsel zwischen Googlebot/2.1, ClaudeBot/1.0, GPTBot/1.4, PerplexityBot/1.0, OAI-SearchBot/1.3 und Amazonbot/0.1, dazu gefälschte Werte für X-Forwarded-For und X-Real-IP, um an IP-Allow-Lists vorbeizukommen. Die Quellen liegen in den Bereichen 34.x und 35.x der Google Cloud über mehrere Regionen verteilt, angeführt von den Vereinigten Staaten mit 17.297 Ereignissen, dann Belgien mit 4.407 und den Niederlanden mit 4.011. In diesem Monat ist eine Zeile in Ihrem Zugriffslog, die von sich behauptet, ein KI-Crawler zu sein, ein schwacher Beleg dafür, dass ein Crawler sie geschickt hat.

Zwei von F5s Empfehlungen sind Architekturfragen

F5 schließt mit fünf Ratschlägen. Drei sind gewöhnlich und richtig: auf 7.3.2 oder 8.0.5 aktualisieren, /@fs/ am Rand filtern, Crawler per Reverse DNS prüfen statt dem Header zu glauben. Die anderen beiden beschreiben eine Haltung, die man halten muss, und keine Aufgabe, die man abschließt.

Stellen Sie sicher, dass Entwicklungsserver nicht an externe Interfaces binden. Prüfen Sie Docker-compose-Konfigurationen, Kubernetes-Ingress-Regeln und Cloud-Sicherheitsgruppen.

Rotieren Sie exponierte Geheimnisse: Wenn ein ungepatchter Vite-Entwicklungsserver im August 2026 aus externen Netzen erreichbar war, behandeln Sie lokale .env-Variablen, AWS-Zugangsdaten, Azure-Access-Token und Terraform-State-Dateien als potenziell kompromittiert.

Die erste verlangt, dass Sie ein Versprechen über jeden Port halten, in jeder Compose-Datei, über jeden Branch hinweg, solange das Projekt lebt, in einem Moment, in dem das, was npm run dev tippt, oft ein Agent ist und nicht Sie. Die zweite fragt, was Sie danach tun, und antwortet: rotieren Sie, was die Maschine sehen konnte.

Beides wird leichter, wenn der Port anderswo aufgeht und die Dateien, die die Wordlist nennt, nichts enthalten, das eine Rotation wert wäre.

Wo ein Bromure-Workspace den Port hinlegt

Bromure Agentic Coding führt Coding-Agenten in einer hardwarevirtualisierten Linux-VM auf Ihrem Mac aus, mit den Sicherheitskontrollen auf der Host-Seite dieser Grenze. Zwei davon antworten auf diese Kampagne.

Der Entwicklungsserver bindet innerhalb eines privaten Switch. Im Standard-NAT-Modus hängt jede Workspace-VM an einem prozessweiten Software-L2-Switch, gemultiplext auf eine einzige vmnet-Schnittstelle, in einem privaten Subnetz. Dieses Subnetz ist 192.168.64.0/24, sofern Ihr eigenes LAN es nicht schon benutzt, Bromure betreibt darauf einen eigenen DHCP-Server, und jeder Workspace behält eine deterministische MAC und ein stabiles Lease. Das Handbuch nennt die Konsequenz: Ihr Mac erreicht die VMs, Ihr physisches LAN sieht sie nicht, und eingehende Verbindungen von anderswoher sind unmöglich, solange Sie nicht selbst einen Dienst veröffentlichen. Ein Agent, der in dieser VM an 0.0.0.0 bindet, hat an jede Schnittstelle gebunden, die ihm gehört, und jede davon zeigt auf einen Switch, der auf Ihrem Laptop beginnt und endet. Sie haben keine Sicherheitsgruppe zu prüfen, weil Sie keine Route hinein zu sichern haben.

Sie sehen trotzdem, was lauscht, und es ist eine Liste, kein Audit. Das Workspace-Dashboard trägt eine Listening-Ports-Karte, die jeden von außen erreichbaren Socket im Gast als Endpunkt <VM-IP>:<Port> mit Kopieren per Klick zeigt. Das macht aus F5s Empfehlung Nr. 2 eine Karte, auf die Sie kurz blicken, alle anderthalb Sekunden aus dem Gast aufgefrischt, statt eines Audits, das Sie terminieren. Wenn Sie doch einmal wollen, dass die Welt eine Vorschau sieht, bekommt ein wahrscheinlich-HTTP-Dienst einen Globus-Knopf, der genau diesen einen Dienst über einen Cloudflare Quick Tunnel veröffentlicht, hinter einem einmaligen Einwilligungsdialog. Sie geben einen Dienst frei, indem Sie ihn anklicken, Dienst für Dienst, auf dem Host, statt ein Flag in einer Compose-Datei am Leben zu lassen.

Der Grund, aus dem Sie ursprünglich zu --host gegriffen haben, bleibt Ihnen erhalten. Der wegwerfbare Chromium-Sidecar teilt sich dasselbe L2-Segment mit der Workspace-VM, sodass der eingebettete Browser den Entwicklungsserver des Agenten unter der Adresse der VM lädt. Nicht unter localhost, denn der Browser läuft auf einer eigenen Maschine.

Wenn andere Maschinen die VM wirklich erreichen müssen, hängt der Bridged-Modus sie an Ihr physisches LAN. Das stellen Sie pro Workspace im Workspace-Editor ein, auf dem Host, und es fällt auf NAT zurück, wenn die Schnittstelle beim Start nicht verfügbar ist. Sie treffen diese Wahl in einem Panel, nicht in einer Konfigurationsdatei, die der Agent bearbeiten kann.

eine exponierte Dev-Maschineeine routbare Adresse, ein echter Benutzer, echte DateienGET /@fs/../.env?raw??kommt aus 34.x an, 200 OK/home/ubuntu/.aws/credentialseine Access Key ID und ein lebendes Secret/proc/self/environjedes Token, das die Shell exportiert hatdanachSchlüssel, Token und State-Datei rotieren,und das Zeitfenster ratenin einem Workspaceein privater Switch, ein Home voller Platzhalterkeine eingehende Route zum Ankommenvmnet NAT · 192.168.64.0/24Listening-Ports-Kartejeder offene Socket, und ein Globus zum Freigebendie Wordlist landet auf Fälschungensk-ant-api03-brm-… · ghp_… · credential_processdanachdie Platte zurücksetzen, wenn Sie mögen,und nichts rotieren
Derselbe Scan, gegen zwei Maschinen. Auf einer Entwicklungsmaschine mit routbarer Adresse erreicht die Anfrage einen Prozess, der als echter Benutzer läuft, und jeder Pfad der Wordlist löst sich in eine echte Datei auf: .env mit lebenden Schlüsseln, ~/.aws/credentials mit einem Secret, /proc/self/environ mit den von der Shell exportierten Token. In einem Workspace von Bromure Agentic Coding zeigt der Port auf einen privaten vmnet-Switch ohne eingehende Route. Veröffentlichen Sie den Dienst absichtlich, landet die Wordlist trotzdem auf Platzhaltern und auf einer ~/.aws/config, die einen Helfer nennt statt einen Schlüssel zu enthalten.

Und wenn Sie den Dienst absichtlich veröffentlichen

Manchmal wollen Sie die Vorschau tatsächlich im Internet haben, für eine Kundin oder eine Kollegin. Also klicken Sie auf den Globus, öffnen den Tunnel und lassen die Scanner sie finden. Gehen Sie die Wordlist durch, Eintrag für Eintrag, und sehen Sie sich an, was zurückkommt.

.env und /proc/self/environ liefern die Umgebung des Workspace, und jede Zugangsberechtigung darin ist eine Fälschung. Bromure leitet jeden Platzhalter aus dem echten Wert plus einem installationseigenen 32-Byte-Salt über HKDF-SHA256 ab und behält dabei die Form, die ein clientseitiger Validator erwartet: ein Anthropic-Schlüssel liest sich als sk-ant-api03-brm-…, ein GitHub-Token ist ghp_ plus 36 Zeichen, GitLab ist glpat- plus 20. Bromure schreibt diese Fälschungen in die Umgebungsvariablen und in ~/.git-credentials, ~/.docker/config.json, ~/.kube/config und die MCP-Konfigurationen. Die echten Werte betreten die VM nie; sie bleiben verschlüsselt auf dem Mac, und ein Proxy auf der Host-Seite tauscht jeden davon im letzten Moment auf die Leitung, jeweils begrenzt auf den Zielhost, zu dem er gehört.

/home/ubuntu/.aws/credentials ist der schärfste Eintrag, denn /home/ubuntu ist das Home-Verzeichnis einer Bromure-Workspace-VM. Der Scanner rät richtig, unter welchem Benutzer der Server läuft. Die Datei ist trotzdem nicht da. Bromures AWS-Einrichtung schreibt gar keine Credentials-Datei; sie schreibt ~/.aws/config mit einer credential_process-Zeile, die auf einen Helfer zeigt, der über einen Host-Socket Ihre echte Access Key ID zusammen mit einem vierzigstelligen gefälschten Secret Key ausgibt und das Session-Token weglässt. Die AWS-SDKs, die aws-CLI, terraform und boto3 nehmen das alle von selbst auf. Ein Scanner, der die Konfigurationsdatei liest, bekommt den Pfad eines Helfers, den er nicht aufrufen kann.

Nehmen Sie das gefälschte Secret trotzdem, haben Sie etwas Totes genommen. Der Gast signiert seine Anfragen mit dieser Fälschung, und der Host entfernt die Signatur und signiert auf dem Weg nach draußen mit dem echten Schlüssel neu. Eine Anfrage, die AWS auf irgendeinem anderen Weg erreicht, scheitert mit InvalidSignatureException. F5s letzte Empfehlung lautet, jedes lokale Geheimnis als kompromittiert zu behandeln und es zu rotieren. Die entsprechende Zeile in Bromures eigener Dokumentation liest sich andersherum: weil nur die Fälschung abgeflossen ist, muss die echte Zugangsberechtigung nie rotiert werden.

Ein Eintrag der Wordlist nennt eine Datei, die echtes Geld wert ist: terraform.tfstate. State-Dateien liegen dort, wo das Repository liegt, also entscheidet Ihre Liste geteilter Ordner über ihre Exposition. Host-Ordner hängen sich als virtiofs-Mounts unter /home/ubuntu/<basename> an einen Workspace, gedeckelt bei acht pro Workspace, und sie sind der einzige Teil des Dateisystems Ihres Mac, den die VM erreicht. Teilen Sie das Repository, an dem der Agent arbeitet, und sonst nichts, dann findet das Traversal ein Verzeichnis statt einer Platte.

Die meisten Entwickler-Sicherheitsgeschichten dieses Jahres beschreiben etwas, das ankommt: ein Paket, ein Archiv, eine Dokumentationsdatei. Hier kommt nichts an. Stattdessen bitten 32.000 Anfragen im Monat einen Prozess, den Sie gestartet haben, einem Fremden Dateien vorzulesen, und der Ausgang hängt daran, welche Maschine diesen Prozess ausführt und was in ihrem Home-Verzeichnis liegt.

Setzen Sie den Entwicklungsserver des Agenten auf einen Switch, auf dem nur Ihr Mac ist, und füllen Sie sein Home-Verzeichnis mit Platzhaltern. Bromure Agentic Coding installieren, und dann lassen Sie die Scanner fragen.