Instalacja Ectoplasm npm Hooks Które kradną dane uwierzytelniające AWS

Ectoplasm: instalacja npm hooks które zbierają dane uwierzytelniające AWS za pomocą wyzwalacza obsługującego wyłącznie kontenery

TL; DR

Klaster pięć pakietów npm, opublikowane na dwóch kontach, wysłane postinstall hak, który odczytuje dane uwierzytelniające do chmury z hosta i wysyła je poza pudełko. Pakiety noszą nazwy pirackie i widmowe — coral-wraith, ecto-corsair-whisper-6f3b9, ecto-corsair-flag-x9m4, ecto-rust-read-f3a9c1, ecto-nightly-spirit — i wszystko, co zbierają, serializują, zamieniając w fałszywy ecto_module: Manifest YAML przed przesłaniem. Śledzimy klaster jako Ektoplazma.

Ładunek zostanie uruchomiony tylko wtedy, gdy wykryje określone środowisko: hosta, którego nazwa to 12-znakowy ciąg szesnastkowy i katalog roboczy pod /app/node_modules — kształt konteneryzowanej kompilacji lub pracownika CI. Po przejściu tej bramy hak wysyła zapytanie do Usługa metadanych instancji AWS (IMDSv2) w przypadku poświadczeń roli IAM wylicza Menedżer tajemnic AWS w trzech regionach, zrzuca zmienne środowiskowe, odczytuje pliki w /appi przeszukuje ciągi znaków do przechwycenia flagi. Następnie wyprowadza wynik na dwa sposoby: jako sygnał do webhook.site kolektor i manifest PUT do punktu końcowego z surowym adresem IP, z listą zapasowych rozwiązań localhost-first.

Późniejsze opisy pakietów brzmią: „Ładunek CTF do testowania łańcucha dostaw verdaccio”. Zgłaszamy ten opis jako obserwowalny fakt. Samo zachowanie – wyjście na żywo na publiczny adres IP, odczyt rzeczywistych danych uwierzytelniających IMDS, wywołania rzeczywistego Secrets Managera – jest tym, czym jest, niezależnie od etykiety i to właśnie dlatego te wersje zostały sklasyfikowane jako złośliwe.

Jedno nazwisko w klastrze, coral-wraith, nie zatrzymał się na jednym wydaniu. W ciągu kilku godzin ukazało się w kolejnych, szybkich edycjach – 1.0.0 wspinaczka do 6.0.0 — a wcześniejsza wersja o tej samej nazwie zawierała zawyżone 9999.0.x numery wersji, klasyczny kształt próba uzależnienia-zamieszaniaW trakcie tego procesu ładunek widocznie dojrzał: od jednorazowego sygnału wyliczenia hosta do pełnego przełącznika poświadczeń AWS, opakowanego w kontrole środowiska, które utrzymują go w tajemnicy poza zamierzonym celem.

Pakiety 5 nazw; coral-wraith samotne przedrukowane w dziesiątkach wersji
ekosystem Npm
Zainstaluj wektor postinstall skrypt cyklu życia
Podstawowy Cel Poświadczenia roli AWS IAM + wartości tajne Secrets Manager, zmienne środowiskowe, /app pliki
Wyjście webhook.site sygnał nawigacyjny + surowy adres IP C2 PUT
Bramka wyzwalająca 12-szesnastkowa nazwa hosta + /app/node_modules cwd, a także sprawdzenie środowiska, które blokuje ładunek poza tym kontekstem
Dotkliwość wysoka — poświadczenia w chmurze i udostępnianie zarządzanych informacji poufnych konteneryzowane środowiska kompilacji i wykonania

Anatomia ataku

Każdy pakiet w klastrze jest zbudowany w ten sam sposób: prawie pusty indeks.js (moduł.eksporty = {}), jednowierszowy pakiet.json scenariusz — „postinstall”: „węzeł postinstall.js” — i ładunek w postinstall.jsAby uruchomić hak, wystarczy zainstalować pakiet; nie jest wymagany żaden import ani wywołanie.

Brama docelowa. Zanim cokolwiek zrobi, ładunek z rodziny ekto sprawdza swoje otoczenie:

function isAppWorker():   host = os.hostname()   if host does NOT match /^[0-9a-f]{12}$/  -> exit   if cwd does NOT contain "/app/node_modules" -> exit   if cwd contains "/tmp/npm-safe"            -> exit   otherwise -> proceed

