@joyfill ב-npm: נוזקה שרצה ברגע שמייבאים אותה, ושולפת את השרת שלה מהבלוקצ'יין
ב-28 ביולי, בהפרש של תשע דקות זו מזו, עלו ל-npm שתי גרסאות בטא של חבילות בשם @joyfill. מי שהתקין אותן לא ראה דבר חריג בהתקנה עצמה, וגם ההתקנה עם הדגל --ignore-scripts, ההגנה הרגילה מפני נוזקות שמופעלות דרך סקריפט התקנה, לא עצרה כלום. חוקרי Socket חשפו למה: הקוד הזדוני לא מחכה להתקנה. הוא מתעורר ברגע שקובץ ה-JavaScript של הפרויקט קורא require על החבילה, כלומר ברגע שמריצים אותה בפעם הראשונה.
וזה החצי הפחות מעניין. השלב הבא של הנוזקה, קובץ ה-RAT האמיתי במשקל 77 קילובייט, לא מגיע ארוז בתוך החבילה. הנוזקה שולפת אותו תוך כדי ריצה, ואת הכתובת שממנה לשלוף היא קוראת מעסקאות שמורות על שלושה בלוקצ'יינים ציבוריים. בהמשך נראה איך שרשרת ההפעלה הזאת עובדת, מה הנוזקה אוספת מהמחשב הנגוע, ולמה דווקא מפתחים הם היעד.
מה זו @joyfill ואיך שתי חבילות נהיות רעילות
Joyfill היא ערכת כלים לבניית טפסים ומסמכי PDF בתוך אפליקציות, והחבילות @joyfill/layouts ו-@joyfill/components הן חלק ממנה. יחד הן נמשכות כ-16 אלף פעם בשבוע, לפי Socket. הגרסאות שנפגעו הן גרסאות בטא בלבד: @joyfill/layouts@0.1.2-2773.beta.0 ו-@joyfill/components@4.0.0-rc24-2773-beta.4, שתיהן פורסמו מאותה זהות ב-npm. הגרסאות היציבות שלפניהן נקיות.
הבחירה בערוץ הבטא היא לא מקרית. מפתח שרוצה לנעול פרויקט על גרסה מדויקת ובטוחה יישאר על היציבה, אבל מי שבודק פיצ'ר חדש, או שסומך על טווח גרסאות רחב בקובץ התלויות, עלול למשוך דווקא את גרסת הבטא. פחות עיניים בודקות אותה, והיא נכנסת בשקט לסביבת הפיתוח. הקוד הזדוני עצמו הודבק בסוף החבילה, אחרי הקוד הלגיטימי, ועטוף בכמה שכבות של עירפול: ערבול תווים, טבלת מחרוזות מקודדת, ובנייה דינמית של פונקציות מתוך טקסט. המטרה של כל זה פשוטה. שמי שיפתח את הקובץ לא יבין במבט ראשון מה הוא עושה.
למה --ignore-scripts לא עוזר כאן
רוב מתקפות שרשרת האספקה ב-npm שראינו עד היום נשענו על סקריפט שרץ אוטומטית בזמן ההתקנה, למשל postinstall. התגובה המקובלת לזה היא להתקין עם הדגל --ignore-scripts, שמכבה את הסקריפטים האלה, וזה באמת עוצר את רובן. כאן זה לא מספיק. הקוד לא תלוי בשלב ההתקנה בכלל, אלא בנקודת הכניסה של החבילה: ברגע ש-Node.js טוען את המודול כדי להריץ אותו, הקוד רץ. Socket מציינים במפורש שהתקנה עם ignore-scripts לא מונעת את ההרצה ברגע שמייבאים את המודול. כלומר כל עוד החבילה נמצאת בעץ התלויות ומישהו קורא לה בקוד, הדגל הזה חסר משמעות.
ההבחנה הזאת חשובה כי היא מסיטה את קו ההגנה. אפשר להתקין חבילה בזהירות, לסרוק אותה, ואפילו לחסום סקריפטים, אבל השורה הראשונה שמייבאת אותה בקוד היא כבר רגע ההדבקה. בסביבת פיתוח שבה עשרות חבילות נטענות בכל הרצה, אין שלב ביניים שבו אפשר לתפוס את זה בעין.
השרת שמסתתר בעסקה על הבלוקצ'יין
כאן נמצא החלק שהופך את הקמפיין לעמיד. במקום להצפין בתוך הקוד כתובת של שרת שליטה ובקרה (C2, השרת שדרכו התוקף שולח פקודות ומקבל מידע), הנוזקה שומרת רק כתובת ארנק על רשת Tron. בזמן ריצה היא פונה לארנק הזה, קוראת את העסקה האחרונה שיצאה ממנו, ומחלצת ממנה מזהה של עסקה אחרת ברשת BNB Smart Chain. את המידע שמאוחסן בעסקה ההיא היא מפענחת, ומקבלת את הכתובת של השלב הבא. אם המסלול הזה נכשל, יש גיבוי: חשבון על רשת Aptos שמחזיק את אותו מידע.
היתרון לתוקף ברור. כתובת ארנק על בלוקצ'יין ציבורי אי אפשר להוריד בבקשה לספק אירוח, כמו שמורידים שרת שנתפס. אם חוקרים חוסמים כתובת IP אחת, התוקף פשוט רושם עסקה חדשה עם כתובת חדשה, והנוזקות שכבר רצות בשטח מכוונות לקרוא את הערך המעודכן בפעם הבאה. שווה להדגיש נקודה אחת שנוטים לפספס: הבלוקצ'יין כאן הוא לוח מודעות, לא מחשב. הנוזקה לא מריצה עליו שום דבר, היא רק קוראת ממנו את הכתובת הבאה ופונה אליה בעצמה. Socket תיעדו שלוש שרשראות כאלה בשימוש באותו קמפיין: אחת לתהליך הראשי, אחת לתהליך נפרד שמתנתק מהאב וממשיך לרוץ לבד, ואחת לשלב מתקדם יותר של פענוח כתובת השרת. בסופו של דבר התנועה מגיעה לשרתי C2 קונקרטיים, למשל בכתובת 166.88.134.62, דרך ערוץ Socket.IO.
השימוש בבלוקצ'יין ציבורי כשכבת עמידות של תשתית תקיפה הוא לא ייחודי לקמפיין הזה, והוא הולך ותופס. הרעיון פשוט מדי מכדי להתעלם ממנו: רשת מבוזרת שאי אפשר לכבות, שכל אחד יכול לכתוב אליה בכמה סנטים, ושהנוזקה רק צריכה לקרוא ממנה. ככל שספקי האירוח וצוותי החסימה נעשים מהירים יותר, כך התמריץ להעביר את נקודת ההכוונה למקום שאי אפשר להחרים גדל.
מה ה-RAT עושה כשהוא כבר בפנים
קובץ ה-RAT הסופי, אותם 77 קילובייט של JavaScript, נותן לתוקף שליטה מלאה על המחשב: הרצת פקודות מערכת ו-JavaScript, העלאה והורדה של קבצים, קריאת תוכן הלוח (clipboard), ואיסוף פרטים על המערכת ועל הכתובת הציבורית שלה. הוא גם מוודא שהוא לא רץ בסביבת ניתוח. אם הוא מזהה שהוא על מכונת בנייה אוטומטית כמו github-runner, על WSL2, או בתוך קונטיינר, הוא נמנע מלפעול, כדי לא להיחשף לחוקרים שמריצים אותו בכוונה בארגז חול.
מעניין יותר איפה הנוזקה בוחרת להתמקם. במקום להסתתר בקובץ אקראי, היא מזריקה את עצמה לתוך כלים שמפתח פותח כל יום: הרכיב @vscode/deviceid, הליבה של Discord Desktop, קובץ ה-main.js של GitHub Desktop, וה-CLI של npm עצמו. ככה, גם אם מסירים את החבילה הנגועה מהפרויקט, שרידי הנוזקה ממשיכים לרוץ מתוך תוכנות שנראות אמינות לגמרי.
לצד ה-RAT מגיע גם גנב מידע כתוב ב-Python. הוא מכוון בדיוק למה שיש למפתח על המחשב: פרטי אימות ממנהל הסיסמאות של Windows ומ-Secret Service בלינוקס, מידע מהדפדפן כולל תוספים של ארנקי קריפטו ומנהלי סיסמאות, פרטי Git, וקובצי ההגדרות של ה-CLI של GitHub ושל VS Code. המחשב של מפתח הוא לא עוד מחשב. הוא צרור מפתחות: token-ים לשרתי קוד, מפתחות גישה לענן, ופרטי אימות לפרויקטים של מעסיקים ושל לקוחות.
של מי הקמפיין
Socket מקשרת את החבילות למשפחת הנוזקות DEV#POPPER לפי מאפיינים חוזרים: מבנה המפענח שמתבסס על כמה בלוקצ'יינים, מפתחות ההצפנה, כותרת מיוחדת בשם Sec-V בבקשות ל-C2, ואותו אוסף פקודות מעל Socket.IO. לפי הניתוח, הקמפיין חופף במידה רבה לאירוע שחברת eSentire ניתחה מוקדם יותר ב-2026, שבו אותה משפחה הפעילה RAT וגנב מידע בשם OmniStealer. The Hacker News מוסיף שהחוקרים מייחסים את הפעילות לגורם המזוהה עם צפון קוריאה, בהמשך לשורה ארוכה של קמפיינים שמכוונים למפתחים דרך הצעות עבודה מזויפות וחבילות קוד נגועות. חשוב לומר את זה בזהירות: הייחוס נשען על חפיפת תשתית ושיטות עבודה, לא על ראיה ישירה, וגם Socket מנסחת אותו כהערכה ולא כקביעה.
מה זה אומר בפועל
אם אתם מפתחים, שווה לבדוק אם אחת משתי גרסאות הבטא האלה נמשכה לפרויקט, ולנעול תלויות על גרסאות יציבות מדויקות במקום על טווחים שיכולים למשוך בטא או release candidate. הכלל ש"התקנה עם --ignore-scripts מספיקה" כבר לא תקף מול נוזקה שרצה בזמן import, אז ההפרדה שבאמת מגנה היא בין סביבת הפיתוח לבין המקום שמחזיק את פרטי האימות הרגישים. ומעל הכול, כדאי להפנים שהמחשב של מפתח הוא יעד בעל ערך גבוה בדיוק בגלל המפתחות שמונחים עליו, ולהתייחס אליו ככה גם כשהוא רק "מחשב עבודה".
תגובות