TL, д-р
Адзін выдавец npm выпусціў восем невялікіх пакетаў, назвы якіх чытаюцца як звычайныя будаўнічыя блокі для распрацоўкі блокчэйна і кашалькоў. base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, і crypto-validate-libКожны з іх утрымлівае працоўную ўтыліту. Аднак пасля гэтай утыліты дадаецца самазапускаючы блок кода, які запускаецца ў момант імпарту модуля.
Прыкладна праз 37 секунд пасля імпарту, гэты блок дэкадуе карысную нагрузку, якая знаходзіцца ўнутры пакета, замаскіраванага пад тэставую прыладу, запісвае яе ў схаваны файл у хатнім каталогу карыстальніка, рэгіструецца для перазагрузкі кожны login у Windows, macOS і Linux, і запускае дэкадаваны скрыпт як асобны працэс. Падчас усталёўкі npm нічога не адбываецца; праграма чакае імпарту і запуску кода, што больш ціха, чым сама ўстаноўка.
Карысная нагрузка не з'яўляецца непразрыстай. Яна пастаўляецца ў выглядзе звычайнага base64, таму дэкадаванне "тэставай прыстасаванасці" цалкам аднаўляе другі этап: a крыпта-кашалёк і выкрадальнік сакрэтаў які чакае, пакуль машына будзе бяздзейная, збірае закрытыя ключы і пачатковыя фразы, шыфруе кожную знаходку з дапамогай жорстка закадаванага ключа RSA-4096 і выцягвае яе, замацоўваючы ў публічным сховішчы IPFS, і адпраўляе сігналы назад кожныя 12 гадзін.
Мы адсочваем кластар як PhantomSyncНа момант аналізу ўсе восем пакетаў былі даступныя ў рэестры npm і апублікаваныя пад адным акаўнтам.
| Экасістэма | НПМ |
| Пакеты | base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, crypto-validate-lib |
| Мэтавыя платформы | Windows, macOS, Linux |
| Асноўная паводзіны | Затрыманы дроппер падчас імпарту, схаваны як тэставая прылада; усталёўвае кросплатформенную персістантнасць |
| карысная нагрузка | Крыпта-кашалёк і выкрадальнік сакрэтаў, шыфраванне RSA-4096, выманне праз публічнае замацаванне IPFS |
Анатомія атакі
На першы погляд кожная ўпакоўка выглядае нічым не характэрнай. base58-утыліты, напрыклад, — гэта некалькі кілабайт дапаможнага кода Base58 / Bitcoin-WIF без залежнасці — менавіта тое, што абяцае назва. Адпаведны код знаходзіцца пасля модуль module.exports, куды чытач, які праглядае пачатак файла, наўрад ці загледзіць: самавыклікальная функцыя, якая плануе сама сябе з дапамогай таймера.
Ланцужок аперацый, рэканструяваны з зыходнага кода пакета, выглядае наступным чынам:
1. Package is required() by the host project 2. A self-invoking function schedules a callback ~37,000 ms later (setTimeout) 3. On fire, the callback reads test/fixtures/keypairs.dat (a base64 blob) 4. It base64-decodes that blob into a Node.js script 5. It writes the script to ~/.cache-db/.node-sync/syncd.js (mode 0o700) 6. It installs login-persistence for that script (see below) 7. It spawns "node syncd.js" as a detached process Вылучаюцца два варыянты дызайну.
Карысная нагрузка перамяшчаецца ў якасці выпрабавальнай прылады. Другі этап не напісаны ў выглядзе відавочнага кода. Ён знаходзіцца ў тэст/прылады/пары ключоў.dat, файл base64, назва якога ўпісваецца ў пакет, які, як сцвярджаецца, апрацоўвае пары ключоў. Для чалавека, які праглядае tarball, ён выглядае як прыклад дадзеных; для далучанага кода гэта скрыпт для дэкадавання і запуску. Сам дроппер не ўтрымлівае сеткавага адраса — ён знаходзіцца на другім этапе — але няма дадатковай абфускацыі: фікстура ўяўляе сабой адзін пласт base64, таму яго дэкадаванне (base64 -d) аднаўляе поўны syncd.js і яго паводзіны ў сетцы. У наступным раздзеле апісана, што робіць гэты адноўлены этап.
Дэтанацыя адкладзеная і прывязаная да імпарту, а не да ўстаноўкі. Паколькі трыгерам з'яўляецца патрабую () плюс таймер працягласцю ~37 секунд замест перахопніка ўстаноўкі, паводзіны абыходзяць праверкі, якія назіраюць толькі за ная ўстаноўка крок, і затрымка доўжыцца даўжэй, чым многія кароткачасовыя запускі ў пясочніцы і неперасякальнай інтэграцыі. Пакуль што-небудзь выконваецца, усталёўка, якая загрузіла пакет, даўно завершана.
Пасля таго, як дэкадаваны сцэнар апынецца на дыску ў ~/.cache-db/.node-sync/syncd.js — шлях, абраны для чытання як звычайны каталог кэша — дроппер дазваляе яму перазагрузіцца на ўсіх трох асноўных платформах:
- Linux: запіс cron, які перазапускае скрыпт. Запіс усталёўваецца шляхам фільтрацыі існуючага crontab праз grep -v сінхранізаваны, што мае пабочны эфект — новы запіс не ўключаны ў просты спіс, які выкарыстоўвае grep для пошуку з той жа назвай.
- Windows: запланаванае заданне пад назвай WinNodeSync, запланаваны паўторны запуск з інтэрвалам у 12 хвілін.
- MacOS: заданне launchd з пазнакай com.apple.syncd, які прысутнічае ў некалькіх пакетах — пазнака, якая імітуе легітымную сістэмную службу Apple.
Скрыпт запускаецца адразу як асобны працэс, таму ён працягвае працаваць пасля завяршэння праграмы імпарту.
Што робіць другі этап
Паколькі прылада мае адзіны пласт base64, другі этап дэкадуецца чыста і можа быць прачытаны цалкам. Усе восем пакетаў утрымліваюць адзін з трох варыянтаў аднаго і таго ж сцэнарыя, які ідэнтыфікуе сябе ў каментарыі загалоўка як phantom syncd v3 — topo durmiente («спячы крот»). Яго задача — красці матэрыялы крыптавалюты-кашалькоў і сакрэты распрацоўшчыкаў і адпраўляць іх з машыны. Ён працуе наступным чынам:
- 1. Пачакайце, пакуль машына спыніцца. Перш чым што-небудзь рабіць, syncd.js правярае, колькі часу карыстальнік быў неактыўным, і працягвае толькі пасля заканчэння пэўнага парога (каля 15 хвілін) — xprintidle у Лінуксе, іорег HIDIdleTime у macOS і запыт PowerShell падчас прастою ў Windows. Такім чынам, збор дадзеных звычайна адбываецца, калі нікога няма за клавіятурай.
- 2. Вазьміце дыстанцыйна кіраваны перамыкач актывацыіСкрыпт атрымлівае невялікую канфігурацыю з недаступнага месцазнаходжання, перш чым пачаць дзейнічаць. Першаснай крыніцай з'яўляецца неапрацаваны URL-адрас gist з GitHub (gist.githubusercontent.com/juang55/…/cfg.txt); скрыпт актывуецца толькі ў тым выпадку, калі гэтая канфігурацыя чытае актыўны=1Калі gist недаступны, выкарыстоўваюцца тры жорстка закадаваныя ідэнтыфікатары кантэнту IPFS, якія атрымліваюцца праз публічныя шлюзы. gateway.pinata.cloud, ipfs.io, і cloudflare-ipfs.comГэта дае аператару магчымасць паслядоўнага кантролю над узбраеннем/зняццем зброі і рэзервовы варыянт, абаронены ад зняцця. (Найменшы варыянт пастаўляецца ў crypto-validate-lib, памочнікі па eth-wallet, і solana-key-utils, мае выдалены ўзровень gist і абапіраецца толькі на ідэнтыфікатары IPFS.)
- 3. Збярыце матэрыял для кашалька і сакрэты. Скрыпт праходзіць па хатнім каталогу карыстальніка — ~/.config/solana, ~/.ethereum/сховішча ключоў, ~/.foundry, ~/.hardhat, ~/.ssh, і працоўны стол/дакументы/загрузкі (у тым ліку назвы папак на іспанскай мове), а таксама ~/.env і файлы rc-аперацыйнай абалонкі, а таксама эквівалент AppData месцазнаходжанні ў Windows. Ён накіраваны на закрытыя ключы Ethereum, ключы Bitcoin WIF, фразы BIP-39, пары ключоў Solana, JSON сховішча ключоў Ethereum, ключы SSH і зменныя асяроддзя, якія змяшчаюць сакрэт. Большы варыянт (у abi-кадаванне, base58-утыліты, eth-dev) змяшчае поўны слоўнік BIP-39 з 2048 слоў і выкарыстоўвае рэгулярныя выразы для выманне асобныя ключы і правераныя пачатковыя фразы з любога тэксту, які ён чытае; два меншыя варыянты замест гэтага супастаўляюць файлы па ключавых словах (насенне, мнеманічна, папернік, metamask, фантом, галоўная кніга, скрынка, …) і загружаць цэлыя файлы.
- 4. Зашыфруйце і выкрасліце дадзеныя праз публічны сэрвіс пінінга. Кожная знаходка суправаджаецца адбіткам пальца гаспадара (імя карыстальніка@імяхаста, платформа, часовая метка) і шыфруецца з дапамогай жорстка закадаванага адкрытага ключа RSA-4096, убудаванага ў скрыпт. Зашыфраваны запіс затым загружаецца шляхам прымацавання яго да IPFS праз api.pinata.cloud/pinning/pinJSONToIPFS, аўтэнтыфікаваны з дапамогай жорстка закадаваных уліковых дадзеных Pinata API. Няма спецыяльнага сервера C2 для захопу: скрадзеныя даныя захоўваюцца ў публічным дэцэнтралізаваным сховішчы, адкуль аператар можа атрымаць іх з дапамогай атрыманых хэшаў кантэнту. Загрузкі адбываюцца з інтэрвалам у некалькі секунд з выпадковым ваганнем, і захоўваецца лакальны журнал часовая метка | тып ключа | вернуты хэш захоўваецца ў ~/.cache-db/.node-sync/.sl.
- 5. Захоўвайце настойлівасць і падтрымлівайце 12-гадзінны цыкл. На другім этапе аднаўляецца ўласная пастаянства — запіс cron у Linux, com.apple.syncd заданне launchd у macOS і запланаванае заданне з назвай Сінхранізацыя вузлоў Windows у Windows — настроена на паўторны запуск кожныя 12 гадзін. Звярніце ўвагу, што гэта розны Назва задачы Windows з той, якую ўсталёўвае дроппер (WinNodeSync); за абодвума варта паляваць.
Timeline
Восем пакетаў былі апублікаваныя ў кароткія тэрміны 13 і 14 ліпеня 2026 года. Некалькі з іх маюць больш за адну версію; дроппер аднолькавы ва ўсіх версіях, змяняюцца толькі зрухі радка ў залежнасці ад памеру бяспечнага ўтылітнага кода над ім.
| дата | падзея |
|---|---|
| 2026-07-13 | Першыя пакеты ў кластары з'яўляюцца пад адным выдаўцом (solana-key-utils, eth-wallet-helpers, crypto-validate-lib і раннія версіі астатніх) |
| 2026-07-13 → 07-14 | Астатнія назвы і наступныя версіі апублікаваныя; тыя ж дададзеныя караблі-пад'ёмнікі ў кожнай |
| 2026-07-14 | Усе восем пазначаных і прааналізаваных пакетаў; кожны пакет усё яшчэ можна ўсталяваць з рэестра. |
Падчас перыяду выяўлення рэпутацыя выдаўца ў рэестры змянілася з прыкладна нейтральнай на пачатку кластара да вельмі адмоўнай па меры назапашвання выяўленняў — гэта назіраны пабочны эфект пазначэння пакетаў, а не спецыяльная функцыя.
Паказчыкі кампрамісу
Усе паказнікі, пералічаныя ніжэй, былі пацверджаны на момант аналізу — тыя, што ў табліцах «Другі этап» — шляхам дэшыфравання тэст/прылады/пары ключоў.dat і чытанне адноўленага syncd.js.
Файлы і шляхі
| індыкатар | Роля |
|---|---|
~/.cache-db/.node-sync/syncd.js | Дэкадаваны другі этап, запісаны ў рэжыме 0o700 |
~/.cache-db/.node-sync/.sl | Лакальны журнал выцяснення (метка часу | тып ключа | хэш IPFS) |
test/fixtures/keypairs.dat | Карысная нагрузка ў кадоўцы Base64 захоўваецца ўнутры tarball як «тэставая прылада» |
Сеткавая інфраструктура другога этапу (аднаўлена з syncd.js)
| індыкатар | Роля |
|---|---|
gist.githubusercontent.com/juang55/b298754cb72942b1cdcf02ccd45cde2f/raw/cfg.txt | Мёртвая кропка актывацыі; скрыпт запускаецца толькі пры чытанні канфігурацыі active=1 |
Qmcqz3w8j4qFQXDAXAxnrdc2oSX3nzBT4NqtpTqL8mr1ga | Рэзервовая канфігурацыя IPFS (CID) |
QmdTXoqVmTHY1i4ZWLdLkoQ9YChp5TXPh5cWXwnAYZt5iF | Рэзервовая канфігурацыя IPFS (CID) |
QmfJkLU5gdCpqbbqEjWYC2anXW9FmuEeSLLeLiHVJKYUjp | Рэзервовая канфігурацыя IPFS (CID) |
gateway.pinata.cloud, ipfs.io, cloudflare-ipfs.com | Шлюзы IPFS, якія выкарыстоўваюцца для атрымання рэзервовай канфігурацыі |
api.pinata.cloud/pinning/pinJSONToIPFS | Канчатковая кропка выкрадання, скрадзеныя даныя замацаваны на публічнай IPFS |
| Ключ API Pinata | 13c766575b9270a9825d, жорстка закадаваныя паўнамоцтвы для экфільтрацыі |
Мэты збору другога этапу (атрыманыя з syncd.js)
| індыкатар | Роля |
|---|---|
~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~/.ssh, ~/.env, rc-файлы абалонкі | Пошук ключоў і сакрэтаў у каталогах/файлах |
AppData\Roaming\Solana, AppData\Local\ethereum\keystore | Пошук эквівалентаў у Windows |
| Тыпы сабраных артэфактаў | Закрытыя ключы ETH, Bitcoin WIF, фразы-пачаткоўцы BIP-39, пары ключоў Solana, JSON для сховішча ключоў Ethereum, ключы SSH, сакрэтныя зменныя асяроддзя |
Артэфакты ўстойлівасці
| Платформа | індыкатар |
|---|---|
| Linux | запуск запісу cron syncd.js; усталёўваецца праз crontab, фільтруецца праз grep -v syncd |
| Windows | запланаваная задача WinNodeSync (кропельніца) і WindowsNodeSync (другі этап) |
| Macos | launchd label com.apple.syncd |
| Усе | Другая стадыя паўтараецца з 12-гадзінным цыклам |
Паводніцкія
- Самавыклікальная функцыя дададзена пасля module.exports, планаванне а усталяваць час чакання ~37 000 мс пры імпарце.
- даччыны_працэс spawn("вузел", ) з асобным наборам опцый.
- Актывацыя ў рэжыме халастога ходу на другой стадыі (xprintidle / іорег час прастою HIDIdleTime / PowerShell; парог ~15 хвілін).
- Шыфраванне RSA-4096 для кожнага з іх, а затым HTTPS POST да публічнага API замацавання IPFS.
Пакеты і версіі (npm, выдавец solbuilder_io)
| пакет | версіі |
|---|---|
base58-utils | 1.0.0, 1.0.1, 1.0.3 |
abi-encode | 1.0.0, 1.0.1, 1.0.2 |
eth-dev | 1.0.0, 1.0.1, 1.0.2 |
arb-kit | 1.0.0, 1.0.1 |
layer2-sdk | 1.0.0, 1.0.1 |
solana-key-utils | 1.0.0 |
eth-wallet-helpers | 1.0.0 |
crypto-validate-lib | 1.0.0 |
Выдавец
- solbuilder_io - angel_lopez89[@]proton[.]me, адрас электроннай пошты не правераны, няма праверанага ўліковага запісу сістэмы кантролю версій, няма звязанага рэпазітара.
Атрыбуцыя і назіраная паводзіны
Восем пакетаў маюць адзін уліковы запіс выдаўца і адну карысную нагрузку. Кожны з іх — гэта трывіяльны пакет — некалькі кілабайт рэальнага ўтылітнага кода з тым самым дададзеным дропарам — апублікаваны без звязанага рэпазітара і пад неправераным аднаразовым адрасам электроннай пошты. Гэтая аднастайнасць, агульны шлях падзення, агульныя меткі захоўвання і агульныя... keypairs.dat прамежкавы файл — гэта тое, што звязвае кластар разам.
Пакеты незвычайна шчырыя адносна сваіх паводзін. solana-key-utils змяшчае ўбудаваныя каментарыі, якія апісваюць далучаны блок простымі словамі — яго пазначаюць ФАНТОМ: нябачная настойлівасць, іншы (па-іспанску) чытае Выкінуць топіку на фоне, «запусціць крота ў фонавым рэжыме». Гэта ўласныя анатацыі кода аб тым, што ён робіць; назва кампаніі ў гэтай публікацыі ўзятая з гэтай першай меткі разам з фальшывым вузлом «дэман сінхранізацыі» (сінхранізаваны), які імітуе механізм настойлівасці.
Мы апісваем толькі тое, што робіць код, што можна назіраць. Назвы пакетаў — усе яны тычацца крыпта-, кашалькоў- і блокчэйн-інструментаў — паказваюць, што распрацоўшчыкі, хутчэй за ўсё, атрымаюць іх па імені; самі па сабе яны не ўстанаўліваюць, хто з'яўляецца выдаўцом. Другі этап можна было цалкам аднавіць шляхам дэкадавання фікстуры base64, і яго паводзіны апісана вышэй: ён збірае ключы кашалькоў, пачатковыя фразы і сакрэты і выносіць іх, зашыфраваныя з дапамогай RSA, у публічнае сховішча IPFS праз Pinata. Характэрным выбарам дызайну з'яўляецца адсутнасць прыватнага сервера C2 — канфігурацыя паступае з gist GitHub і IPFS, а скрадзеныя даныя захоўваюцца ў публічным дэцэнтралізаваным сховішчы, заключаным з дапамогай хэша кантэнту, абодва з якіх цяжэй захапіць, чым адзін хост, кантраляваны зламыснікам. На момант напісання тэксту папярэдніх публічных справаздач, якія адпавядаюць гэтаму кластару, не было знойдзена.
Уплыў, тэндэнцыі і рэкамендацыі для абаронцаў
Хто выкрыты. Любы, хто дадаў адзін з гэтых пакетаў у праект Node, а затым запусціў код, які яго імпартуе. Паколькі дэтанацыя адбываецца падчас імпарту, а не падчас усталёўкі, простага наяўнасці пакета недастаткова, але любое звычайнае выкарыстанне, якое загружае модуль, дасягае карыснай нагрузкі. Такія назвы прывабных модуляў накіраваны на распрацоўшчыкаў, якія будуюць на Ethereum, Solana, Arbitrum, Layer-2 і агульных інструментах для кашалькоў/кадавання — насельніцтва, якое, хутчэй за ўсё, мае менавіта тыя актывы, якія шукае другі этап. Любы, хто запусціў адзін з гэтых модуляў на машыне, якая захоўвае ключы кашалька, пачатковыя фразы, сховішчы ключоў, ключы SSH або... .env сакрэты павінны разглядаць гэтыя ўліковыя дадзеныя як скампраметаваныя і ратаваць іх.
Дзве мадэлі, якія варта засвоіць. Па-першае, карысная нагрузка як прыстасаванне: адпраўка другога этапу ў фармаце base64 унутры файла дадзеных з праўдападобнай назвай (тэст/прылады/пары ключоў.dat) захоўвае бачны зыходны код пакета чыстым і перамяшчае шкоднасны кантэнт у файл, які інструменты праверкі і чалавечы кантроль часта апрацоўваюць як інертныя дадзеныя. Па-другое, затрымка выканання з часам імпарту і кросплатформенная захаванасцьПеранясенне трыгера з усталявальнага перахопніка, даданне таймера і захаванне яго ў cron, запланаваных задачах і launchd — гэта наўмысны крок ад больш шумных метадаў усталявальных скрыптоў, за якімі найбольш уважліва сочыць аўтаматычнае сканаванне рэестра.
Рэкамендацыі для абаронцаў і тых, хто падтрымлівае:
- Апрацоўваць дададзены код пасля module.exports як прыярытэт праверкі — логіка дропа часта хаваецца пад паверхняй «рэальнага» модуля.
- Не мяркуйце, што файлы дадзеных інертныя. Аб'ект base64 пад тэст/прыстасаванні/ які зчытваецца падчас выканання і дэкадуецца, з'яўляецца сумежным з выканальным файлам; пазначаць чытанне падчас выканання файлаў фітынгаў, якія падаюць функцыя/эвал/напішыце-тады-нераст ланцужок.
- Шукайце сцежку падзення ~/.cache-db/.node-sync/ і для адзінак настойлівасці WinNodeSync (запланаваная задача) і com.apple.syncd (launchd) на машынах распрацоўшчыкаў, якія выцягнулі гэтыя назвы.
- Паведамляць пра працэсы Node, якія ствараюць запісы cron, запланаваныя задачы або заданні launchd — легітымныя бібліятэкі рэдка робяць гэта пры імпарце.
- Аддавайце перавагу файлам блакіроўкі і замацаваным версіям, а таксама праглядайце адрозненні любой новай невялікай залежнасці ад "ўтыліт", асабліва апублікаваныя пакеты з нулявой залежнасцю неправеранымі акаўнтамі без падлучанага рэпазітара.
Для абаронцаў рэестра важна адзначыць, што маніторынг перахопнікаў усталёўкі неабходны, але недастатковы: затрымка часу імпарту, дроппер, размешчаны ўнутры файла дадзеных, пройдзе праверку толькі падчас усталёўкі, і крок захавання часта з'яўляецца самым гучным сігналам, які трэба перахапіць.



