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

Es bat darum, ausgeführt zu werden

Sonatype verfolgt 846 bösartige npm-Pakete, veröffentlicht aus Hunderten von Wegwerfkonten unter KI-generierten Namen. Keines davon bringt ein Install-Skript mit. Die README sagt Ihnen, Sie sollen das Paket mit require() laden, und der Import startet den Loader. Abwehrmechanismen, die ein schlechtes Paket am Namen, am Autor oder am deklarierten Hook erkennen, laufen jetzt gegen einen Generator. Bromure Agentic Coding blockiert anhand der einen Eigenschaft, die ein Generator nicht fälschen kann: wie alt das Paket ist.

Die Pakete tragen keinen Install-Hook, npm hat also nichts abzufangen. Die README sagt Ihnen, Sie sollen sie mit require() laden, und der Import startet den Loader. Immer häufiger ist der Leser, der dieser Anweisung folgt, ein Coding-Agent.

Sonatype Research Labs hat am 5. August eine Kampagne offengelegt, die es Flooding Dropper nennt, mit 846 gezählten bösartigen npm-Komponenten und weiter steigender Tendenz. Paul McCarty von OpenSourceMalware führt die Payload als WEL1DROPPER. Die breitere Berichterstattung erschien am 7. August.

Zwei Details heben die Sache von den npm-Lieferkettengeschichten des letzten Monats ab.

Das erste ist, wie die Pakete zu ihren Namen kommen. Der Angreifer generiert sie und betreibt Slopsquatting als Fließband. Wiederkehrende Stämme wie bigops und bnpl rekombinieren sich mit anderen Wörtern zu Namen wie bigops-api und dolyame-boxy-desktop-bnpl-card-gallery, viele davon mit einem 35.x.y-Versionsmuster. Die Veröffentlichung verteilt sich auf Hunderte von Wegwerfkonten mit je einer Handvoll Pakete, es gibt also keinen produktiven Autor, den man sperren könnte.

Kein Install-Skript

Die Pakete deklarieren überhaupt keine Lifecycle-Hooks, npm install führt also nichts aus. McCartys Text sagt es unverblümt: „Die NPM-Pakete verwenden kein preinstall- oder postinstall-Skript. Sie brauchen keines.“

Sie liefern eine gebündelte _helpers.js aus, die beim Import des Moduls läuft, dazu eine README, die dem Leser sagt, er solle es importieren. Laden Sie das Paket mit require(), sagt die Dokumentation, und die Dokumentation hat recht. Die erste Stufe erfasst Betriebssystem und Prozessorarchitektur, holt eine native zweite Stufe per HTTPS von einem von drei Cloudflare-Workers- Hosts und weicht darauf aus, das Binary aus base64-Bruchstücken in DNS-TXT- Records unter wel1.ru zusammenzusetzen, wenn der direkte Abruf scheitert.

Die zweite Stufe variiert je nach Plattform. Linux bekommt ein UPX-gepacktes ELF, das einen Sliver-C2-Implantat absetzt. macOS bekommt Anti-Debugging- Prüfungen, eine VMware-Sonde, ein Beacon-Binary und Persistenz per LaunchAgent. Windows bekommt ETW- und AMSI-Patching, Sandbox-Erkennung, reflektive Ausführung im Speicher und Persistenz über einen Registry-Run- Schlüssel und eine geplante Aufgabe. McCarty und Sonatype lesen die Kampagne beide als Weiterentwicklung von „Moika“, der Dependency-Confusion-Welle vom April, die rund 250 Pakete rund um Umgebungsdiebstahl ausrollte.

Der Angreifer hat den Auslöser vom Paketmanager weg auf den Leser verschoben.

Drei Wege, unerkennbar zu seinder Name ist generiertbigops-api, dolyame-boxy-desktop-bnpl-card-gallery, und ~844 mehreine Deny-List braucht den Namender Generator hat mehr davonder Publisher ist WegwerfwareHunderte Konten, je eineHandvoll Paketeden Autor sperren sperrt vierPakete von achthundertes gibt kein Install-Skriptkein preinstall, kein postinstalldie README sagt: require() es_helpers.js läuft beim Importder Auslöser ist der Leserder Import startet den LoaderOS und Arch erfassen, native Stufe holenCloudflare Workers, oder DNS TXT unter wel1.ru
Die Kampagne ist darauf gebaut, Erkennung auszuhebeln. Der Angreifer generiert die Namen, statt sie zu wählen, wechselt die Publisher über Hunderte von Wegwerfkonten und hat den Ausführungsauslöser vom Lifecycle-Hook auf eine README-Anweisung verlegt, sodass kein Install-Skript zum Prüfen bleibt und kein stabiler Name und kein stabiler Autor, an dem sich eine Deny-List festhalten könnte.

Automatisierung schlägt die Deny-List

Sonatypes eigene Zusammenfassung ist der schärfste Satz der ganzen Berichterstattung:

Lesen Sie das als Aussage über Abwehrmechanismen. Jede Schicht, die Sie schützt, indem sie das schlechte Paket erkennt, ob am Namen, am Veröffentlichungskonto oder am deklarierten Hook, konkurriert jetzt mit einem Generator. Diese Schichten fangen weiterhin, was sie schon gesehen haben. Sie werden daran gemessen, eine bestimmte Zeichenkette zu erkennen, und der Nachschub an bestimmten Zeichenketten kostet einen Skriptlauf.

Der Agenten-Blickwinkel schärft das Ganze. Slopsquatting zahlt sich überhaupt erst aus, weil Sprachmodelle Paketnamen erfinden, die richtig klingen und nicht existieren, und ein Angreifer, der diese Namen registriert, sammelt Installationsverkehr umsonst ein. Nehmen Sie den fehlenden Install-Hook dazu, und Sie haben ein Paket, dessen Auslöser eine geschriebene Anweisung in einer README ist. Ein Coding-Agent ist der Leser, der den Namen halluziniert, ein Paket darunter findet, die Dokumentation liest und tut, was die Dokumentation sagt. Die Kampagne ist an beiden Enden auf den Agenten zugeschnitten.

Wählen Sie eine Eigenschaft, die der Generator nicht fälschen kann.

Alter ist kein Name

Jedes Paket dieser Kampagne teilt eine Eigenschaft, an der keine Automatisierung etwas ändert: es ist neu. Ein Name, den ein Modell letzten Dienstag erfunden hat, kann kein Paket von 2019 unter sich haben. Hunderte von Wegwerfkonten veröffentlichen Hunderte brandneuer Versionen, denn ein Wegwerfkonto hat keine Historie, aus der es veröffentlichen könnte. Das Modell hängt davon ab, dass das Paket da ist, wenn jemand danach greift, also legt es der Angreifer höchstens ein paar Tage vorher an.

Bromure Agentic Coding lässt den Agenten in einer Wegwerf-Linux-VM auf Apple Silicon laufen und leitet jedes Byte seines Netzwerkverkehrs über einen Proxy auf dem Host, außerhalb der Box, in der der Agent läuft. Paketabrufe für npm, PyPI, Cargo, RubyGems, Maven, NuGet, Go-Module und Packagist queren allesamt diesen Proxy, und die erste Richtlinie, auf die sie treffen, ist eine Uhr.

Die Altersschranke, standardmäßig aktiv

Bromure weist Pakete ab, die jünger sind als ein Mindestalter, ab Werk auf zwei Tage gesetzt. Bewegliche Referenzen wie latest oder ein Semver-Bereich lösen auf die neueste Version vor der Grenze auf, sodass gewöhnliche Installationen weiter funktionieren und beim gestrigen Release landen. Eine fest gepinnte Referenz auf eine zu frische Version bekommt einen 451 samt klarer Bromure-Fehlermeldung, die benennt, was passiert ist. Achthundert Pakete zu stoppen kostet so viel wie acht, denn Bromure erkennt keines davon.

Sie greift, bevor die Diskussion beginnt

Der clevere Teil dieser Kampagne, den Lifecycle-Hook wegzulassen und die Ausführung auf require() zu verlegen, beschreibt, was passiert, nachdem der Tarball auf der Platte liegt. Die Altersschranke sitzt am Abruf, im Host-Proxy, oberhalb von alldem. Ob die Payload zur Installationszeit oder zur Importzeit gelaufen wäre, ist eine Frage zu einem Paket, das nie angekommen ist. Die .npmrc und pip.conf in der VM können diese Richtlinie verschärfen und können sie nicht lockern.

Reputation, hinter der Uhr

Mit einem socket.dev-Key prüft Bromure jedes Paket gegen die Datenbank von socket.dev, bevor es den Abruf durchlässt, und blockiert die als kompromittiert, als Malware oder als Typosquat markierten. OSV-Abfragen blockieren Pakete mit bekannten Schwachstellen ab einem Schweregrad Ihrer Wahl. Delpi leitet jede npm-Anfrage komplett auf eine filternde Registry um. Diese Schichten erkennen Dinge, was sie nützlich macht und an die zweite Stelle setzt.

Die README wird als das bewertet, was sie ist

„Lade das mit require()“ ist eine Anweisung, geschrieben von einem Fremden, angekommen in Text, den der Agent liest. Bromure bewertet die Tool-Ausgaben, die der Agent konsumiert, einschließlich Dateiinhalten, Webseiten und abgerufener Dokumentation, mit einem lokalen Prompt-Injection-Modell. Es bewertet die Anweisungsdateien, die der Agent als Autorität behandelt, CLAUDE.md und AGENTS.md und ihre Varianten, mit einem eigenen Klassifikator plus einem Scanner für unsichtbare Unicode-Payloads. Dafür verlässt nichts den Mac.

Die Payload profiliert die falsche Maschine

