TL; DR
עשרים חבילות npm שפורסמו במשך יומיים בסוף יולי ותחילת אוגוסט 2026 מציגות את עצמן כספריות רכיבי ממשק משתמש פנימיות וככלי שירות משותפים לתמיכה בשרת. אף אחת מהן אינה מיישמת את הפונקציונליות שהן מפרסמות. כל אחת מהן כוללת מחלקת stub, קובץ README המתאר את החבילה כפנימית, וקובץ בשם _polyfill.js.
החלק המעניין הוא מאיפה הקובץ הזה נקרא. זה לא וו התקנה — המניפסטים אינם מצהירים כלל על קדם-התקנה, התקנה או התקנה לאחר ההתקנה. במקום זאת, השורה האחרונה של index.js קורא: try { require("./_polyfill"); } catch (_) {}המטען פועל בפעם הראשונה שקוד האפליקציה מייבא את החבילה. התקנה באמצעות --ignore-scripts, ה standard עצות להפחתת החשיפה לשרשרת האספקה בזמן ההתקנה, לא מונע את זה.
מה _polyfill.js מה שעושה זאת הוא לאחזר קובץ הרצה ספציפי לפלטפורמה מאחד משלושה מארחי Cloudflare Workers, לכתוב אותו לספרייה זמנית תחת שם אקראי, לסמן אותו כניתן להרצה ולהפעיל אותו במנותק מתהליך האב. קובץ ההרצה הזה הוא בעצמו טוען: הוא מונה מוצרי אבטחת נקודות קצה לפי שם התהליך, בודק את שם המשתמש הנוכחי מול חשבונות סביבת הניתוח הידועים, מתקן את ממשקי הטלמטריה AMSI ו-ETW בזיכרון, מתקין persistence תחת שם רכיב שמע של Windows ומאחזר קובץ בינארי שלישי בגודל של כמה מגה-בייטים. השלב השלישי מגיע מוצפן והוא מפוענחים ומבוצעים בזיכרון במקום להיכתב לדיסק.
ב-1 באוגוסט, כארבע שעות לאחר שהחבילות אושרו כזדוניות בבדיקה ידנית, תשעה עשר מתוך עשרים השמות הוסרו מ-npm על ידי המפרסם שלהם ברצף של עשרים וחמש שניות. ב-2 באוגוסט אחד מאותם שמות נרשם שוב - על ידי חשבון אחר, הנושא את אותו קוד. תשתית האספקה שירתה את שלושת השלבים בעת כתיבת שורות אלה.
| מארזים | 20 שמות npm, ~74 גרסאות שפורסמו |
| מתן שמות לערכות נושא | beaver-ui-* (13), accounts-* (3), ועוד afisha-storybook-default, bcore-bravo-eslint-config, arbocrate-sla-prober-arbocrate-sla-prober-core, a.poltoradnev-package-c |
| הדק | ייבוא מודול (require), לא סקריפט מחזור חיים של npm |
| שלב 1 | _polyfill.js — אחזור ושיגור |
| שלב 2 | טוען מקושר סטטית, ארוז ב-UPX בלינוקס; ספירת אבטחת נקודות קצה, תיקון AMSI/ETW, שמירה על נוכחות |
| שלב 3 | beacon_linux.bin / beacon_mac.bin / update_win.exe, 4–13 מגה-בייט; מסופק מוצפן, מבוצע בזיכרון |
| פלטפורמות | לינוקס x64 ו-arm64, macOS אוניברסלי, Windows x64 |
| מוציאים לאור | שני חשבונות, בסיס קוד אחד |
| מצב | 19 מתוך 20 שמות שלא פורסמו על ידי המפעיל; 1 פורסם מחדש ופעיל; תשתית המסירה פעילה |
אנטומיה של התקפה
שלב 1 - החבילה
כל חבר שולח את אותה צורה בת שלושה קבצים. index.js מייצא מחלקה עם init () שמגדיר ערך בוליאני, א גִרְסָה()ו הגדרת () שממזג אובייקט אפשרויות. שום דבר אחר. README.md נותן שורת התקנה, א לדרוש שורה, והמשפט "חבילה פנימית. ראה CONTRIBUTING.md להנחיות" — CONTRIBUTING.md לא נמצא בטארבול.
_polyfill.js קצת פחות מ-5 קילו-בייט ונקרא כקוד תשתית רגיל. הוא נפתח עם שלוש בדיקות סביבה:
if (process.env.DISABLE_TELEMETRY ||
process.env.ANALYTICS_OPT_OUT ||
process.env.DO_NOT_TRACK)
return;משתנים אלה הם בקרות פרטיות קונבנציונליות. כאן הם קובעים האם קובץ בינארי הוריד ויופעל. מכונה שמגדירה אל תעקוב מדלגים עליו לחלוטין.
לאחר מכן מגיע חותמת הפעלה חד פעמית: קובץ ב /tmp/.analytics_state, או מצב_אנליטיקה תחת TEMP%% ב-Windows, שזמן השינוי שלו מושווה לחלון של 20,357 שניות. בתוך חלון זה המודול חוזר באופן מיידי, כך שיבוא חוזר במהלך בנייה מייצר ניסיון אחזור אחד במקום עשרות.
מארחי היעד אינם קיימים כליטרלים. הם מורכבים בזמן ריצה מפרגמנטים:
["oob-worker.cf102-","baf.workers.d","ev"].join("")
שלושה מארחים בנויים כך ומעורבבים לפני השימוש, כך שהסדר משתנה לפי ביצוע. אחזור הוא HTTPS GET המוצמד ל-IPv4 עם User-Agent of node-fetch/2.6 — ערך סביר עבור תהליך Node לפלוט. תגובה מתחת ל-1,000 בתים נמחקת ונעשה ניסיון לשרת המארח הבא.
אם שלושתם נכשלים, המודול חוזר ל-DNS. הוא שולח שאילתת TXT עבור ג. כדי לקבל ספירת נתחים, ואז בקשות 0. דרך נ. בקבוצות של עשר, משרשר את התשובות ומפענח את התוצאה באמצעות base64. נתיב זה נושא את אותה קובץ הרצה על רשומות DNS TXT, אשר שורדות סביבות בהן HTTP יוצא מסונן אך רזולוציית DNS לא.
הבייטים שאוחזרו נכתבים אל /var/tmp/.cache_<8-hex> על יוניקס, או %TEMP%\dotnet_diag_<8-hex>.exe ב-Windows - שם קובץ שנקרא כ-.NET Diagnostic Artifact. ב-Unix הקובץ מקבל מצב 0755. הפעלה היא להשריץ עם מנותק: נכון, stdio: "התעלם", ו לא רפרנס (), כך שהילד שורד יותר מתהליך ה-Node שהתחיל אותו; ב-Windows הקריאה מוסיפה חלונותהסתרה: נכון.
שני פרטים משלימים את התמונה. עוזר המוגדר כ const _l = (…a) => process.stderr.write("") נקרא בכל שלב של האחזור - הוא כותב מחרוזת ריקה וזורק את הארגומנטים שלה, תופס את המיקום שבו היה אמור להיות רישום. וכל חבילה שולחת קובץ של 81 KB ב lib/telemetry.js שהערת הכותרת שלה מתארת "Analytics SDK" עם "הובלת HTTPS עם סיבוב נקודות קצה וניסיון חוזר" ו"גילוי שירות מבוסס DNS עבור גיבוי". בגרסה הנוכחית שום דבר לא דורש זאת.
שלב 2 - המטען
קובץ הבינארי של לינוקס x64 הוא קובץ ELF מקושר סטטית ודחוס עם UPX 3.96. גירסת ה-macOS היא קובץ בינארי אוניברסלי המכסה את x86_64 ואת arm64; גירסת ה-Windows היא קובץ הפעלה של קונסולת PE32+ חסום. ארבעתם אוחזרו ונבדקו סטטית; אף אחד מהם לא בוצע.
ייבוא וטבלת המחרוזות של גירסת ה-Windows מתארים את התנהגותה בצורה ברורה. היא פותרת האם ניפוי באגים נוכח, CheckRemoteDebuggerPresent, ו תהליך מידע של NtQuery, וקורא SYSTEM\CurrentControlSet\Services\Disk\Enum — נתיב רישום שערכיו מזהים בקרי דיסק וירטואליים. הוא קורא GetDiskFreeSpaceExW ו סטטוס זיכרון גלובלי (GlobalMemoryStatusEx), בהתאם לבדיקה האם משאבי המארח תואמים למופע ניתוח קצר מועד.
הוא מכיל רשימה של שמות משתמש: משתמש נוכחי, מנהל IT, הונג לי, תיבת בדיקה, הפובווס, מילוז, מתוק, מנהל, תוכנות זדוניות, לִטעוֹם, וירוס, סביבהכמה מאלה הם ממצאים של סביבות ניתוח מתועדות באופן ציבורי.
הוא כולל גם רשימה של מוצרי אבטחת נקודות קצה ושמות התהליכים שלהם, המפורטים עד CreateToolhelp32 תמונת מצב ו תהליך32ראשוןW/תהליך32הבא:
- CrowdStrike Falcon (
csfalconservice,CSFalconContainer) - שחור פחם (
cb.exe) - סיילנס (
cylancesvc.exe) - BitDefender (
bdagent.exe) - קספרסקי (
avp.exe,kavfs.exe) - Trend Micro ו-Deep Security (
coreServiceShell,ds_agent.exe) - FireEye/Trellix (
xagt.exe) - טניום (
taniumclient.exe) - HitmanPro (
hmpalert.exe) - אלסטי (
elastic-agent.exe,elastic-endpoint,filebeat.exe,winlogbeat.exe) - ווזוה ו-OSSEC (
wazuh-agent.exe,ossec-agent.exe) - צק פוינט
- Defender Windows
שני סמלים נוספים קיימים: AmsiScanBuffer, נקודת הכניסה ש-Windows חושפת לסריקת תוכן בזיכרון, ו כתיבה לאירועים, הפונקציה שדרכה Event Tracing for Windows מתעד אירועים. שתיהן מטרות נפוצות לשינוי בתהליך שמדכא את הטלמטריה שממשקים אלה היו מייצרים אחרת.
Persistence מופיע פעמיים ב-Windows. הקובץ הבינארי מועתק אל %APPDATA%\Microsoft\Windows\Themes\SystemSoundsService.exe, ושני מנגנונים מפנים אליו: ערך תחת תוכנה\מיקרוסופט\חלונות\גרסה נוכחית\הפעלה, ומשימה מתוזמנת שנרשמה כ
schtasks /Create /F /SC ONLOGON
/TN "\Microsoft\Windows\Multimedia\SystemSoundsService"
/TR "\"%s\""
/RL HIGHESTנתיב המשימה, שם המשימה ושם הקובץ ההפעלה משקפים כולם רכיב שמע מקורי של Windows. /RL הגבוה ביותר מבקש את רמת ההרשאה הגבוהה ביותר הזמינה.
בניית macOS עוקבת אחר אותו דפוס עם שמות המתאימים לפלטפורמה: היא כותבת רשימת מאפיינים אל ~/Library/LaunchAgents/com.apple.windowserver.helper.plist וממקם את ההפעלה שלו ב ~/.local/share/runtime/com.apple.runtimeזה בודק אם /יישומים/VMware Fusion.app ו /ספרייה/תמיכה ביישומים/כלי VMware.
יכולת הרשת משתרעת על פני שלוש תעבורות: WININET.dll עבור HTTPS עם https://%s:%d%s מחרוזת עיצוב, WS2_32.dll שקעים, ו DNSAPI.dll עם DnsQuery_A לצד א ג.%s.%s מחרוזת פורמט - אותה צורת שאילתת chunk-count שבה משתמשת השלב הראשון.
התיאור לעיל מציג את מה שמוצג על ידי ייבוא, מחרוזות ונתיבי רישום של קובץ בינארי בבדיקה. ניתוח ציבורי מרובה מנועים של אותה דוגמת Windows מוסיף פרטים שקריאה סטטית של טבלת ייבוא אינה יכולה לקבוע בפני עצמה, והוא מיוחס כאן ולא מוצג מחדש כתצפית ממקור ראשון. ניתוח זה מדווח כי ההפניות ל-AMSI ול-ETW לא רק נפתרו אלא גם תוקנו - AmsiScanBuffer in amsi.dll ו כתיבה לאירועים in ntdll.dll — אשר מדכא סריקת תוכן בזיכרון ומעקב אחר אירועים עבור התהליך. הוא מדווח על בדיקות האנטי-אנליזה כמבוססות תזמון בנוסף ל- CheckRemoteDebuggerPresent, ומזהה את קבל הקשר של חוט קריאה כבדיקת נקודת עצירה של חומרה הקוראת את אוגרי ניפוי השגיאות. היא גם מדווחת על mutex של מופע יחיד, Global\{7A3B9E1F-C2D4-4F8A-B5E6-D7A1F0C3E2B9}, ושמות שירות צלילי מערכת כערך Run-key. ערך mutex זה לא היה בין המחרוזות ששוחזרו כאן - מסנן החילוץ ששימש לא כלל אסימונים משושים באותיות גדולות-הקסדואליות - והוא נרשם בטבלת האינדיקטורים על סמך ניתוח ציבורי זה ולא על סמך תצפית ישירה.
שלב 3
הטוען מפנה לקבוצה נוספת של נתיבים באותם מארחים. כולם היו נגישים במהלך הניתוח: beacon_linux.bin ב-5.3 מגה-בייט, beacon_mac.bin ב-13.3 מגה-בייט, update_win.exe ב-10.3 מגה-בייט, ו- arm.bin בגודל 4.3 מגה-בייט. אלה לא אוחזרו, והתנהגותם אינה ידועה.
עבור נתיב Windows, ניתוח ציבורי של ה-loader מתאר כיצד מטופל השלב השלישי: הוא נלקח דרך WinINet, מגיע מוצפן, ומפוענח בזיכרון ומבוצע באופן רפלקטיבי - זיכרון מוקצה, ייבוא נפתר, העברת נתונים מעובדת, והשליטה מועברת לשרשור חדש דרך צור שרשורמסיבה זו, השלב השלישי לעולם לא נכתב למערכת הקבצים, כך שלזיהוי מבוסס קבצים ולזיהוי פלילי של דיסק לאחר תקרית אין מה לשחזר ממנו. משמעות הדבר היא גם שאחזור הבייטים ממארח המסירה יניב טקסט מוצפן, ולא קובץ הרצה שניתן לנתח.
ציר זמן
כל חותמות הזמן הן UTC ומגיעות ממטא-דאטה של רישום npm, אשר מתעד יצירה וביטול פרסום של חבילות ללא תלות בכל צופה.
| מתי | מה |
|---|---|
| 2026-07-31 09:25:05 → 09:29:56 | עשרה שמות נוצרו, כל אחד מתחיל בגרסה 0.0.1, בהפרש של בערך שלוש עשרה שניות. |
| 2026-08-01 09:38 → 11:29 | שמות קיימים מטפסים דרך מספרי גרסה מנופחים, 6.1.9 דרך 6.1.12, 9.9.9 דרך 9.9.12, 8.9.3 דרך 8.9.6, 5.4.6 דרך 5.4.9. |
| 2026-08-01 13:04:14 → 13:04:18 | תשע נוספות beaver-ui-* שמות שנוצרו בפרץ של ארבע שניות, כל אחד מהם פורסם ישירות בגרסת 12.x ללא היסטוריה קודמת. |
| 2026-08-01 ~13:00–14:30 | גרסאות שסווגו כזדוניות בבדיקה ידנית. |
| 2026-08-01 17:39:49 → 17:40:14 | תשעה עשר מתוך עשרים שמות לא פורסמו תוך עשרים וחמש שניות, ברצף, בהפרש של כ-1.2 שניות זה מזה. |
| 2026-08-02 18:58:41 | a.poltoradnev-package-c נוצר שוב תחת חשבון מתחזק אחר, גרסה 0.0.2. |
| 2026-08-02 19:20:28 | גִרְסָה 33.9.5 פורסם תחת אותו שם. |
| 2026-08-02 20:14 | כל שלושת מארחי המסירה משרתים את שלבים שתיים ושלוש; פתרון חלופי של DNS עונה באמצעות ספירת חבילות. |
שניים מהערכים הללו ראויים להתעכב עליהם.
פרץ ה-13:04 הכניס תשע חבילות לרישום תוך ארבע שניות, כל אחת עם גרסה אחת ב-12 ו-non 0.0.1 מתחתיו. מספרי גרסאות בסקאלה הזו, על שמות בני יום אחד, אינם מתארים היסטוריית יציאה.
רצף ביטול הפרסום הוא מכני באותה מידה: תשעה עשר שמות, עשרים וחמש שניות, מרווח שווה. npm מתעד את אלה כאירועי ביטול פרסום רגילים ולא כמציין המיקום שהסרה ביוזמת הרישום משאירה מאחור, מה שממקם את הפעולה עם חשבון הפרסום במקום עם הרישום.
אינדיקטורים של פשרה
מארחים ונתיבים עברו דה-פאנג'. אין לפתור או לאחזר אותם ממערכת ייצור.
| סוּג | ערך |
|---|---|
| מארח משלוחים | oob-worker[.]cf99-9b3[.]workers[.]dev |
| מארח משלוחים | oob-worker[.]cf102-baf[.]workers[.]dev |
| מארח משלוחים | oob-worker[.]cf103-070[.]workers[.]dev |
| תעבורת DNS | tin[.]dl[.]well1[.]site (לינוקס x64), tina[.] (arm64), ldr[.] (macOS), win[.] (Windows) — ספירת קטעים ב c.<domain>, נתונים ב <n>.<domain> |
| מסלולי שלב 2 | /pkg/package, /pkg/package-arm64, /pkg/loader_mac, /pkg/package.exe |
| מסלולי שלב 3 | /pkg/beacon_linux.bin, /pkg/beacon_mac.bin, /pkg/update_win.exe, /pkg/arm.bin |
| נתיב שחרור (יוניקס) | /var/tmp/.cache_<8-hex> |
| נתיב שחרור (Windows) | %TEMP%\dotnet_diag_<8-hex>.exe |
| התמדה (חלונות) | %APPDATA%\Microsoft\Windows\Themes\SystemSoundsService.exeמשימה \Microsoft\Windows\Multimedia\SystemSoundsServiceערך מפתח ריצה SOFTWARE\Microsoft\Windows\CurrentVersion\Run\SystemSoundsService |
| מוטקס (חלונות) | Global\{7A3B9E1F-C2D4-4F8A-B5E6-D7A1F0C3E2B9} |
| התמדה (macOS) | ~/Library/LaunchAgents/com.apple.windowserver.helper.plistבינארי ב ~/.local/share/runtime/com.apple.runtime |
| חותמת "הפעלה חד פעמית" | /tmp/.analytics_state, %TEMP%\analytics_state (חלון של 20,357 שניות) |
| בקשת חתימה | HTTPS GET, מוצמד ל-IPv4, סוכן משתמש: node-fetch/2.6 |
| קובץ שלב 1 | _polyfill.js, SHA-256 392dc9ec85042ce1796bfa9b8c7bf6e05c7710579aab8b7b1eb73788683883af |
| קובץ ללא הפניה | lib/telemetry.js, SHA-256 2266c9b8e199daebe31a9afcb61d9b0d76706b63c92312fc8223d22de42e63c7 |
| שלב 2 (לינוקס x64) | SHA-256 7e486657f30594afda379b97030252a09a19fe8055e25c9e371544f59bd8e9e3 |
| שלב 2 (לינוקס arm64) | SHA-256 aafc37589c0d21fb729761a1579ca3b278b95bce152aacfa29243ffd27f5eeb7 |
| שלב 2 (macOS) | SHA-256 c214746c74cae8ece8bdaf69aa05da4db6ce013f9e77452d1eed1a002fd9ba00 |
| שלב 2 (חלונות) | SHA-256 d632c2c0c7801c3da57cf849fe4b4ca8351d9a7fe73c31050bf5dbcc96a16791 |
שמות חבילות
ייחוס והתנהגות נצפית
מדובר בשני חשבונות npm. תשעה עשר השמות שלא פורסמו פורסמו על ידי חשבון אחד; a.poltoradnev-package-c נרשם מחדש ב-2 באוגוסט תחת שם חשבון אחר ודומיין דוא"ל שונה. הקוד הוא מאותו שושלת בשניהם, והרישום השני נושא את אותם מארחי מסירה, אותם דומיינים של DNS backback, אותו מבנה מסלול שחרור ואותו חותמת הפעלה חד פעמית.
הזוג שפורסם מחדש מראה גם כיצד הוא הורכב. 0.0.2 ו 33.9.5 זהים לבייט מלבד מחרוזת הגרסה ב package.json, וה index.js נשלח כ 0.0.2 מצהיר גרסה קבועה = "33.9.5"שני הממצאים נחתכו מעץ מקור אחד וממוספרו לאחר מכן. אין 0.0.2 היסטוריית פיתוח עבור ה- 0.0.2 שחרור כדי לייצג.
מספור הגרסאות על פני כל הסט עוקב אחר אותו היגיון. שמות חבילות בקמפיין זה נקראים כדברים שקיימים בתוך ארגון ולא במרשם ציבורי: סט רכיבי ממשק משתמש, תצורת ESLint, בדיקת SLA, חבילה על שם אדם. כאשר בנייה פותרת תלות לפי שם ומתייעצת עם מרשם ציבורי לצד מרשם פרטי, מספר הגרסה הגבוה יותר הוא זה שמנצח. שם שנוצר ב 0.0.1 ביום שני ונושאת 12.9.6 ביום שלישי ממוצב להשוואה זו.
שני גלי הפרסום נבדלים זה מזה באופן שמרמז על איטרציה. הקוד הקודם כינה את קובץ המטען שלו setup.js ופירטו חמישה מארחי מסירה; הקובץ הנוכחי נקרא _polyfill.js ומפרטת שלושה. הגרסה הקודמת הסירה את הקובץ שנמחק חמש שניות לאחר ההפעלה, והותירה את התהליך פועל מ-inode שנמחק; הגרסה הנוכחית לא עושה זאת. הקובץ ללא הפניה lib/telemetry.js נשאר בקובץ tarball על פני שניהם, עדיין נשלח, כבר לא מחובר.
כדאי לציין תצפית אחת לגבי מתגי הכיבוי מראשcisאלי, כי זה סותר קריאה נפוצה. השבתת_טלמטריה, ניתוח_הסכמה_להוציא_אנליטיקה, ו אל תעקוב מכובדים, והם נבדקים לפני שמשהו אחר קורה. חוקר שסביבת הניתוח שלו מגדירה אחת מהן צופה בחבילה שלא עושה דבר.
השפעה ומה המגנים יכולים לעשות
התוצאה המעשית של הטריגר בזמן הייבוא היא צרה וראוי לציין אותה בפני עצמה. התקנת npm –ignore-scripts מונע הפעלה של סקריפטי מחזור חיים, וזו עצה טובה. אין לכך השפעה כאן, מכיוון שקוד זה אינו משתמש בסקריפט מחזור חיים. המטען פועל כאשר קוד יישום, ריצת ניסיון, bundler או linter מייבאים תחילה את המודול. ב-CI runner שמתקין ואז בונה מיד, השניים נמצאים במרחק של שניות זה מזה.
בציד, האותות העמידים הם מבניים ולא מילוליים. המארחים, שם הקובץ ונתיבי השחרור ישתנו; צורות אלו קשות יותר לשינוי:
- נקודת כניסה למודול שההצהרה הסופית שלה היא נסה { require("./x"); } catch (_) {} סביב קובץ שאינו חלק מה-API המיוצא. חבילה עם פוליפילו מקורי מייבאת אותו בחלק העליון ומשתמשת בתוצאה שלו. בליעת כל שגיאה מייבוא בתחתית הקובץ אינה טיפול בשגיאות.
- מחרוזות מארח שהורכבו על ידי .לְהִצְטַרֵף("") מתוך מערך של שברים. לקוד לגיטימי אין סיבה לפצל שם מארח על פני אלמנטים של מערך.
- חבילה שתלויותיה המוצהרות ריקות אך מושיטה יד ל- https, DNS, ו תהליך_ילד בזמן הייבוא. אחזור בתוספת ביצוע בחבילה שמפרסמת רכיב ממשק משתמש היא הממצא כולו.
- מספרי גרסה אינם תואמים לגיל החבילה. שם שפורסם לראשונה לפני ימים בגרסה ראשית דו-ספרתית, ללא מהדורות ביניים, מתאר משהו שאינו התפתחותו.
- כותב אל / var / tmp or TEMP%% ואחריו א chmod ל-0755 וספון מנותק. הרצף קצר, מסודר, ורק לעתים רחוקות שפיר יחד.
במחשבי Windows ישנן שתי בדיקות זולות ובעלות ביטחון גבוה. אובייקטי ההתמדה הם מחרוזות קבועות ואף אחת מהן אינה שייכת למערכת נקייה: משימה מתוזמנת ב \Microsoft\Windows\Multimedia\SystemSoundsService, ערך מפתח הפעלה בשם שירות צלילי מערכת, וקובץ הרצה בעל שם זה תחת %APPDATA%\Microsoft\Windows\ערכות נושא\שירות האודיו המקורי של Windows אינו נמצא באף אחד מהמקומות הללו. שנית, הטוען משתמש ב-mutex קבוע של מופע יחיד, Global\{7A3B9E1F-C2D4-4F8A-B5E6-D7A1F0C3E2B9}נוכחותו על גבי מארח היא אינדיקטור, ומכיוון שהמטעין מתייחס ל-mutex קיים כ"כבר פועל", יצירה מוקדמת שלו היא חיסון קצר טווח בר-קיימא בזמן מתוכנן תיקון.
זיהוי בשלב השלישי הוא בעיה קשה יותר וכדאי להבהיר לגביה. על פי הניתוח הציבורי, הקבצים מפוענחים וממופים בזיכרון במקום להיכתב לדיסק, כך שסריקת קבצים וזיהוי פלילי של הדיסק לא יראו אותם. מה שנותר ניתן לצפייה הוא טביעת הרגל של הטוען עצמו - ערכי המתמדת, ה-mutex ובקשות יוצאות למארחי המסירה או לדומייני DNS-TXT.
עבור סביבות שפותרות תלויות הן מרישום פרטי והן מרישום ציבורי, ההפחתה אינה משתנה על ידי שום דבר כאן: שמות פנימיים של טווחים כך ששם ציבורי לא יוכל לספק אותם, והצמדת רזולוציה לרישום הפנימי עבור טווחים אלה. התנהגות ניפוח הגרסאות שתוארה לעיל פועלת רק כאשר גרסה ציבורית גבוהה יותר יכולה לזכות בשם.
לבסוף, כדאי לקרוא בעיון את אי-הפרסום ולא כסיום. תשעה עשר שמות עזבו את הרישום תוך עשרים וחמש שניות ואחד חזר יום לאחר מכן תחת חשבון חדש, כאשר תשתית המסירה לא נפגעה לכל אורך הדרך. הסרת חבילות אינה הסרת הפעולה; המארחים המשיכו לשרת כל שלב בזמן כתיבת קוד זה. כל עץ תלות שפתר אחד מהשמות הללו בין ה-31 ביולי ל-1 באוגוסט צריך להיחשב כאילו הייתה לו הזדמנות לבצע את השלב הראשון, בין אם השם עדיין קיים ובין אם לאו.
הפניות
- ניתוח רב-מנועי של טוען Windows — מקור לתיקון AMSI/ETW, ביצוע רפלקטיבי בזיכרון ופרטי mutex המיוחסים לעיל.
- תיעוד סקריפטים של npm — אירועי מחזור החיים שבהם קמפיין זה אינו משתמש.
- תיעוד התקנת npm — כיסויים –להתעלם מתסריטים והיקפו.







