W czerwcu 2026 roku badacze odkryli kolejną odsłonę rodziny malware Mini Shai-Hulud, nazwaną Miasma. W odróżnieniu od klasycznego złośliwego oprogramowania w npm, które uderza w garść pakietów, ta kampania pokazała, jak przejęcie jednego elementu infrastruktury deweloperskiej może błyskawicznie rozlać się na wiele ekosystemów: npm, GitHub Actions, a nawet moduły Go.
Atakujący nie celowali w użytkowników końcowych. Celowali w publikujących, w pipeline’y CI/CD oraz w mechanizmy zaufania, które pozwalają oprogramowaniu przejść z laptopa dewelopera na produkcję bez ponownej weryfikacji po drodze. Dla żadnego z tych elementów nie istnieje CVE, ponieważ dotknięty kod nigdy nie był wadliwy. Punktem wejścia było skradzione poświadczenie, nie podatność.
Jedną rzecz warto zaznaczyć na wstępie: 12 maja 2026 roku grupa, która pierwotnie stała za tą rodziną robaków, TeamPCP, publicznie opublikowała na GitHubie pełny kod źródłowy Shai-Hulud/Mini Shai-Hulud na licencji MIT. To zmienia sposób, w jaki należy czytać tę historię. Cztery kolejne fale korzystają z tego samego zestawu narzędzi i technik, ale wspólne narzędzia nie oznaczają już wspólnego operatora. Gdy framework jest publiczny, każdy technicznie kompetentny aktor może przeprowadzić kampanię wyglądającą identycznie jak działania samego TeamPCP. To, co następuje, należy czytać jako pokrywające się taktyki, techniki i procedury (TTP), a nie jako potwierdzoną atrybucję do jednej grupy.
Dotknięte pakiety (npm i Go)
| Pakiet | Zainfekowana wersja |
|---|---|
hexo-deployer-wrangler | 1.0.4 |
hexo-shoka-swiper | 0.1.10 |
leo-auth | 4.0.6 |
leo-aws | 2.0.4 |
leo-cache | 1.0.2 |
leo-cdk-lib | 0.0.2 |
leo-cli | 3.0.3 |
leo-config | 1.1.1 |
leo-connector-elasticsearch | 2.0.6 |
leo-connector-mongo | 3.0.8 |
leo-connector-mysql | 3.0.3 |
leo-connector-oracle | 2.0.1 |
leo-connector-redshift | 3.0.6 |
leo-cron | 2.0.2 |
leo-logger | 1.0.8 |
leo-sdk | 6.0.19 |
leo-streams | 2.0.1 |
prism-silq | 1.0.1 |
rstreams-metrics | 2.0.2 |
rstreams-shard-util | 1.0.1 |
serverless-convention | 2.0.4 |
serverless-leo | 3.0.14 |
solo-nav | 1.0.1 |
github.com/verana-labs/verana-blockchain (Go) | v0.10.1-dev.20 |
Socket kontynuuje dochodzenie; nowe ustalenia są na bieżąco dodawane na ich stronie śledzącej kampanię Miasma.
Jeśli w Twoim drzewie zależności znajduje się którykolwiek z tych pakietów w wymienionych wersjach (lub pakiety z wcześniejszych fal tej samej kampanii z początku czerwca), traktuj środowisko jako potencjalnie skompromitowane i wykonaj natychmiastowe kroki naprawcze opisane poniżej.
Oś czasu
24 czerwca 2026
↓
Przejęcie konta maintainera LeoPlatform
↓
Publikacja 23 złośliwych wersji pakietów (w odstępie sekund)
↓
Payload w binding.gyp wykonuje się podczas instalacji
↓
Kradzież poświadczeń deweloperów i CI/CD
↓
Wdrożenie workflow GitHub Actions („Run Copilot”)
↓
Eksfiltracja skradzionych sekretów do publicznych repozytoriów GitHub
↓
Przejęcie modułu Go (Verana Blockchain) pokrewną techniką
↓
Badacze z Socket, Wiz, Sonatype i innych firm identyfikują i ujawniają kampanię
To już czwarta fala tej samej rodziny kampanii w niecały miesiąc: najpierw pojawiła się 1 czerwca w przestrzeni nazw npm należącej do Red Hata, 3 czerwca nastąpiła fala 57 pakietów, a 24–25 czerwca ta fala LeoPlatform/Go. Jak wspomniano wyżej, TeamPCP otworzyło kod bazowego frameworka robaka 12 maja, więc określenie „ta sama rodzina kampanii” odzwierciedla wspólne TTP tych fal, a nie potwierdzony dowód, że za wszystkimi czterema stoi jeden operator.
Co tak naprawdę się wydarzyło
- Maintainerowi ekosystemu npm LeoPlatform skradziono poświadczenia do konta lub token npm, najprawdopodobniej za pomocą infostealera.
- Atakujący wykorzystał ten skradziony dostęp do publikacji strojanizowanych wersji pakietów, które maintainer już legalnie posiadał. Nic nie trzeba było „hakować” w tradycyjnym sensie, ponieważ atakujący dysponował już zaufaną tożsamością publikującego.
- Każdy, kto zainstalował jedną z tych wersji, uruchomił ukryty payload, który kradł jego własne poświadczenia.
- Część skradzionych poświadczeń należała do środowisk GitHub Actions, co pozwoliło atakującemu podłożyć złośliwy workflow i wykradać sekrety CI/CD bezpośrednio z runnera.
- Niezależnie od tego przejęto moduł Go za pomocą pokrewnej, choć odrębnej techniki, co pokazuje, że kampania nie jest przywiązana do jednego języka ani menedżera pakietów.
- Całość wykryto dlatego, że kilka niezależnych zespołów badawczych obserwowało dokładnie ten wzorzec znany z wcześniejszych fal tej samej kampanii.
Analiza techniczna
Etap 1: przejęty maintainer
Konto npm czirker, powiązane z LeoPlatform, zostało przejęte, najprawdopodobniej przez wyciek poświadczeń lub token wykradziony przez infostealera działającego już na maszynie maintainera.
Etap 2: publikacja złośliwych pakietów
Za pomocą skradzionego tokenu atakujący opublikował 23 złośliwe wersje pakietów w ekosystemach LeoPlatform i RStreams w oknie sześciu sekund. Taka szybkość wskazuje na skryptowane, zautomatyzowane narzędzie do publikacji, a nie na człowieka ręcznie wgrywającego pliki.
Najbardziej konkretny szczegół całej kampanii kryje się właśnie w tym etapie: atakujący nie tylko opublikował złośliwe wersje, ale też przestawił dist-tag latest pakietu leo-sdk na zatrutą wersję 6.0.19, podczas gdy czyste wydanie 7.1.21 pozostało dostępne wyłącznie pod trudniejszym do znalezienia tagiem awsv3. W praktyce oznacza to, że każde środowisko, które wykonało npm install leo-sdk bez jawnego przypięcia wersji semver, było aktywnie kierowane w stronę robaka, mimo że czyste wydanie istniało przez cały ten czas. Praktyczny wniosek jest brutalnie prosty: to przypięcie wersji, a nie tagu, faktycznie decydowało o tym, czy instalacja była czysta, czy zakończyła się kompromitacją.
Etap 3: wykonanie podczas instalacji
To jest sprytna część. Większość złośliwych pakietów npm zostaje wykryta, ponieważ skanery obserwują pola skryptów preinstall i postinstall w package.json. Miasma całkowicie je omija i zamiast tego dodaje plik binding.gyp, czyli konfigurację, której Node.js używa do kompilowania modułów natywnych podczas instalacji. Ponieważ binding.gyp jest zupełnie normalnym elementem budowania natywnych dodatków, uruchamia kod podczas npm install, nie dotykając pól, na które patrzy większość narzędzi.
Ta technika miała dwa realne środki zaradcze i warto być precyzyjnym co do obu. Pierwszy istniał już wcześniej: npm install --ignore-scripts faktycznie wyłącza niejawny hook node-gyp rebuild, który wyzwala plik binding.gyp, jak podaje własna dokumentacja npm. Samo w sobie nie jest to pełna obrona, ponieważ złośliwy pakiet nadal może zostać pobrany i rozpakowany, a późniejsze przebudowanie bez tej flagi wciąż może go uruchomić, ale ta konkretna ścieżka wykonania zostaje zablokowana. Drugi środek zaradczy pojawił się o trzy tygodnie za późno: npm v12, wydany w lipcu 2026 roku, domyślnie zamyka ten wektor, traktując niejawny node-gyp rebuild wyzwalany przez binding.gyp dokładnie tak jak każdy inny skrypt instalacyjny, czyli blokując go, o ile pakiet nie znajduje się jawnie na liście dozwolonych. Technika „Phantom Gyp” użyta w Miasmie była w istocie bezpośrednim wykorzystaniem dokładnie tej luki, którą npm v12 miał zamknąć.
Etap 4: zbieranie sekretów
Po uruchomieniu payload zbiera szeroki zestaw poświadczeń, w tym:
- tokeny npm i PyPI
- tokeny GitHub Personal Access Token i tokeny GitHub OIDC
- poświadczenia AWS, Azure i GCP
- klucze SSH
- ścieżki konfiguracji i poświadczeń Claude Code, Cursora i innych asystentów AI do programowania
- klucze API OpenAI
- tokeny Slack i Twilio, pliki uwierzytelniania Dockera, konfiguracje Kubernetes, dane Vault oraz historię powłoki
Etap 5: nadużycie GitHuba
Malware podrzuca workflow GitHub Actions (widziany pod nazwą „Run Copilot”), który podczas wykonywania wyciąga sekrety bezpośrednio z pamięci runnera CI. Skradzione dane są szyfrowane i wypychane do nowo utworzonych publicznych repozytoriów GitHub pełniących rolę skrzynek kontaktowych; w chwili pisania tego tekstu ponad 550 repozytoriów nosi znacznik eksfiltracji tej kampanii. W powiązanym incydencie tego samego dnia atakujący wykonał force-push złośliwego commita do szeroko używanej akcji GitHub Action i przekierował na niego tagi wersji, co oznacza, że każdy workflow uruchomiony po tym momencie względem tych tagów wykonywał payload atakującego bezpośrednio w runnerze GitHub Actions.
Etap 6: propagacja
Przejęcie maszyny dewelopera
↓
Kradzież poświadczeń GitHub dewelopera
↓
Zebranie sekretów pipeline’u CI/CD z runnera
↓
Użycie skradzionego tokenu npm do publikacji kolejnych złośliwych pakietów
↓
Kolejni deweloperzy instalują zainfekowane pakiety
↓
(powtórka)
Każda udana kompromitacja staje się trampoliną dla następnej. To właśnie czyni z tego samopropagującą się kampanię, a nie pojedynczy incydent.
Dlaczego ten atak jest inny
| Dawne ataki na łańcuch dostaw | Miasma |
|---|---|
| Pojedynczy pakiet | Cały ekosystem |
| Kradzież kryptowalut | Szeroka kradzież poświadczeń |
| Jeden język | Wiele języków (npm, teraz Go) |
| Jeden maintainer | Infrastruktura deweloperska jako całość |
| Statyczny malware | Samopropagujący się, ewoluujący między falami |
Wcześniejsze incydenty w łańcuchu dostaw npm były w większości szybkimi skokami: przejąć pakiet, wykraść klucze do portfeli lub tokeny sesji, iść dalej. Miasma zachowuje się raczej jak robak z modelem biznesowym. Każdą udaną infekcję traktuje jako paliwo dla następnej fali i, jak widać, wciąż ewoluuje po ponad trzech tygodniach i czterech odrębnych falach.
Dlaczego GitHub Actions stał się celem
GitHub Actions zajmuje wyjątkowo cenne miejsce w łańcuchu dostaw oprogramowania: przechowuje tokeny OIDC używane do zaufanej, bezpoświadczeniowej publikacji u dostawców chmury i w rejestrach, trzyma długowieczne sekrety CI i z założenia działa w sposób zautomatyzowany i bezobsługowy. Przejęcie runnera CI nie odsłania tylko laptopa jednego dewelopera, odsłania każdy pipeline wdrożeniowy, każdą publikację do rejestru i każde poświadczenie chmurowe, którego ten workflow dotyka. To znacznie większy promień rażenia niż lokalne środowisko pojedynczego dewelopera i właśnie dlatego kampania zbudowała dedykowane narzędzia wymierzone konkretnie w ten cel.
Dlaczego ekspansja na Go ma znaczenie
Większość relacji o tej kampanii skupiała się na npm, bo tam znajduje się większość pakietów. Ale przejęcie modułu Go jest prawdopodobnie najważniejszym szczegółem całej historii i zarazem tym, który większość opracowań pomija.
Próbka Go nie używa binding.gyp. Nie opiera się w ogóle na mechanizmach rozwiązywania modułów ani budowania w Go. Zamiast tego deweloper, który po prostu sklonuje repozytorium albo otworzy je w zaufanym IDE lub asystencie AI do programowania, może uruchomić payload wyłącznie przez konfigurację projektu.
To mówi coś istotnego: atakujących nie interesuje w gruncie rzeczy JavaScript, npm ani mechanika żadnego konkretnego menedżera pakietów. Interesują ich deweloperzy, wszędzie tam, gdzie deweloperzy ufają zautomatyzowanym narzędziom, czy będzie to skrypt instalacyjny, krok budowania, czy IDE otwierające folder. Język nie ma znaczenia. Poświadczenia deweloperów mają.
Szersza lekcja
Stare myślenie:
Skanujemy zależności
↓
Łatamy znane CVE
↓
Jesteśmy bezpieczni
Współczesna rzeczywistość:
Deweloper zostaje przejęty
↓
CI/CD zostaje przejęte
↓
Legalny pakiet zostaje opublikowany ze złośliwym kodem
↓
Każdy konsument w dół łańcucha go instaluje
↓
Na żadnym etapie nie jest potrzebne CVE
Skanowanie zależności odpowiada na pytanie „czy ten kod ma znaną wadę”. Miasma odsłania zupełnie inne pytanie, na które większość programów bezpieczeństwa w ogóle nie jest przygotowana odpowiedzieć: „czy ufam ludziom i zautomatyzowanym systemom, którym wolno publikować kod do mojego środowiska, teraz, dzisiaj”.
Możliwości detekcji
Zamiast pytać „jak to zatrzymać”, bardziej użyteczne jest pytanie, gdzie obrońca realistycznie miał szansę zauważyć, że coś jest nie tak. W tym miejscu większość relacji o Miasmie się urywa, więc warto omówić to dokładniej.
Tempo publikacji. 23 wersje pakietów opublikowane w oknie sześciu sekund to nie jest coś, co robi człowiek. Każda organizacja utrzymująca własne mirror rejestru npm albo obserwująca zdarzenia publicznego rejestru dla pakietów, od których zależy, mogłaby automatycznie flagować takie serie publikacji. Pojedyncze podbicie wersji jest normalne. Dwa tuziny w kilka sekund to sygnał sam w sobie, niezależnie od tego, co pakiet faktycznie zawiera.
Zmiany dist-tagów bez podbicia wersji, o które nikt nie prosił. Przejęcie leo-sdk zadziałało właśnie dlatego, że latest przeskoczył na niższy, starszy na pierwszy rzut oka numer wersji (6.0.19), podczas gdy nowsze czyste wydanie (7.1.21) siedziało pod innym tagiem. Każde narzędzie porównujące w czasie dist-tagi pakietu, a nie tylko listę jego wersji, wychwyciłoby, że latest wskazuje wstecz w sekwencji wersji, co niemal nigdy nie jest legalnym zachowaniem.
Artefakty budowania niezgodne ze źródłem. Zainfekowany pakiet leo-sdk rozrósł się z 10 KB index.js w czystej wersji do 5 MB index.js w wersji zatrutej, z plikiem binding.gyp, który nie istnieje nigdzie w faktycznym repozytorium GitHub projektu. Porównanie tego, co jest opublikowane w rejestrze, z tym, co jest zacommitowane w systemie kontroli wersji, pakiet po pakiecie, ujawniłoby to natychmiast. Większość organizacji w ogóle nie wykonuje takiego porównania.
Nieoczekiwane natywne buildy w pakietach czysto JavaScriptowych. leo-sdk to SDK w JavaScripcie bez żadnej uzasadnionej potrzeby kompilacji natywnej. Plik binding.gyp pojawiający się w pakiecie, który nigdy wcześniej go nie zawierał albo nie ma wiarygodnego powodu, by go potrzebować, jest sam w sobie silnym sygnałem anomalii, na długo zanim ktokolwiek sprawdzi, co ten plik faktycznie robi.
Nowe pliki workflow pojawiające się bez odpowiadającego im pull requesta. Plik workflow o nazwie „Run Copilot” pojawiający się w .github/workflows/ bez powiązanego PR, bez review i bez autora, którego ktokolwiek rozpoznaje, to dokładnie ten rodzaj zmiany, do wychwytywania którego istnieją ochrona gałęzi i wymagane review, o ile faktycznie są egzekwowane konkretnie dla plików workflow.
Ruch wychodzący do świeżo utworzonych repozytoriów GitHub. Ponieważ eksfiltracja opierała się na nowo utworzonych publicznych repozytoriach pełniących rolę skrzynek kontaktowych, runnery CI lub maszyny deweloperów wykonujące wychodzące wywołania API tworzące nowe repozytoria albo wypychające do repozytoriów, które godzinę wcześniej nie istniały, to wykrywalny i dość nietypowy wzorzec zachowania.
Użycie OIDC i tokenów poza normalnym wzorcem. Token GitHub OIDC lub token publikacji npm użyty z zakresu IP, workflow lub okna czasowego, z którego nigdy wcześniej nie był używany, to klasyczny sygnał anomalii tożsamości, do którego wychwycenia nie trzeba wiedzieć nic o tej konkretnej kampanii.
Żadna z tych metod nie wymaga bazy CVE ani wcześniejszej wiedzy konkretnie o Miasmie. Wymagają one faktycznego obserwowania, jak zaufanie, zachowania publikacyjne i tożsamości przepływają przez Twoje środowisko w czasie, a nie tylko tego, co jest wymienione w pliku manifestu w jednym punkcie czasu.
Co powinny zrobić organizacje
Natychmiast
- Zrotuj poświadczenia w każdym środowisku, które zainstalowało dotknięty pakiet, ale dopiero po usunięciu złośliwej wersji i potwierdzeniu, że mechanizmy persystencji zostały usunięte, nie wcześniej.
- Zaktualizuj lub usuń dotknięte wersje pakietów w lockfile’ach, cache’ach, kontenerach i runnerach CI, nie tylko w manifeście najwyższego poziomu.
- Unieważnij i wydaj ponownie wszystkie tokeny GitHub, konfiguracje OIDC i tokeny npm, które mogły zostać ujawnione.
- Przypinaj akcje GitHub Actions do hasha commita zamiast do zmiennych tagów wersji.
Średnioterminowo
- Wprowadź listy dozwolonych pakietów dla tego, co jest dopuszczone w CI/CD i buildach produkcyjnych.
- Wymagaj provenance i podpisywania pakietów tam, gdzie są dostępne.
- Utrzymuj prawdziwy Software Bill of Materials (SBOM), a nie jednorazową migawkę.
- Dodaj okno karencji, zanim nowo opublikowane wersje pakietów zostaną uznane za zaufane w buildzie.
Długoterminowo
- Ciągła walidacja tego, co faktycznie działa w Twoim środowisku, zamiast okresowych audytów.
- Monitorowanie repozytoriów i CI/CD pod kątem nieoczekiwanych zmian w workflow lub zachowaniach publikacyjnych.
- Bieżąca analiza ścieżek ataku: nie tylko „czy ten pakiet jest podatny”, ale „do czego ktoś mógłby dotrzeć, gdyby ten pakiet był złośliwy”.
- Monitorowanie tożsamości deweloperów, ponieważ w tej kampanii to tożsamość była faktyczną powierzchnią ataku.
Dlaczego to ma znaczenie dla Penetrify
Miasma nie była problemem npm. Była problemem tożsamości. Problemem CI/CD. Problemem zaufania. Problemem ścieżki ataku.
Atakujący nie odkryli podatności. Odkryli ścieżkę od stacji roboczej jednego dewelopera do tysięcy systemów w dół łańcucha, a ta ścieżka istniała w całości w relacjach, których większość programów bezpieczeństwa nigdy faktycznie nie monitoruje: którzy ludzie mogą publikować kod, które tokeny mogą uruchomić wdrożenie, które workflow działają bezobsługowo z realnym dostępem do produkcji.
npm v12 zamyka konkretny wektor wykonania, na którym opierała się Miasma. Nie robi nic z leżącym pod spodem problemem tożsamości. Jak ujął to Moshe Siman Tov Bustan z OX Security w relacji o wdrożeniu npm v12: gdy atakujący kontroluje poświadczenia legalnego maintainera, blokowanie skryptów instalacyjnych nie pomaga, ponieważ złośliwy kod trafia do użytkowników jako zaufane, podpisane wydanie.
To cała teza tego artykułu, wyrażona prościej, niż sami potrafilibyśmy to zrobić. Współczesne bezpieczeństwo łańcucha dostaw oprogramowania musi wyjść poza skanowanie zależności i zmierzać w stronę ciągłego rozumienia, jak relacje zaufania ewoluują między repozytoriami, pipeline’ami budowania, tożsamościami i środowiskami produkcyjnymi, a nie tylko tego, jakie CVE widnieją w pliku manifestu.
Źródła
- The Hacker News: Miasma Malware Targets npm Packages and GitHub Actions in Supply Chain Attack
- Socket: Miasma Mini Shai-Hulud Hits LeoPlatform npm Packages and Go Ecosystem
- StepSecurity: Mass npm Supply Chain Attack: 20 Leo Platform Packages Compromised oraz Supply Chain Compromise: codfish/semantic-release-action
- Endor Labs: Shai-Hulud Strikes Leo Platform npm
- Corgea: Leo Platform npm packages compromised with Phantom Gyp Miasma toolkit
- Snyk: Node-gyp Supply Chain Compromise
- Datadog Security Labs: Shai-Hulud Goes Open Source
- The Register: Malware crew TeamPCP open-sources its Shai-Hulud worm on GitHub
- GitHub Changelog: Upcoming breaking changes for npm v12
- TechTimes: npm v12 Ships This Month, Blocking Install Scripts That Enabled Year of Supply Chain Attacks
