TL; DR
En klynge av fem npm-pakker, publisert på tvers av to kontoadresser, sendt en postinstall en krok som leser skylegitimasjon fra verten og sender dem direkte fra esken. Pakkene har spøkelses- og piratnavn – coral-wraith, ecto-corsair-whisper-6f3b9, ecto-corsair-flag-x9m4, ecto-rust-read-f3a9c1, ecto-nightly-spirit — og serialisere det de samler inn til en forfalskning ecto_module: YAML-manifestet før det sendes. Vi sporer klyngen som ektoplasma.
Nyttelasten kjører bare når den oppdager et bestemt miljø: en vert med navnet en 12-tegns heksadesimal streng og en arbeidskatalog under /app/node_modules — formen på en containerisert bygning eller CI-arbeider. Når den porten passerer, spør kroken om AWS-forekomstmetadatatjeneste (IMDSv2) for IAM-rollelegitimasjon, oppregner AWS Secrets Manager på tvers av tre regioner, dumper miljøvariabler, leser filer under /app, og skraper etter strenger for å erobre flagget. Deretter filtrerer den resultatet på to måter: et signal til en webhook.site samler og en manifest-PUT til et rå-IP-endepunkt, med en liste over localhost-first-fallbacks.
De senere pakkebeskrivelsene lyder «CTF-nyttelast for verdaccio supply-chain testing». Vi rapporterer denne selvbeskrivelsen som et observerbart faktum. Selve oppførselen – live utgang til en offentlig IP, ekte IMDS-legitimasjonsavlesninger, ekte Secrets Manager-kall – er hva den er uavhengig av etiketten, og er grunnen til at disse versjonene ble klassifisert som skadelige.
Ett navn i klyngen, coral-wraith, stoppet ikke ved én enkelt utgivelse. Den ble utgitt på nytt i rask rekkefølge gjennom dusinvis av versjoner i løpet av få timer – 1.0.0 klatring til 6.0.0 — og en tidligere utgave med samme navn hadde brukt oppblåst 9999.0.x versjonsnumre, den klassiske formen på en forsøk på avhengighetsforvirringPå tvers av den forandringen har nyttelasten synlig modnet: fra et ett-stegs vert-enumerasjon-beacon til en fullstendig AWS-legitimasjonspivot pakket inn i miljøkontroller som holder den stille utenfor det tiltenkte målet.
| Nettsidepakker | 5 navn; coral-wraith alene gjenutgitt på tvers av dusinvis av versjoner |
| Økosystem | NPM |
| Installer vektor | postinstall livssyklusskript |
| Primærmål | AWS IAM-rollelegitimasjon + hemmelige verdier for Secrets Manager, miljøvariabler, /app filer |
| Exfil | webhook.site beacon + rå-IP C2 PUT |
| Triggerport | 12-heksadecimalt vertsnavn + /app/node_modules cwd, pluss en miljøsjekk som undertrykker nyttelasten utenfor den konteksten |
| Alvorlighetsgrad | høy — skylegitimasjon og administrert hemmelig avsløring fra containeriserte bygge- og kjøretidsmiljøer |
Angrepsanatomi
Hver pakke i klyngen er bygget på samme måte: en nesten tom index.js (modul.eksport = {}), en én-linje pakke.json manus — «etterinstallasjon»: «node postinstall.js» — og nyttelasten i postinstall.jsDet er nok å installere pakken for å kjøre hooken; ingen import eller kall er nødvendig.
Målrettingsporten. Før den gjør noe, sjekker nyttelasten fra ektofamilien omgivelsene:
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 Et 12-heksadecimalt vertsnavn er standardformen Docker tilordner en container, og /app/node_modules er en konvensjonell installasjonssti i en container. Den tredje klausulen utelates hvis stien ser ut som en sandkassebasert utvinningskatalog. Nettoeffekten er at nyttelasten forblir sovende på en utviklerlaptop eller en analysesandkasse og bare aktiveres i en containerisert bygg- eller runtime-arbeider – den typen miljø som mest sannsynlig har live skylegitimasjon. Den tidligste pakken i klyngen, korallåpn, har ingen slik port og driver sin (enklere) samling ubetinget.
SamlingNår porten passerer, skaller kroken ut gjennom execFileSync("/bin/sh", ["-c", …]) og kjører en enkelt sammensatt kommando som, i rekkefølge:
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) Trinn 1–3 er en lærebok for IMDSv2-henting: be om et økttoken, og legg det deretter ved som X-aws-ec2-metadata-token header for å hente instansens IAM-rolle og den rollens midlertidige tilgangsnøkler. Valget om å implementere IMDSv2 i stedet for den enklere, uautentiserte IMDSv1 GET er verdt å merke seg – det betyr at nyttelasten fungerer selv på instanser som er konfigurert til å kreve tokenbasert metadatatilgang, som er den AWS-anbefalte herdingen. Legitimasjonene som returneres av trinn 3 er kortvarige. Tilgangsnøkkel-ID/Hemmelig tilgangsnøkkel/Pollett tripler omfanget til instansens rolle; hva enn den rollen kan gjøre, kan innehaveren av disse nøklene gjøre i løpet av legitimasjonens levetid.
Trinn 4–6 utvider perspektivet. env dump fanger opp alt som bygge- eller kjøretidsprosessen arvet – i praksis er det her registertokener, databasetilkoblingsstrenger og API-nøkler oftest befinner seg. / app filvandring leser opptil femten applikasjonsfiler utenfor node_modules, som kan overflatekonfigurasjon, .og V filer eller kilde. Trinn 6 kaller aws secretsmanager liste-hemmeligheter i tre regioner; legitimasjonene som hentes i trinn 1–3 er nøyaktig det som autentiserer disse kallene, så IMDS-lesingen og Secrets Manager-opplistingen kjedes sammen til en enkelt eskalering: instansrolle → administrert hemmelig inventar. Trinn 7 er en nikk til capture-the-flag-rammeverket – når en HTB{…} Når flagget finnes, sendes det alene. Ellers deles den rå, innsamlede klatten i fire deler og sendes.
Senere versjoner i klyngen tar eskaleringen videre. I stedet for å stoppe ved en inventarliste, analyserer de IMDS-svaret og eksporterer de midlertidige nøklene som AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN miljøvariabler, bekreft identiteten med aws sts get-caller-identity, og deretter gå over hver hemmelighet returnert av listehemmeligheter ringer aws secretsmanager get-secret-value på hver – henter det hemmelige innholdet, ikke bare navnene deres. De samme versjonene leser også prosesserstatte flaggbinærfiler (/leseflagg og venner) og forsøk en ladekjøring mot ethvert Rust-prosjekt funnet under / app, og utvider innhøstingen utover skylegitimasjon til hva byggemiljøet enn eksponerer.
Disse senere versjonene bruker også mer aggressive gateways. I tillegg til 12-heksadecimal-vertsnavnet og /app/node_modules sjekker, inspiserer nyttelasten den aktive pakkeregisterkonfigurasjonen og arbeidskatalogbanen og avslutter stille når de indikerer en analyse- eller speilkontekst i stedet for et aktivt mål. Den kombinerte effekten er en nyttelast som ikke gjør noe observerbart i de fleste inspeksjonsmiljøer og kjører hele samlingen sin bare der den vurderer seg selv å være på en ekte containerisert vert.
exfiltrationDe innsamlede dataene forlater verten på to kanaler. Først en beacon POST til en fast webhook.site samler, som inneholder vertsnavn, numerisk UID, arbeidskatalog og opptil 120 KB innsamlede data. For det andre blir dataene brettet inn i et falskt YAML-"modulmanifest" og PUT til /api/moduler/ på en målserver:
ecto_module: name: "<flag-or-chunk-0>" version: "1.0.0" power_level: "<chunk-1>" ship_deck: "<chunk-2>" cargo_hold: "<chunk-3>" Navnene på manifestfeltene (effektnivå, skipsdekk, lasterom) er dekorasjon – de stjålne dataene sitter inne i strengverdiene, og det er derfor en nettverksmonitor ser det som ser ut som en godartet pakkeregistermanifestopplasting i stedet for en åpenbar datadump. Beacon-kanalen bærer mer: POST kroppen til webhook.site inkluderer vertsnavn, numerisk UID, arbeidskatalog og opptil 120 KB av den innsamlede bloben, slik at selv en enkelt vellykket beacon leverer hele overtaket. webhook.site er en gratis tjeneste for inspeksjon av forespørsler; å bruke den som en samler betyr at operatøren aldri trenger å sette opp sin egen mottaksinfrastruktur for den kanalen, og de registrerte forespørslene forblir i tjenestens beholder.
Manifestet PUT bruker en reserveliste som begynner med flere 127.0.0.1/localhost porter og går deretter gjennom til tre offentlige adresser i `154.57.164.0/24` rekkevidde, og stopper ved det første endepunktet som svarer med statusen 2xx. Rekkefølgen «localhost-first» er i samsvar med selvbeskrivelsen «verdaccio testing» (et lokalt register på loopback), men fallbacks for public-IP betyr at dataene forlater verten når loopback ikke lytter – det vil si på en hvilken som helst maskin som ikke er forfatterens egen testrigg.
Tidslinje
Klyngen viser trinnvis kapasitetsvekst snarere enn et enkelt fall. Vi sorterer den etter observert oppførsel, ikke etter publisert veggklokke:
| Scene | Pakker / versjoner | Behavior |
|---|---|---|
| Tidlig løp | coral-wraith 9999.0.x | Oppblåste versjonsnumre i samsvar med et forsøk på avhengighetsforvirring; opplisting ved installasjonstid og exfil |
| Seed | coral-wraith 1.0.0 | postinstall samler id/env/flag-filer; enkelt PUT til 154[.]57[.]164[.]71:30782, markør ECT-472839 |
| Rask iterasjon | coral-wraith 1.0.1 → 6.0.0 | Dusinvis av utgivelser i løpet av timer; nyttelasten øker isAppWorker() gate, IMDSv2-legitimasjonshenting, hele get-secret-value pivot, kontroll av register-/banemiljø og markører for dobbel lagring |
| Parallelle navn | ecto-corsair-whisper-6f3b9 1.0.14–1.0.18 | Samme porterte nyttelast; webhook.site beacon og reserveliste for flere endepunkter |
| varianter | ecto-rust-read-f3a9c1 1.0.1–1.0.2 | Legger til ekstra vaskemarkører ECT-987654, ECT-654321, ECT-839201 |
| varianter | ecto-corsair-flag-x9m4 1.0.0, ecto-nightly-spirit 1.1.0 | Samme portstyrte nyttelast, samme C2 og beacon |
Det definerende trekket ved klyngen er publiseringskadensen: i stedet for én pakke og én versjon, publiseres det samme navnet om og om igjen i rask rekkefølge, hver utgivelse en liten variant av den forrige, sammen med en håndfull søsken med forskjellige navn som bærer samme nyttelast. Innenfor hviske familie delte koden seg i to tette fingeravtrykk – ett sett som utløser to kritiske deteksjoner, et annet tre (en ekstra fillesingsavløp) – men begge løser opp til samme nyttelast; forskjellen er kodeavvik, ikke en atferdsmessig forgrening. Versjoner av hviske utenfor det analyserte området (opptil minst 1.0.25 i skrivende stund) ble observert live i registeret, og korallåpn navnet fortsatte å klatre sin egen versjonsstige over det samme vinduet.
Indikatorer for kompromiss
Alle indikatorene nedenfor ble hentet fra pakkekilden på disken. Nettverksindikatorene er defanget.
Network
| Indikator | Rolle |
|---|---|
hxxp://154[.]57[.]164[.]71:30782 | C2 PUT-mål (coral-wraith) |
hxxp://154[.]57[.]164[.]80:30543 | C2 PUT-reserve (ecto-*) |
hxxp://154[.]57[.]164[.]82:31250 | C2 PUT-reserve (ecto-*) |
hxxp://154[.]57[.]164[.]71:31289 | C2 PUT-reserve (ecto-*) |
hxxps://webhook[.]site/602a4c72-7033-4e28-92ea-dc66e59206e5 | Beacon-samler |
169[.]254[.]169[.]254/latest/... | IMDSv2-legitimasjonslesing (målside, AWS-metadata) |
Atferdsmessig/fil
| Indikator | Rolle |
|---|---|
"postinstall": "node postinstall.js" | Installer vektor |
ecto_module: YAML med power_level / ship_deck / cargo_hold nøkler | Exfil manifestskjema |
Vaskmarkører ECT-472839, ECT-987654, ECT-654321, ECT-839201 | C2-banesegment /api/modules/<marker> |
isAppWorker() port: vert /^[0-9a-f]{12}$/, cwd inneholder /app/node_modules | Aktiveringsbetingelse |
aws secretsmanager list-secrets enn us-east-1, eu-west-1, eu-central-1 | Opplisting av hemmeligheter |
HTB{...} regex-skraping | Fang flagg-høsten |
Fil-hasher (sha256, registrert under analyse)
| filet | sha256 |
|---|---|
coral-wraith/postinstall.js | ce5ff035cfdfed1d0015446424b352c27b66bcb77e9fdb0a51e4245199146824 |
ecto-corsair-whisper-6f3b9 1.0.18/postinstall.js | b58432acba376aa6976f0490d9a1c04257ccdbc856d8390260c50322d63e31c3 |
Attribusjon og observert atferd
De fem pakkenavnene ble publisert under to npm-kontonavn, men de deler nok infrastruktur til å behandle dem som én klynge: den samme ecto_module manifest skjema, det samme ECT-472839 primær vaskmarkør, den samme webhook.site samler-ID og C2-endepunkter i det samme 154.57.164.0/24`-blokken. Frøpakken (`coral-wraith`, enklere og ikke-portert) og den porterte ektofamilien leses dermed som iterasjoner på ett verktøysett i stedet for uavhengige anstrengelser.
Pakkene beskriver seg selv, i senere versjoner, som «CTF-nyttelast for Verdaccio-forsyningskjedetesting.» Vi fremhever denne etiketten som et observerbart faktum og gjentar den ikke som et funn om formål. Det koden gjør er entydig og uavhengig av hvordan den er merket: den leser IAM-rollelegitimasjon fra instansens metadatatjeneste, lister opp administrerte hemmeligheter på tvers av tre AWS-regioner og overfører resultatene til en offentlig IP-adresse og en tredjeparts webhook-innsamler. En genuint loopback-only testharness ville ikke trenge reservelisten for offentlige IP-adresser, IMDS-legitimasjonslesingene eller kryssregions-kall fra Secrets Manager. Fordi utgående og rekkevidde for legitimasjon er reelle, ble de porterte versjonene klassifisert som skadelige.
Container-only-gaten er den mest bemerkelsesverdige egenskapen i drift. Det er både et unnvikelsestiltak – å holde seg stille på bærbare datamaskiner og i analysesandkasser – og et målrettet tiltak, som bare utløses der en reell IAM-rolle og live-hemmeligheter er mest sannsynlig til stede. Analytikere som kjører disse pakkene i en generisk sandkasse ville ikke observere noe; oppførselen manifesterer seg bare under et Docker-stil vertsnavn og en installasjonssti i containeren.
Innvirkning, trender og veiledning for forsvarere
Eksponeringen her er skylegitimasjon og hemmelig avsløring i bygge- og runtime-containere. En IAM-rollelegitimasjon hentet fra IMDS bærer de tillatelsene rollen har; hemmelighetssjef:Listehemmeligheter (og eventuelle oppfølgere GetSecretValue) utvider det til lagrede applikasjonshemmeligheter. Miljøvariabeldumper inneholder ofte registertokener, database-URL-er og API-nøkler. I en CI- eller containerkontekst – akkurat det porten velger – er en enkelt transitiv installasjon av en av disse pakkene nok til å lekke det materialet.
Ektoplasma passer inn i et mønster vi fortsetter å se: nyttelaster under installasjon som bruker skymetadata og administrerte hemmeligheter i stedet for lokale filer, og som bare aktiverer seg selv i miljøer med høy verdi. To defensive observasjoner følger.
- Formen er merkbarEn npm/PyPI-installasjonskrok hvis kallegraf når både et Cloud Secrets API (aws hemmelighetssjef, gcloud-hemmeligheter, az nøkkelhvelv) eller IMDS-adressen og en nettverksutgangsavløp er et smalt mønster med høyt signal – det forekommer nesten aldri i et legitimt livssyklusskript. Statisk strømningsanalyse kan flagge den uten å være avhengig av noe spesifikt domene eller IP-adresse.
- Miljøherding sløver den. Å håndheve IMDSv2 med en hoppgrense på 1 forhindrer at containerarbeidsbelastninger når instansmetadata; å sette IAM-roller til minst mulig privilegium begrenser eksplosjonsradiusen til enhver legitimasjon som lekker; og å kjøre installasjoner med –Ignorer-skript I CI fjerner install-hook-vektoren fullstendig for pakker som ikke trenger den.
For forsvarere er de praktiske kontrollene: varsling om utgående tilkoblinger fra build/CI-containere til offentlige IP-adresser som ikke er på tillatelseslisten under npm installasjon; se etter IMDS-tilgang som stammer fra pakkelivssyklusskript; og behandle enhver installasjonshooke som sender ut til et skybasert CLI som mistenkelig inntil det motsatte er bevist.
To ytterligere merknader spesifikt for denne klyngen. For det første, fordi aktivering er portet til containeriserte miljøer, kan en pakke som ser inert ut når den kontrolleres på en arbeidsstasjon, fortsatt være aktiv i produksjon – kontrollen må reprodusere containerens vertsnavn og stibetingelser, eller lese kildekoden direkte, i stedet for å stole på «Jeg installerte den, og ingenting skjedde». For det andre betyr bruken av en offentlig forespørselsinspeksjonstjeneste som en beacon-innsamler at noen av de eksfiltrerte dataene kan gjenopprettes for hendelsesrespons: en organisasjon som finner en av disse pakkene i avhengighetstreet sitt, kan resonnere om hva en vellykket beacon ville ha inneholdt fra nyttelastens innsamlingslogikk, og bør rotere eventuelle IAM-rollelegitimasjonsopplysninger, registertokener og administrerte hemmeligheter som var tilgjengelige fra det berørte bygge- eller kjøretidsmiljøet. Rotasjon av legitimasjon, ikke fjerning av pakker, er den operative utbedring når en installasjon har kjørt.





