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