Domyślnym kształtem, jaki Docker przypisuje kontenerowi, jest 12-szesnastkowa nazwa hosta. /app/node_modules to konwencjonalna ścieżka instalacji w kontenerze. Trzecia klauzula nie działa, jeśli ścieżka wygląda jak katalog ekstrakcji w piaskownicy. W rezultacie ładunek pozostaje uśpiony na laptopie programisty lub w piaskownicy analitycznej i aktywuje się tylko w konteneryzowanym procesie kompilacji lub środowisku wykonawczym — środowisku, które najprawdopodobniej przechowuje dane uwierzytelniające w chmurze. Najwcześniejszy pakiet w klastrze, koralowy widmo, nie ma takiej bramy i prowadzi swój (prostszy) zbiór bezwarunkowo.

. Kiedy brama przechodzi, hak wychodzi przez execFileSync(„/bin/sh”, [„-c”, …]) i uruchamia pojedyncze polecenie złożone, które w kolejności:

1. PUT /latest/api/token to 169.254.169.254          (IMDSv2 token request) 2. GET .../iam/security-credentials/                  (IAM role name) 3. GET .../iam/security-credentials/<role>            (temporary credentials) 4. dump env | sort                                    (environment variables) 5. list /app (excl. node_modules) + cat first 15      (application files) 6. aws secretsmanager list-secrets                    (us-east-1, eu-west-1, eu-central-1) 7. scrape readable files for HTB{...}                 (capture-the-flag strings)

Kroki 1–3 to podręcznik pobierania IMDSv2: poproś o token sesji, a następnie dołącz go jako Token metadanych X-aws-ec2 Nagłówek do pobrania roli IAM instancji i tymczasowych kluczy dostępu do tej roli. Wybór implementacji IMDSv2 zamiast prostszego, nieuwierzytelnionego IMDSv1 GET Warto zauważyć, że oznacza to, że ładunek działa nawet na instancjach skonfigurowanych tak, aby wymagać dostępu do metadanych opartego na tokenach, co jest zalecanym przez AWS wzmocnieniem. Dane uwierzytelniające zwrócone w kroku 3 są krótkotrwałe. Identyfikator klucza dostępu/Tajny klucz dostępu/żeton potrójny zakres jest ograniczony rolą instancji; wszystkie czynności, jakie może wykonać dana rola, mogą być wykonywane przez posiadacza kluczy przez cały okres istnienia poświadczenia.

Kroki 4–6 poszerzają ujęcie. env dump przechwytuje wszystko, co odziedziczył proces kompilacji lub wykonania — w praktyce to właśnie tam najczęściej znajdują się tokeny rejestru, ciągi połączeń z bazą danych i klucze API. / app File Walk odczytuje do piętnastu plików aplikacji poza node_modules, który może wyświetlać konfigurację, .env pliki lub źródło. Krok 6 wywołuje lista sekretów menedżera sekretów AWS w trzech regionach; dane uwierzytelniające pobrane w krokach 1–3 to właśnie te uwierzytelniające te połączenia, więc odczyt IMDS i łańcuch wyliczeń Secrets Manager łączą się w jedną eskalację: rola instancji → inwentarz zarządzanych sekretów. Krok 7 to ukłon w stronę mechanizmu „przechwyć flagę” — gdy HTB{…} jeśli flaga zostanie znaleziona, zostanie wysłana osobno, w przeciwnym wypadku surowy zebrany blob zostanie podzielony na cztery części i wysłany.

Późniejsze wersje w klastrze kontynuują eskalację. Zamiast zatrzymywać się na inwentaryzacji, analizują odpowiedź IMDS i eksportują klucze tymczasowe jako AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN zmienne środowiskowe, potwierdź tożsamość za pomocą aws sts pobierz-tożsamość-wywołującego, a następnie wykonaj pętlę po każdym zwróconym przez lista sekretów powołanie aws secretsmanager pobierz-wartość-tajemniczą na każdym z nich — pobierając tajną zawartość, a nie tylko ich nazwy. Te same wersje odczytują również pliki binarne flag z podstawionymi procesami (/flaga odczytu i przyjaciół) i spróbuj bieg ładowania przeciwko każdemu projektowi Rust znalezionemu pod / app, rozszerzając zbiór poza dane uwierzytelniające w chmurze na wszystko, co udostępnia środowisko kompilacji.