Nehmen wir an, der Abruf geht trotzdem durch, weil das Paket über die Grenze hinaus gealtert ist oder ein Allowlist-Eintrag es ausnimmt. Verfolgen Sie WEL1DROPPER durch ein Bromure-Profil und sehen Sie zu, wie er seine Arbeit gegen eine Maschine erledigt, die Sie ohnehin wegwerfen wollten.

Der erste Schritt des Loaders ist, den Host zu erfassen. In einem Bromure-Profil ist dieser Host eine Ubuntu-VM hinter NAT, einen Hypervisor von macOS entfernt, also nimmt er den Linux-Zweig und pflanzt dort ein Sliver-Implantat. Den macOS-Zweig fordert er nie an: das Anti-Debugging, die VMware-Sonde, den beacon_mac.bin-Download, den LaunchAgent, der einen Neustart überlebt hätte, das Interesse an russischen Zahlungsdomains. All das ist Code für eine Maschine, die der Dropper nie gesehen hat. Erase home verwirft die Schicht, in der er gelandet ist, und setzt das Home-Verzeichnis auf den Zustand direkt nach dem Klonen zurück.

Dann macht sich die Infostealer-Stufe auf die Suche nach dem, wonach ihr Moika-Vorfahr suchte: der Umgebung. In einem Bromure-Profil enthält die Umgebung brm_…-Platzhalter. Keiner der echten Anthropic-, OpenAI- und xAI-Schlüssel, keines der GitHub- und GitLab-Token, kein AWS-Material und keine Registry-Zugangsdaten liegen im Speicher der VM oder in ihren Dotfiles. Bromure hält sie auf dem Host und setzt sie im Proxy in die Anfrage ein, auf der Leitung, für den einen Zielhost, zu dem jedes gehört. Der Diebstahl läuft vollständig durch und verschickt Zeichenketten, die auf genau einem Mac auflösen, und der ist nicht die Maschine, die den Code ausgeführt hat.

Auf dem Entwickler-Maces erfasst den HostmacOS, arm64: nimm den Mac-Zweigbeacon_mac.bin, LaunchAgent, neustartfestes liest die UmgebungAPI-Keys, Git-Credentials, Cloud-Materialalles echt, alles nutzbarund es bleibtdie Maschine, die es übernimmt, ist IhreIn einem Bromure-Profilder Abruf trifft zuerst eine Uhrheute veröffentlicht, Grenze zwei Tage: 451kein Name getroffen, keine Signatur nötigund falls er je ankommtLinux in einer VM, Mac-Zweig ungenutztdie Umgebung hält brm_-Platzhalterund es bleibt nichtErase home setzt die Schicht zurück
Der Dropper wählt seine Payload nach dem Host, den er vorfindet. In einem Bromure-Profil ist dieser Host eine Wegwerf-Linux-VM, also fordert er nie den macOS-Zweig mit seiner LaunchAgent-Persistenz, seiner VMware-Sonde und seinem Beacon-Binary an, und die Infostealer-Stufe liest Platzhalter-Zugangsdaten, deren echte Werte nie in die VM gelangt sind.

Die empfohlene Jagd, längst im Gange

McCarty schließt den WEL1DROPPER-Text mit defensiven Ratschlägen: nach DNS-TXT-Anfragen an wel1.ru jagen, Proxy-Logs auf Verkehr zu den Cloudflare-Workers-Hosts durchsehen und nach verwaisten temporären Dateien und LaunchAgents suchen.

Lesen Sie den zweiten Punkt noch einmal. Ihr Laptop führt kein Proxy-Log. Ein Bromure-Profil führt eines von Bauart wegen: jede Anfrage, die die VM stellt, quert den Host-Proxy, und das Security Log und der Trace Inspector halten Host, Status, Latenz und Substitutionsbericht für jede fest. Drei unbekannte *.workers.dev-Ziele, die während eines Builds auftauchen, sind drei Zeilen auf einer Liste, die Sie lesen können, auf der Maschine, auf der der Build lief, ohne dafür irgendetwas ausrollen zu müssen.

Registries werden diese Pakete weiter entfernen, und Takedowns wirken: 846 davon sind weg oder auf dem Weg dorthin. Aber die Kampagne ist längst über tausend hinaus, veröffentlicht aus Konten, die nichts kosten, unter Namen, die nichts kosten, und die nächste Welle ist einen Skriptlauf entfernt. Erkennung ist der falsche Wettbewerb.

Ein zwei Tage altes Paket verdient kein tiefes Vertrauen, das einem zwei Stunden alten fehlt. Es liegt außerhalb des Fensters, in dem diese Ökonomie arbeitet, und Sie können Ihren Abhängigkeitsgraphen mit einer Einstellung, die schon aktiv ist, hinter dieses Fenster schieben.

Installieren Sie Bromure Agentic Coding, und lassen Sie den Abruf auf eine Uhr treffen, bevor er auf Ihre Maschine trifft.