ביטול ה-token הוא מה שמפעיל את המחיקה: כך חזרה התולעת Shai-Hulud ל-npm דרך ה-SDK של Tensorlake
ב-8 באוקטובר, ב-01:12 לפי שעון UTC, workflow הפצה אוטומטי של הפרויקט tensorlake פרסם ל-npm גרסה חדשה של ה-SDK שלו, 0.5.144. הגרסה יצאה מהמקום הנכון, נבנתה על ידי ה-CI של ה-repository עצמו, וקיבלה תעודת מקור חתומה שמאשרת בדיוק את זה. אחת עשרה דקות אחר כך סימנו אותה חוקרי Socket כזדונית.
מה שהגיע לאותה גרסה נכתב יממה קודם. לפי StepSecurity, שמייחסת את הניתוח לאשיש קורמי, הקומיט הזדוני הראשון נחת על ענף main ב-7 באוקטובר ב-01:20, תחת שמו של אחד ממתחזקי הפרויקט, ואחריו באו עוד שבעה שעידנו את המטען והוסיפו ל-package.json את השורה שמריצה אותו. אף אחד מהם לא עבר דרך pull request. ה-SDK הזה אינו חבילה שולית: Socket סופרת כ-12 אלף הורדות שבועיות ויותר מאלף כוכבים ב-GitHub, ומדגישה שהמספר הזה מתאר שימוש כללי בחבילה ולא מספר נדבקים. בהמשך נראה איך החבילה מריצה קוד לפני שמישהו ייבא ממנה שורה, מה היא אוספת, איך היא מוצאת את השרת שלה בלי שכתובתו מופיעה בקוד, ולמה דווקא הפעולה הראשונה שכל אחד היה עושה, לבטל את ה-token שנגנב, היא זו שמוחקת את התיקיות.
Socket מסווגת את התולעת תחת השם ChainDrop ומציינת שמנגנון איתור השרת שלה זהה לזה שהופיע בפריצה לחבילת keyv. השם Shai-Hulud, שהתוקפים עצמם ממשיכים לחתום בו, מסתובב ב-npm כבר חודשים: בספטמבר דיווחנו כאן ששני repositories זדוניים מגל קודם חזרו לאוויר בלי שאף אחד תקף שוב, ובאוגוסט נעצרו באוסטרליה שני חשודים בהפעלת אחד הגלים. הקוד ממשיך לרוץ בלי קשר לשאלה מי מפעיל אותו, והוא לומד. הגל הזה מביא שני דברים שלא היו בקודמים: ציד ממוקד של קובצי ההגדרות של כלי הפיתוח מבוססי ה-AI, ומנגנון שמעניש את מי שמנסה לנקות.
ההתקנה שמריצה קוד לפני שייבאתם שורה אחת
יש שלב בהתקנה של חבילת npm שבו מנהל החבילות מריץ סקריפטים שהחבילה הגדירה לעצמה, עוד לפני שמישהו כתב לה import אחד. השלב הזה נקרא preinstall, והוא קיים כדי שחבילות יוכלו לקמפל רכיבים או להתאים את עצמן לסביבה. בגרסה 0.5.144 הוא הצביע על קובץ אחד, lib/setup.mjs, וזה כל מה שהתוקף היה צריך. מי שהריץ npm install בפרויקט שתלוי ב-SDK הריץ אצלו את הקוד, בהרשאות של התהליך שביצע את ההתקנה, בלי להפעיל שום דבר מה-SDK עצמו.
ה-loader הזה מעורפל, והוא לא מסתפק ב-Node. לפי StepSecurity הוא מוריד את סביבת ההרצה Bun ומריץ דרכה קובץ מעורפל בגודל 856 קילובייט בשם Math_Symbol.js, שהוא גם נוזקת גניבת המידע וגם התולעת עצמה. פרט אחד שם מעניין: הסקריפט מדלג על עצמו כשהוא מזהה שהוא רץ על CI. StepSecurity בכל זאת ממליצה שמי שהחבילה נכנסה אצלו רק לתוך job של CI יחליף בכל מקרה את הסודות שאותו job יכול היה להגיע אליהם.
וכאן החלק שאמור להפריע לכם. החבילה הגיעה ל-npm עם attestation של Sigstore, חתימה שמעידה שהקובץ נבנה בדיוק מה-repository ומה-workflow שהוא מתיימר לבוא מהם. החתימה הייתה תקינה לחלוטין, מפני שהיא אמרה את האמת: התוקף דחף את הקוד לענף main של הפרויקט, וה-workflow הרשמי בנה ופרסם אותו משם. "התעודה אומרת איפה החבילה נבנתה. היא לא אומרת שהקוד בטוח", כתב קורמי.
מה היא אוספת, ומאיפה
הרשימה של Socket מסבירה למה חבילה אחת יכולה להפיל סביבה שלמה. הנוזקה קוראת קובצי .npmrc, פונה ל-API של ה-token-ים של npm ומנסה גם את מסלול חילופי ה-token של OIDC. את ה-token-ים של GitHub שהיא מוצאת היא בודקת מול השירות כדי לראות אילו הרשאות יש להם על repositories ועל workflows. בסביבות ענן היא פונה לשירות המטא-דאטה של AWS, ל-ECS, ל-Secrets Manager ול-Parameter Store. היא מחפשת מופע Vault מקומי על 127.0.0.1:8200, כולל מסלולי האימות שלו מול Kubernetes ומול AWS, ואוספת token-ים של service accounts וקובצי kubeconfig. על הדיסק היא לוקחת מפתחות SSH, קובצי .env, ארנקי מטבעות קריפטו ונתונים של אפליקציות מסרים.
הקטגוריה האחרונה ברשימה היא החדשה. הנוזקה מחפשת במפורש את קובצי ההגדרות וקובצי ה-MCP של כלי הפיתוח מבוססי ה-AI: .claude, .cursor, .kiro, וגם Windsurf ו-Zed. אלה הקבצים שמחזיקים היום את המפתחות ל-service-ים שהמפתח חיבר לעוזר הקוד שלו, והם יושבים בתוך ה-repository או בספריית הבית כמו כל קובץ הגדרות אחר. חילוץ פרטי אימות עבר לשם כי שם הם נמצאים.
אז איפה נמצא השרת?
בקוד אין כתובת. זאת לא השמטה אלא בחירת תכנון: במקום דומיין שאפשר להשתיק, הנוזקה שולפת את כתובת שרת השליטה והבקרה (C2, השרת שאליו המידע הגנוב נשלח ושממנו מגיעות הפקודות) מתוך חוזה חכם על בלוקצ'יין האת'ריום. לצורך זה היא מתשאלת כשלושים נקודות RPC ציבוריות, כך שגם אם חלקן חוסמות אותה, אחת תענה. הבלוקצ'יין כאן הוא אחסון פסיבי: מקום שהתוקף יכול לעדכן בו את הכתובת ולהחליף שרת בלי לגעת בקוד שכבר מותקן אצל הקורבנות, וקשה מאוד להוריד אותו משם. Socket מציינת שאותו מנגנון איתור הופיע גם בפריצה ל-keyv.
מי שחוסם את הדרך הזאת לא מנתק את הנוזקה, כי יש לה נפילה לאחור: GitHub. את המידע שאספה היא מצפינה ושולחת, או לדומיין שהגיע מהחוזה, או ל-repository ציבורי חדש שהיא יוצרת בחשבון הקורבן עצמו. את ה-repository הזה אפשר לזהות לפי התיאור שהיא כותבת לו, "Shai-Hulud: Here We Go Again". לגבי הדומיין עצמו שני הדוחות לא אומרים אותו דבר: StepSecurity נוקבת ב-iseekaigogo.com, ואילו Socket מתארת רק את מנגנון האיתור ולא נוקבת בשום דומיין.
איך היא ממשיכה לחבילה הבאה
עד כאן זאת גניבה. מה שהופך אותה לתולעת הוא מה שהיא עושה עם ה-token של npm שהרגע חילצה. הנוזקה מונה את החבילות הרשומות על זהות הפרסום של הקורבן, בונה להן provenance של Sigstore, ומפרסמת מהן גרסאות נגועות. כל מפתח שנדבק הוא נקודת הפצה חדשה, והחבילות שיצאו ממנו ייראו תקינות בדיוק באותה מידה שבה נראתה 0.5.144, ומאותה סיבה.
היא גם משתלת מאחז לטווח ארוך. Socket מצאה בקוד מחרוזות שמצביעות על שתילת workflows ב-GitHub Actions שמחופשים ל-Copilot או ל-Dependabot, בדיוק השמות שאף אחד לא בודק פעמיים ברשימת ה-workflows. StepSecurity מוסיפה שבבדיקת התפשטות כדאי לחפש גם קומיטים שנחתמו בכתובת [email protected], וקבצי .claude או .vscode שצצו ב-repository בלי סיבה. התולעת נשארת בסביבה דרך הקבצים שאיש לא קורא.
ה-token כבן ערובה, והמלכודת שבניקוי
החלק הזה הוא מה שמייחד את הגל הנוכחי. כשהנוזקה מחזיקה token של GitHub, היא לא מסתפקת בשימוש בו. היא מתקינה על המכונה שירות רקע שכל תפקידו לבדוק אם ה-token עוד חי: לפי StepSecurity הוא פונה ל-api.github.com/user כל שישים שניות, לאורך עד 24 שעות. ברגע ש-GitHub דוחה את ה-token, כלומר ברגע שמישהו ביטל אותו, השירות מוחק את ספריית הבית של המשתמש בלינוקס וב-macOS, או את פרופיל המשתמש ב-Windows. התוקפים לא טרחו להסתיר את הכוונה: בקוד יושבת מחרוזת שאומרת במילים פשוטות שביטול ה-token ימחק את המחשב של הבעלים. אותה מחרוזת הופיעה, לפי Socket, גם בגלים קודמים של Shai-Hulud.
את השירות הזה אפשר למצוא. בלינוקס הוא יחידת systemd בשם gh-token-monitor עם תיקייה ב-~/.config/gh-token-monitor, ב-macOS הוא LaunchAgent בשם com.user.gh-token-monitor.plist, וב-Windows זה task במתזמן שמופעל בכל כניסת משתמש ומריץ קובץ monitor.ps1. הסדר שבו מטפלים בו הוא העיקר, ושני הדוחות מדגישים אותו: קודם מוציאים את המנטר, רק אחר כך מבטלים token-ים. "אל תבטלו אף token של GitHub עד שסיימתם את שלב 3", כתבה StepSecurity בהוראות הטיפול שלה. מי שעובד לפי האינסטינקט ומבטל ראשון מפעיל בעצמו את מה שניסה למנוע.
מה זה אומר בפועל
אם יש לכם פרויקט שתלוי ב-tensorlake, הבדיקה היא לא רק אם 0.5.144 מופיעה ב-lockfile, אלא גם אם מישהו התקין את החבילה מענף main אחרי 7 באוקטובר. StepSecurity ממליצה להצמיד ל-0.5.143, למחוק את node_modules ולנקות את ה-cache של npm. על מכונה שההתקנה רצה בה באמת, בדקו קודם כול אם שירות gh-token-monitor יושב עליה, והוציאו אותו לפני שאתם נוגעים ב-token-ים. רק אחרי זה מחליפים פרטי אימות, ומחליפים הכול: npm, GitHub, מפתחות ענן, SSH, Kubernetes ו-Vault, התוכן של קובצי .env, והמפתחות שיושבים בהגדרות של כלי ה-AI.
ולמי שהחבילה הזאת לא נוגעת לו בכלל: ההגדרה ignore-scripts בקובץ .npmrc הייתה מנטרלת כאן את כל השרשרת, מפני שבלי preinstall שום דבר לא היה רץ. היא שוברת חבילות שצריכות שלב בנייה, ולכן היא לא ברירת מחדל באף מקום. אחרי השבוע הזה שווה לבדוק מה בדיוק היא שוברת אצלכם.
תגובות