Te późniejsze wersje również bramkują się bardziej agresywnie. Oprócz 12-heksadecymalnej nazwy hosta i /app/node_modules Podczas kontroli ładunek sprawdza aktywną konfigurację rejestru pakietów oraz ścieżkę katalogu roboczego i kończy działanie w trybie cichym, gdy wskazują one kontekst analizy lub lustrzanego odbicia, a nie rzeczywisty cel. W efekcie ładunek nie wykonuje żadnych obserwowalnych działań w większości środowisk inspekcyjnych i uruchamia pełną kolekcję tylko wtedy, gdy uzna, że ​​znajduje się na prawdziwym, skonteneryzowanym hoście.

ExfiltracjaZebrane dane opuszczają hosta dwoma kanałami. Pierwszym jest sygnał radiowy POST do stałego webhook.site kolektor, zawierający nazwę hosta, numeryczny identyfikator użytkownika, katalog roboczy i do 120 KB zebranych danych. Następnie dane są składane do fałszywego „manifestu modułu” w formacie YAML i PUT do /api/moduły/ na serwerze docelowym:

ecto_module:   name: "<flag-or-chunk-0>"   version: "1.0.0"   power_level: "<chunk-1>"   ship_deck: "<chunk-2>"   cargo_hold: "<chunk-3>"

Nazwy pól manifestu (poziom_mocy, pokład statku, ładownia) są ozdobą — skradzione dane znajdują się w wartościach ciągu, dlatego monitor sieci widzi coś, co wygląda na nieszkodliwe przesłanie manifestu rejestru pakietów, a nie oczywisty zrzut danych. Kanał nawigacyjny przenosi więcej: POST ciało do webhook.site obejmuje nazwę hosta, numeryczny identyfikator użytkownika, katalog roboczy i do 120 KB zebranego blobu, więc nawet pojedynczy pomyślnie wysłany sygnał dostarcza pełnych danych. webhook.site jest bezpłatną usługą inspekcji żądań; korzystanie z niej jako kolektora oznacza, że ​​operator nigdy nie musi uruchamiać własnej infrastruktury odbiorczej dla danego kanału, a zarejestrowane żądania pozostają w pojemniku usługi.

Manifest PUT przechodzi przez listę zapasową, która zaczyna się od kilku 127.0.0.1/localhost porty, a następnie przechodzi do trzech adresów publicznych w `154.57.164.0/24` zakres, zatrzymując się na pierwszym punkcie końcowym, który odpowie statusem 2xx. Kolejność „localhost first” jest zgodna z samoopisem „verdaccio testing” (lokalny rejestr w pętli zwrotnej), ale rezerwowe adresy IP publiczne oznaczają, że dane opuszczają hosta, gdy pętla zwrotna nie nasłuchuje — czyli na każdej maszynie, która nie jest własnym stanowiskiem testowym autora.

Oś czasu

Klaster pokazuje stopniowy wzrost możliwości, a nie pojedynczy spadek. Sortujemy go według obserwowanego zachowania, a nie według zegara publikacji:

STAGE Pakiety / Wersje Zachowanie
Wczesny bieg coral-wraith 9999.0.x Zawyżone numery wersji są zgodne z próbą wprowadzenia w błąd co do zależności; wyliczenie i usunięcie w czasie instalacji
Nasienie coral-wraith 1.0.0 postinstall zbiera pliki id/env/flag; pojedyncze PUT do 154[.]57[.]164[.]71:30782, znacznik ECT-472839
Szybka iteracja coral-wraith 1.0.1 → 6.0.0 Dziesiątki wydań w ciągu kilku godzin; ładunek zyskuje na wartości isAppWorker() brama, pobieranie poświadczeń IMDSv2, pełna get-secret-value punkt obrotu, sprawdzenie środowiska rejestru/ścieżki i podwójne znaczniki zlewu
Nazwy równoległe ecto-corsair-whisper-6f3b9 1.0.14–1.0.18 Ten sam ładunek bramkowany; webhook.site lista zapasowa sygnałów nawigacyjnych i wielu punktów końcowych
Warianty ecto-rust-read-f3a9c1 1.0.1–1.0.2 Dodaje dodatkowe znaczniki zlewu ECT-987654, ECT-654321, ECT-839201
Warianty ecto-corsair-flag-x9m4 1.0.0, ecto-nightly-spirit 1.1.0 Ten sam ładunek bramkowany, ten sam C2 i sygnalizator

