TL; DR
בין ה-15 ביוני 2026 ל-16 ביוני 2026, מפרסם NPM יחיד דחף שש חבילות - 26 גרסאות בסך הכל — שמריצים סקריפט בזמן ההתקנה על כל npm installכל חבילה כוללת קובץ README המצהיר שהיא כארכיון מחקר "שפיר" של HackerOne / GitHub Bug Bounty ש"לעולם לא" נוגע בערכי אישורים ו"לא מבצע שום שמירה". הקוד אינו תואם את הצהרת הוויתור. ברצי CI של לינוקס, ה-install hook מאתר את הסביבה ומבצע בדיקת נקודת קצה של מטא-נתונים של מופעי ענן; ב-Windows הוא מבקש העלאת ערך, פועל כ- SYSTEM, ופותח ערוץ פקודה למארח חיצוני. האינדיקטור היחיד לחפש כעת הוא מארח C2 173.255.233[.]239חומרה: גָבוֹהַמערכת אקולוגית מושפעת: Npmשמות משתלבים לתוך רעש תלות של אינטגרציה רציפה (metrics-pipeline-d8k2, event-metrics-q3x7, pkg-telemetry-r4f9, ושלושה אחים).
ההתקפה: איך זה עובד
כל חבילה באשכול בנויה באותו אופן. package.json מקשר את אותה פקודה לשני מחזורי חיים hooks:
{
"scripts": {
"preinstall": "node run.js",
"postinstall": "node run.js"
}
}רץ על שניהם התקנה מוקדמת ואחריה פירושו שהמטען מופעל בין אם שלבי התקנה מאוחרים יותר נכשלים ובין אם לאו. run.js עצמו הוא משגר פלטפורמה בן עשר שורות:
// Platform dispatcher — runs beaconNN.js on Windows, beacon_linux.js on Linux.
// GitHub Bug Bounty authorized research. Contact: nicholas@curran.tech
'use strict';
const { execFileSync } = require('child_process');
const path = require('path');
const node = process.execPath;
const script = process.platform === 'win32'
? path.join(__dirname, 'beacon34.js')
: path.join(__dirname, 'beacon_linux.js');
try { execFileSync(node, [script], { stdio: 'inherit', timeout: 90000 }); } catch (e) {}לאחר מכן, ההתנהגות מתפצלת לפי מערכת הפעלה. כל שש החבילות נושאות את אותם שני חלקים - run.js ומשואות הפלטפורמה - כך שהן דרופרים הניתנים להחלפה ולא שישה כלים נפרדים. ההבדל המשמעותי היחיד ביניהם הוא קצב הפרסום: ארבעה שמות נשלחו פעם אחת, בעוד ששניים שוחררו מחדש כל 30-60 דקות במשך שעות, מה ששומר על גרסה חדשה שפורסמה מול המתקינים והפותרים גם כאשר גרסה ישנה יותר נמשכת.
על לינוקס (beacon_linux.js) הסקריפט מבצע פרופילים של המקומות בהם הוא פועל. הוא מונה את שמות משתני הסביבה, קורא קבצים תחת / proc, בודק עבור docker.sock, ורץ המארח ו Whoamiלאחר מכן הוא שולח בקשת HTTP לנקודת הקצה של מטא-נתונים של מופע המכולה של AWS. 169.254.170[.]2 — כתובת הקישור המקומית שמשימת ECS מבצעת שאילתה כדי לקבל תצורה משלה ותפקידים קצרי מועד (IMDS, שירות המטא-נתונים של המופע). התוצאות נשלחות לאספן הקמפיין ב- 173.255.233[.]239.
ב- Windows (beacon34.js / beacon18.js) החבילה עושה הרבה יותר מאשר רק יצירת פרופיל של המארח שלה. הקוד המסומן מוסיף מפתח רישום מתחת ל- ms-הגדרות מטפל עם הנציג/הפעל ערך - עקיפת בקרת חשבון משתמש ידועה המאפשרת לתהליך שהופעל לפעול עם רמת הגברה ללא בקשה. הוא מפעיל את PowerShell עם -עקוףעקוף ו Invoke-Expression, מבצע ב- NT AUTHORITY\SYSTEM הקשר, ומבקש פקודות ממארח חיצוני. ערוץ הפקודות של Windows פועל מעל מנהרת קלאודפלאר, קוטר-מטבעות-מגבלות-הורים.trycloudflare[.]com:443, נופל חזרה ל 173.255.233[.]239:443, תוך שימוש בשלוש נקודות קצה: /c2/סקר כדי להביא פקודה, /c2/החוצה כדי להחזיר פלט, /c2/eof לסגור.
מה חדש כאן: הוויתור כהסוואה
האלמנט החדשני אינו המטען - install-hook reconnaissance ו-Windows elevation שניהם מתועדים היטב. זהו סיפור הכיסוי. כל חבילה נושאת קובץ README שנקרא כ-responsible-disclosure artifact:
# @ncurran/sandbox-recon-880538 — authorized npm-sandbox security researchזה שָׁפִיר חבילה שפורסמה תחת טווח `@ncurran` של המחבר עצמו כחלק מ- מעורבות מורשית של HackerOne / GitHub Bug Bounty (npm נמצא במסגרת).
בהתקנה הוא בודק סביבת ביצוע משלה ... משתנה סביבתי
שמות מפתח בלבד ... בודק האם ידוע נקודות קצה של מטא-דאטה בענן יש לו ניתן להשגה... הוא מדווח רק על קודי סטטוס, אורכי תגובה ו... גיבובים — לעולם לא כל אישור, ערך אסימון או נתוני צד שלישי. הוא לא מבצע שום התמדה, ללא תנועה צידית וללא פעולה הרסנית.
שלוש טענות בטקסט זה סותרות את הקוד שנשלח. קובץ ה-README אומר "ללא שמירה על נוכחות", אך קוד ה-beacon של Windows כותב מפתח עלייה מבוסס רישום. הוא אומר "לעולם לא ערך אישורים או אסימון", אך נתיב Windows מריץ פקודות מורשות שרירותיות כ- מערכת, אשר אינו מציב מגבלה כזו על מה שנקרא או מוחזר. הוא אפילו נותן שם לחבילה - @ncurran/sandbox-recon-880538 — זה לא התבנית שבה הוא נשלח, מה שמצביע על כך שקובץ ה-README הוא תבנית בשימוש חוזר ולא תיאור של החפץ שביד. הצהרת הסכמה בחבילה שהקורבן מעולם לא הסכים להתקין אינה נותנת הסכמה. הסיווג כאן זדוני ללא קשר לניסוח. להצהרת ההסכמה יש גם מטרה שקטה יותר: שלב מיון אוטומטי שקורא את טקסט החבילה לאיתור אותות מרגיעים — "שפיר", "מורשה", תוכנית בעלת שם, כתובת דוא"ל ליצירת קשר — ניתן לדחוף אותו לפסק דין בטוח באמצעות ניסוח שהמטען סותר. הלקח נכון גם לבודקים אנושיים ומכונתיים כאחד: שקלו את ההתנהגות בזמן ההתקנה, לא את התיאור העצמי של קובץ ה-README.
ציר זמן
כל החבילות הופיעו בתוך חלון של 24 שעות. ארבעת החבילות בעלות הגרסה היחידה נחתו ראשונות, ולאחר מכן שתי חבילות שפורסמו מחדש כל 30-60 דקות.
| תאריך (UTC) | אירוע |
|---|---|
| 2026-06-15 18:32 | חבילה ראשונה שפורסמה - npm:pkg-telemetry-r4f9@1.0.0 |
| 2026-06-15 19:50 | npm:runtime-metrics-w7k2@1.0.0 לאור |
| 2026-06-15 20:14 | npm:build-tracker-n5p1@1.0.0 לאור |
| 2026-06-15 20:35 | npm:npm-sandbox-ping-r9t2@1.0.0 לאור |
| 2026-06-15 21:09–22:42 | npm:event-metrics-q3x7 לאור 1.0.0 → 1.0.8 (9 גרסאות) |
| 2026-06-15 23:40 → 2026-06-16 04:40 | npm:metrics-pipeline-d8k2 לאור 1.0.0 → 1.0.10 (11 גרסאות) |
| 2026-06-16 | אשכול זוהה וסווג כזדוני; מספר קבצי tarball כבר מחזירים 404 ברישום (מתמשך) |
הקמפיין הוא חדש וההסרה עדיין בתהליך של יישוב: בזמן הניתוח, חלק מהגרסאות נפתרו ברישום ואחרות לא. יש להתייחס לכל 26 הגרסאות כנחשפות.
אינדיקטורים לפשרה
מארזים
| מערכת אקולוגית | חֲבִילָה | גרסאות | מצב |
|---|---|---|---|
| Npm | metrics-pipeline-d8k2 | 1.0.0 – 1.0.10 | זדוני |
| Npm | event-metrics-q3x7 | 1.0.0 – 1.0.8 | זדוני |
| Npm | runtime-metrics-w7k2 | 1.0.0 | זדוני |
| Npm | build-tracker-n5p1 | 1.0.0 | זדוני |
| Npm | npm-sandbox-ping-r9t2 | 1.0.0 | זדוני |
| Npm | pkg-telemetry-r4f9 | 1.0.0 | זדוני |
רשת
| סוּג | אינדיקטור | הערות |
|---|---|---|
| IP | 173.255.233[.]239 | אספן C2; POST של גילוי לינוקס וחלופה של Windows :443 |
| תְחוּם | diameter-coins-limitations-parents.trycloudflare[.]com | ערוץ פקודות של Windows מעל מנהרת Cloudflare, :443 |
| נתיב URI | /c2/poll, /c2/out, /c2/eof | פקודות Windows סקר / פלט / סגירה |
| IP | 169.254.170[.]2 | נקודת קצה של מטא-נתונים של מופע AWS ECS נבדקה מ-install hook |
התנהגות
| לאותת | תיאור |
|---|---|
| התקן hooks | preinstall ו postinstall שניהם רצים node run.js |
| שיגור פלטפורמה | run.js פועל beacon_linux.js ב- Linux, beacon34.js / beacon18.js על Windows |
| סיור לינוקס | מונה שמות מפתחות של env-var, קורא /proc, צ'קים docker.sock, רץ hostname / whoami, בדיקות IMDS |
| גובה חלונות | ms-settings DelegateExecute עקיפת UAC ברישום; PowerShell -ExecutionPolicy Bypass + Invoke-Expression; פועל כ SYSTEM |
| לְהַסווֹת | README טוען למחקר "שפיר", "מורשה" ו"ללא נוכחות"; מפנה לשם חבילה שלא נשלח בו |
ייחוס והתנהגות נצפית
אנו מתארים את המוציא לאור באמצעות אותותיו בלבד ואיננו טוענים לזהותו של אף אדם העומד מאחורי החשבון.
- קטלוג אותות. כל שש החבילות פורסמו על ידי אותו חשבון npm, npmresearch8847, עם האימייל המצוין npmresearch8847@web-library.net (דוא"ל ו SCM אימות שניהם חסר; לא אימתנו בעלות). קבצי ה-README מתפרסמים תחת @ncurran היקף ולתת קשר של nicholas@curran.tech, ו run.js נקודות במאגר github.com/ncurran/npm-sandbox-research`. שש החבילות חולקות מבנה זהה run.js משגר, נפוץ beacon_linux.js שלב הסיור, ומארח האספן היחיד 173.255.233[.]239`. סכמת השמות עקבית: גזע גנרי שנשמע כמו CI (מדדים, טלמטריה, מעקב אחר בנייה, פינג ארגז חול) בתוספת סיומת קצרה אקראית.
- אמון. נראה כי שש החבילות הן קמפיין אחד המבוסס על המשגר המשותף, שלב הסיור ומחשב C2. נראה כי המסגור של "מחקר מורשה" עקבי בכל ששת קבצי ה-README. לא הצלחנו לאשר שאף תוכנית באגים-באונטי אישרה פעילות זו, וטענות הוויתור אינן תואמות את הקוד שנשלח.
- יכולת נצפית. המטען הממומןcisארבע מחלקות יכולות: ביצוע קוד install-hook בשניהם התקנה מוקדמת ו לאחר ההתקנהסיור מארח וסביבת CI עם יציאה לאספן חיצוני; בדיקת נגישות של מטא-נתונים של מופעי ענן מהקשר ההתקנה; וב-Windows, הסלמת הרשאות מקומיות, ביצוע כ- מערכת, ולולאת פקודה מסוג "אחזור והפעל" דרך ערוץ חיצוני. לולאת הפקודות של Windows היא יכולת שליטה מרחוק: היא סורקת הוראות ומחזירה את הפלט שלהן.
השפעה, מגמות ומה על מגינים לעשות
פְּגִיעָה
החשיפה היא הגבוהה ביותר עבור סביבות בנייה אוטומטיות. התקנה של npm ב-CI מפעילה את מחזור החיים hooks עם כל זהות שיש לרץ; ברץ ענן, זהות זו כוללת לעתים קרובות נקודת קצה של מטא-דאטה שניתן להגיע אליה ואישור תפקיד. הכתובת שבה בודקת המשואה של לינוקס, 169.254.170[.]2, הוא נקודת הקצה של מטא-נתוני המשימות של AWS ECS: משימה שיכולה להגיע אליה יכולה בדרך כלל גם לאחזר את האישורים הזמניים של תפקיד המשימה שלה מאותו שירות קישור-מקומי. בדיקת נגישות מתוך סקריפט התקנה היא השלב שקודם לשליפת האישורים הללו, כך שכל רץ שהריץ את ה-hook ויכול להגיע לנקודת הקצה הזו צריך להיחשב כאילו תפקיד המשימה שלו נחשף. במפתח או במארח של Windows, המטען מבקש העלאת גישה ופותח ערוץ פקודות, מה שמרחיב את החשיפה מפרופיל ויציאה יחידים לשלט רחוק אינטראקטיבי.
לא הצלחנו להשיג ספירת הורדות אמינה עבור הגרסאות הזדוניות לפני ההסרה, לכן איננו מעריכים את מספר הקורבנות. הפרסום המחודש המהיר של שתיים מהחבילות - אחת עשרה ותשע גרסאות תוך שעות - השאיר תקלות חדשות מול מתקינים ופותרים בזמן שגרסאות ישנות יותר הוסרו.
דפוסים מתעוררים
אשכול זה הוא דוגמה לתווית המשמשת ככיסוי. "מחקר מורשה", "באגים באונטי" ו"בדיקות ארגז חול" מצורפים יותר ויותר לחבילות שסקריפטי ההתקנה שלהן עושים יותר מאשר פרופיל של מארח. המסגור מסתמך על היסוסו של סוקר לפעול נגד משהו שמכריז על עצמו כמאושר. זה משתלב עם טקטיקה מוכרת שנייה: שמות חבילות מהונדסים להיראות כמו כלי CI פנימיים כך שהם חומקים מעבר למבט חטוף בעץ תלות. השילוב הוא מה שהופך את הדפוס לכדאי לעקוב אחריו - שם שנקרא כאינסטלציה של תשתית, עטוף בפרוזה שנקראת כבדיקה מאושרת, המציג וו התקנה שמסלים במערכת הפעלה אחת ובודק אישורי ענן באחרת. לא הוויתור ולא השם התפל משנים את מה שהקוד עושה בהתקנה, ותהליך מיון ששוקל כל אחד מהם כראיה מקלה יגיע לפסק דין שגוי.
מה שגופי ההגנה צריכים לעשות
- חפש קבצי נעילה והתקן יומני רישום עבור ששת שמות החבילות; התייחס לכל התאמה כאל תקרית, לא כאזהרה.
- חסימה והתראה על תנועה יוצאת אל 173.255.233[.]239 כדי *.trycloudflare[.]com מארחים מבניית תשתית.
- חפש בקשות HTTP בזמן ההתקנה לכתובות מטא-נתונים של מופעים (169.254.170.2, 169.254.169.254) שמקורם במנהלי חבילות ב-CI.
- סובב את כל פרטי הכניסה והטוקנים של תפקידי ענן שהיו נגישים מרַץ שהתקין גרסה מושפעת.
- ביקורת התקנה מוקדמת/לאחר ההתקנה סקריפטים בך קבוצת תלותראוי לקריאה של hook של מחזור חיים שמריץ קובץ סקריפט שני.
- בנקודות קצה של Windows, חפש נקודות חדשות הנציג/הפעל ערכים שנכתבו תחת ms-הגדרות מפתח shell-handler - פרימיטיבי הגובה בו משתמש מטען זה ואות אמין שאינו תלוי בשם החבילה.
- הצמד ובדוק קבצי נעילה, והשבת סקריפטי התקנה (התקנת npm –ignore-scripts) ב-CI שבו הבנייה שלך אינה דורשת אותם.
- התייחסו ל"מחקר מורשה" או כתבי ויתור "שפירים" בתוך סקריפט התקנה כטקסט לא מהימן, ולא כסיבה לאפשר את הסקריפט.
חבילה המכריזה על כוונות טובות בפרוזה ובבקשות מערכת בקוד יש לשפוט על סמך הקוד.