Cechą charakterystyczną klastra jest rytm publikacji: zamiast jednego pakietu i jednej wersji, ta sama nazwa jest wielokrotnie publikowana w krótkich odstępach czasu, przy czym każde wydanie stanowi niewielką wariację poprzedniego, wraz z garstką pokrewnych pakietów o różnych nazwach, przenoszących ten sam ładunek. W ramach szept rodzina kod podzielił się na dwa bliskie odciski palców — jeden zestaw wyzwalający dwa krytyczne wykrycia, drugi trzy (dodatkowy odbiornik odczytu pliku) — ale oba rozwiązują się z tym samym ładunkiem; różnica polega na dryfowaniu kodu, a nie na rozwidleniu behawioralnym. Wersje szept poza analizowanym zakresem (przynajmniej do 1.0.25 w momencie pisania) obserwowano na żywo w rejestrze, a koralowy widmo nazwa nadal wspinała się po drabinie wersji nad tym samym oknem.

Wskaźniki kompromisu

Wszystkie poniższe wskaźniki zostały wyodrębnione ze źródła pakietu na dysku. Wskaźniki sieciowe zostały usunięte.

Sieć

Wskaźnik Rola
hxxp://154[.]57[.]164[.]71:30782 Cel C2 PUT (coral-wraith)
hxxp://154[.]57[.]164[.]80:30543 Zapasowy C2 PUT (ecto-*)
hxxp://154[.]57[.]164[.]82:31250 Zapasowy C2 PUT (ecto-*)
hxxp://154[.]57[.]164[.]71:31289 Zapasowy C2 PUT (ecto-*)
hxxps://webhook[.]site/602a4c72-7033-4e28-92ea-dc66e59206e5 Kolekcjoner sygnałów świetlnych
169[.]254[.]169[.]254/latest/... Odczyt poświadczeń IMDSv2 (po stronie docelowej, metadane AWS)

Behawioralny/plik

Wskaźnik Rola
"postinstall": "node postinstall.js" Zainstaluj wektor
ecto_module: YAML z power_level / ship_deck / cargo_hold Klawisze Schemat manifestu Exfil
Znaczniki zlewu ECT-472839, ECT-987654, ECT-654321, ECT-839201 Segment ścieżki C2 /api/modules/<marker>
isAppWorker() brama: gospodarz /^[0-9a-f]{12}$/, cwd zawiera /app/node_modules Warunek aktywacji
aws secretsmanager list-secrets koniec us-east-1, eu-west-1, eu-central-1 Wyliczanie sekretów
HTB{...} zeskrobywanie wyrażeń regularnych Żniwa w ramach akcji „zdobądź flagę”

Skróty plików (sha256, przechwycone w momencie analizy)

filet sha256
coral-wraith/postinstall.js ce5ff035cfdfed1d0015446424b352c27b66bcb77e9fdb0a51e4245199146824
ecto-corsair-whisper-6f3b9 1.0.18/postinstall.js b58432acba376aa6976f0490d9a1c04257ccdbc856d8390260c50322d63e31c3

Atrybucja i obserwowane zachowanie

Pięć nazw pakietów opublikowano pod dwoma uchwytami kont npm, ale mają one wystarczającą infrastrukturę, aby traktować je jako jeden klaster: ten sam ecto_module schemat manifestu, taki sam ECT-472839 główny znacznik zlewu, ten sam webhook.site identyfikator kolektora i punkty końcowe C2 w tym samym Blok 154.57.164.0/24. Pakiet nasion (`coral-wraith, prostsze i nieograniczone) oraz ektorodzina ograniczona mogą być traktowane jako iteracje jednego zestawu narzędzi, a nie jako niezależne wysiłki.

W późniejszych wersjach pakiety opisują się jako „Ładunek CTF do testowania łańcucha dostaw verdaccio”. Przedstawiamy tę etykietę jako obserwowalny fakt i nie powtarzamy jej jako ustalenia dotyczącego celu. To, co robi kod, jest jednoznaczne i niezależne od sposobu, w jaki jest oznaczony: odczytuje dane uwierzytelniające roli IAM z usługi metadanych instancji, wylicza zarządzane sekrety w trzech regionach AWS i przesyła wyniki do publicznego adresu IP oraz zewnętrznego kolektora webhooków. Prawdziwy test z pętlą zwrotną nie wymagałby listy rezerwowej publicznych adresów IP, odczytów danych uwierzytelniających IMDS ani wywołań międzyregionalnego menedżera sekretów. Ponieważ dane wyjściowe i zasięg danych uwierzytelniających są rzeczywiste, wersje z bramkami zostały sklasyfikowane jako złośliwe.

Bramka typu „container-only” jest najbardziej zauważalną cechą operacyjną. Jest to zarówno środek obejścia – pozostając nieaktywnym na laptopach i w środowiskach testowych – jak i środek namierzający, aktywowany tylko tam, gdzie najprawdopodobniej występuje prawdziwa rola IAM i aktywne sekrety. Analitycy uruchamiający te pakiety w ogólnym środowisku testowym niczego by nie zaobserwowali; zachowanie to ujawnia się tylko w przypadku nazwy hosta w stylu Dockera i ścieżki instalacji wewnątrz kontenera.

Wpływ, trendy i wskazówki dla obrońców

W tym przypadku ujawnienie dotyczy poświadczeń chmurowych i ujawnienia poufnych informacji w kontenerach kompilacji i środowiska wykonawczego. Poświadczenia roli IAM pobrane z IMDS niosą ze sobą wszystkie uprawnienia przypisane tej roli; secretsmanager:ListSecrets (i wszelkie dalsze działania PobierzSecretValue) rozszerza to na przechowywane sekrety aplikacji. Zrzuty zmiennych środowiskowych często zawierają tokeny rejestru, adresy URL baz danych i klucze API. W kontekście CI lub kontenera – dokładnie tak, jak wybiera brama – pojedyncza instalacja przechodnia jednego z tych pakietów wystarczy, aby ujawnić te dane.

Ectoplasm wpisuje się w schemat, który wciąż obserwujemy: ładunki w czasie instalacji, które sięgają po metadane w chmurze i zarządzane sekrety zamiast plików lokalnych, i same się uruchamiają tylko w środowiskach o wysokiej wartości. Oto dwie obserwacje obronne.

  • Kształt jest wykrywalnyHak instalacyjny npm/PyPI, którego graf wywołań dociera zarówno do interfejsu API sekretów chmury (Menedżer sekretów AWS, sekrety gcloud, az keyvault) lub adres IMDS i odpływ wychodzący z sieci to wąski wzorzec o wysokim sygnale — prawie nigdy nie występuje w prawidłowym skrypcie cyklu życia. Analiza przepływu statycznego można oznaczyć go bez względu na konkretną domenę lub adres IP.
  • Utwardzanie w środowisku powoduje jego stępienie. Wymuszanie IMDSv2 z limitem przeskoków równym 1 zapobiega docieraniu obciążeń kontenerów do metadanych instancji; zakres ról IAM do najmniejszych uprawnień ogranicza promień rażenia wyciekających poświadczeń; a uruchamianie instalacji z –Ignore-scripts w CI całkowicie usuwa wektor install-hook dla pakietów, które go nie potrzebują.

W przypadku obrońców praktyczne kontrole obejmują: alert dotyczący połączeń wychodzących z kontenerów kompilacji/CI do publicznych adresów IP, które nie znajdują się na liście dozwolonych podczas npm zainstalować; uważaj na dostęp do IMDS pochodzący ze skryptów cyklu życia pakietu; i traktuj każdy hak instalacyjny, który przekierowuje do interfejsu wiersza poleceń w chmurze, jako podejrzany, dopóki nie zostanie udowodnione, że jest inaczej.

Dwie dodatkowe uwagi dotyczące tego klastra. Po pierwsze, ponieważ aktywacja jest ograniczona do środowisk kontenerowych, pakiet, który wydaje się bezczynny podczas weryfikacji na stacji roboczej, może nadal działać w środowisku produkcyjnym — weryfikacja musi odtworzyć nazwę hosta kontenera i warunki ścieżki lub odczytać kod źródłowy bezpośrednio, zamiast polegać na scenariuszu „Zainstalowałem i nic się nie stało”. Po drugie, wykorzystanie publicznej usługi kontroli żądań jako kolektora sygnałów oznacza, że ​​część wykradzionych danych może być odzyskana na potrzeby reagowania na incydenty: organizacja, która znajdzie jeden z tych pakietów w swoim drzewie zależności, może wywnioskować, co zawierałby pomyślnie działający sygnał nawigacyjny na podstawie logiki gromadzenia danych w ładunku, i powinna dokonać rotacji wszelkich poświadczeń roli IAM, tokenów rejestru i zarządzanych kluczy tajnych, które były dostępne z poziomu środowiska kompilacji lub środowiska wykonawczego. Rotacja poświadczeń, a nie usuwanie pakietów, jest operacyjną naprawą po uruchomieniu instalacji.

sca-tools-oprogramowanie-narzędzia-analizy-kompozycji
Określ priorytety, rozwiąż problemy i zabezpiecz zagrożenia związane z oprogramowaniem
Załóż darmowe konto.
Nie wymagamy karty kredytowej.

Zabezpiecz swoje oprogramowanie i dostarczanie

z pakietem produktów Xygeni