בקובץ ההסבר שצורף לתרגיל התכנות היו שני כללים. שלוש שעות לסיים, ואסור להיעזר בעוזרי AI. היה שם גם משפט מרגיע אחד, שלפיו הקובץ server.js תקין ואין צורך לגעת בו. זה היה הקובץ היחיד בכל הפרויקט שהתוקפים שינו.

את הארכיון הזה ניתח צוות המחקר GReAT של קספרסקי, בדוח שפרסם החוקר עומר אמין הבוקר. בשורה הראשונה של server.js הופיע ייבוא של חבילת npm בשם colorized_terminal בגרסה 2.1.0, שהתוקפים ארזו ישירות לתוך תיקיית node_modules של הפרויקט במקום לפרסם אותה למאגר הציבורי, כך שאין לה היסטוריה ציבורית ואין למי להתלונן עליה. ברגע שהמועמד הריץ את הפרויקט, החבילה הפעילה ברקע תוכנית נסתרת מהנתיב node_modules/.cache/.320697f1/index.js. בקספרסקי מציינים שעוזר קוד אוטומטי שהיה סורק את הפרויקט היה כנראה מסמן את הייבוא החשוד הזה מיד, מה שנותן לאיסור על AI פשר קצת אחר. בהמשך נראה מה התוכנית הזאת עושה, איך היא נשארת על המחשב גם אחרי אתחול, ולמה מפתחים הם יעד נוח במיוחד לקבוצה הזאת.

קבוצה אחת, ארבעה שמות

קספרסקי מייחסת את הפעילות לקבוצה שהיא מכנה Mirage Kitten, וברשומה קודמת שלה מסוף יולי היא מפרטת שזו אותה קבוצה שחברות אחרות מכנות UNC1549, Smoke Sandstorm ו-Nimbus Manticore. השם האחרון אמור להיות מוכר לקוראים כאן: בשבוע שעבר סיקרנו את הכלים החדשים שקבוצה בשם הזה קיבלה לפי Group-IB. מי שקישרה את הקבוצה לאיראן היא בעיקר מנדיאנט, שכתבה שהיא מעריכה ברמת ביטחון בינונית שהפעילות קשורה ל-UNC1549, קבוצת ריגול איראנית, ושיש קשר אפשרי בינה לבין משמרות המהפכה. היעדים שמנדיאנט מונה הם תעופה, חלל וביטחון, בעיקר בישראל ובאיחוד האמירויות.

הקורבנות בגל הנוכחי נמצאים במקום אחר. לפי הטלמטריה של קספרסקי מדובר בגופי פינטק, תעופה וחלל במזרח התיכון ובאפריקה, וספציפית במצרים, באתיופיה ובאפגניסטן. יש עוד נתון בדוח שקל להגזים בו: ארכיוני ZIP עם הפרויקטים הנגועים הועלו לשירות סריקה מקוון מכמה מדינות, ביניהן הודו, טורקיה, ישראל, עיראק, גרמניה ואירלנד. קספרסקי לא כותבת שאלה קורבנות. העלאה לשירות סריקה אומרת רק שמישהו שם החזיק את הקובץ ביד וחשד בו מספיק כדי לבדוק אותו.

מה היה בתוך ארכיון ה-ZIP

הקובץ הראשון שאותר, Front-Technical-Challenge.zip, אוחסן בדלי Amazon S3, כלומר על תשתית ענן לגיטימית לגמרי שאף מסנן לא יחסום. בתוכו ישבה TaskFlow, אפליקציית הערכה למועמדים שבנויה על Express, React ו-Vite, וקובץ README שביקש מהמועמד לעבור על הצד הקדמי ולתקן בו תקלות. אותה הפניה מנומסת אל הצד הקדמי היא שהרחיקה את תשומת הלב מהקובץ היחיד שכן נגעו בו.

מעקב לאחור בטלמטריה של קספרסקי העלה שלוש גרסאות של אותה נוזקה, כל אחת ממחשב במדינה אחרת, שהופצו דרך תרגילי תכנות בעלי אופי דומה ודרך שתי חבילות נגועות: colorized_terminal ו-pretty-log, שתיהן מקובעות לגרסה 2.1.0. הקבוצה גם שינתה הרגלים. עד היום היא אירחה את ארכיוני הפיתיון על שירותי צד שלישי לגיטימיים, ובעבר עשתה זאת דרך onlyoffice.com; הפעם היא עברה ל-S3.

NodeRabbit, ולמה דווקא Node.js

התוכנית שהופעלה מתוך node_modules יושבת על המחשב, פונה כל כמה זמן לשרת מרוחק, שואלת אם יש עבורה פקודה, מריצה אותה ומחזירה את התוצאה. לסוג הזה של תוכנה קוראים RAT, וקספרסקי קראה לה NodeRabbit. מה שמייחד אותה הוא שהיא כתובה כולה ב-Node.js, כך שאותו קוד בדיוק רץ על Windows, על Linux ועל macOS. לפי קספרסקי אלה המימושים הראשונים המתועדים של הקבוצה בשפות סקריפט, אחרי שנים של נוזקות שנכתבו ב-C, ב-C++ וב-Go והוזרקו דרך DLL search-order hijacking.

הנוזקה מייצרת לעצמה מזהה ייחודי מתוך שם המחשב, שם המשתמש, גרסת מערכת ההפעלה, הארכיטקטורה וכתובת ה-MAC, מגבבת את הכל ב-SHA-256 ולוקחת את 32 התווים ההקסדצימליים הראשונים. אחר כך היא פותחת מאזין TCP מקומי על 127.0.0.1:48739, לא כדי לקבל תעבורה אלא כדי לוודא שהיא היחידה: אם הפורט תפוס, היא מניחה שכבר רצה עליו עותק שלה ומסיימת בשקט. הגרסה השנייה כבר לא משתמשת בפורט קבוע אלא גוזרת אותו מארבעת התווים הראשונים של המזהה, לפי 41984 ועוד השארית מחלוקה ב-5000, כך שכל מחשב נגוע מקבל פורט משלו בטווח 41984 עד 46983. פורט קבוע אצל כולם הוא חתימה שקל לחפש ברשת שלמה.

שימור האחיזה בנוי כולו על התחזות לרכיבים משעממים. ב-Windows הנוזקה מעתיקה את עצמה אל תיקייה בשם EdgeUpdate תחת AppData, משכפלת את node.exe המקומי לקובץ בשם nodew.exe ומשנה בו את ה-PE subsystem מ-Console ל-GUI כדי שלא יקפוץ חלון מסוף, ואז רושמת מפתח הרצה בשם MicrosoftEdgeUpdate. ב-Linux היא נשענת על רשומת cron שרצה בכל אתחול, וב-macOS על LaunchAgent בשם com.microsoft.edgeupdate עם RunAtLoad ו-KeepAlive. הגרסה השנייה מחליפה תחפושת ומתחזה ל-Intel Driver & Support Assistant, עם משימה מתוזמנת בשם IntelDriverSupportUpdate שרצה כל יום בעשר בבוקר.

גם ערוץ השליטה נבנה כדי להיבלע ברעש. שרתי השליטה והבקרה (C2, השרתים שדרכם התוקף שולח פקודות לנוזקה) יושבים ברובם על תת-דומיינים של azurewebsites.net, ולפי קספרסקי היו מקרים שבהם התוקפים שתלו את שם הארגון המותקף עצמו בתוך שם התת-דומיין, כדי שהתעבורה תיראה כמו עוד שירות פנימי שעובד מדבר איתו באמצע יום עבודה. כל בקשה נארזת ב-AES-256-GCM עם IV חדש בן 12 בתים ותג אימות בן 16 בתים. הגרסה השנייה גם בודקת קודם אם היא בכלל על מחשב אמיתי: מעט זיכרון, מעט ליבות, מערכת שרק עלתה, שמות משתמש שנראים כמו של חוקר או כלי ניתוח שרצים ברקע. אם משהו מזה מתקיים היא שולחת שלוש בקשות תמימות ל-google.com, ל-microsoft.com ול-cloudflare.com ומסיימת, בלי לפנות אף פעם לשרת האמיתי שלה. הגרסה הראשונה תמכה ב-11 פקודות. השלישית מגיעה ל-23.

אז למה התוקפים רודפים דווקא אחרי מפתחים?

כאן נכנס לתמונה החלק המעניין בגרסה השלישית. מעבר לכל מנגנוני שימור האחיזה שכבר תוארו, היא מוסיפה שניים שרוכבים ישירות על שגרת העבודה של מתכנת. הראשון הוא תוסף מזויף ל-VS Code שמוצג למשתמש בשם GitHub Copilot Helper, עם התיאור "AI coding assistant helper service" ואירוע הפעלה ברגע שהעורך מסיים לעלות. קובץ ה-extension.js שלו פשוט מפעיל את הנוזקה כתהליך Node נפרד. כדי להיראות סביר יותר הוא שואל שם של מפרסם מהימן מתוך המטא-דאטה של תוספים אחרים שכבר מותקנים או מתוך הערך trustedPublishers שיושב ב-state.vscdb, אבל שום חתימה ושום סטטוס אמיתי לא מועתקים איתו. באותה הזדמנות הוא גם מנסה לכבות את Workspace Trust.

השני מתוחכם יותר. פקודה אחת סורקת את הנתיבים האחרונים שנפתחו ב-VS Code ואת מיקומים מקובלים כמו ~/projects ו-~/source, בודקת שם רק את 60 התיקיות הישירות הראשונות ומחזירה עד 20 מאגרי git. פקודה שנייה בוחרת מאגר ומוסיפה שורת הפעלה מסומנת אל תוך .git/hooks/post-merge ו-.git/hooks/post-checkout, כלומר אל תוך הסקריפטים שגיט מריץ אוטומטית אחרי משיכה של קוד או מעבר בין ענפים. הסימון שהתוקפים משאירים לעצמם בקובץ הוא ההערה # shepherd-persist. הנוזקה לא תעלה מיד; היא תחכה עד שהמפתח יעשה pull או יחליף ענף, ואז תעלה כאילו כלום. (ומי שמנקה מחשב נגוע בודק משימות מתוזמנות ומפתחות רישום, לא את תיקיית .git של פרויקט שנגעו בו לאחרונה לפני חצי שנה.) באותה גרסה נוספה גם פקודה שאוספת כתובות מייל מקבצי OST ו-PST של Outlook.

PollCat, והקוד החד-פעמי שהמגייסת שולחת

הנוזקה השנייה שקספרסקי מתארת הגיעה בתרגיל אחר, פרויקט React בשם RankChallenge-react, ומדריך PDF שצורף אליו הסביר למועמד שעליו ללחוץ Continue, להקליד קוד בן שש ספרות ולסיים בתוך שעה. הקודים, נכתב שם, מגיעים מהמגייסת עצמה, הם חד-פעמיים ופגי תוקף מהר, ועמוד ההתחברות אף הבטיח שהם מתחלפים כל 30 שניות. הקריאה של קספרסקי היא שזה נותן למפעיל שליטה על מי בכלל מגיע לפיתיון, ובמקביל יוצר לחץ זמן שדוחף את היעד להריץ את הפרויקט מהר.

רק שהמנגנון הזה הוא תפאורה. הקובץ app.js טוען את requireAuth.js, וזה מייבא ומפעיל מיד את הרכיב הזדוני requireObjects.js. כלומר הנוזקה, שקספרסקי קראה לה PollCat, נרשמת אצל שרת השליטה ומתחילה לבקש פקודות בזמן שהאפליקציה עדיין עולה, לפני שהמשתמש הקליד ספרה אחת. קוד שגוי חוסם את הגישה לתרגיל, ולא עוצר את הנוזקה.

משהו בפרויקט הזה גם נראה מיוצר בחיפזון. חבילת השורש נקראת ctf-server, השרת מדפיס "CTF server running", והמדריך מפנה לנתיב שכולל ctf, למרות שהתרגיל עצמו אינו תחרות אבטחה. קספרסקי מציעה הסבר אפשרי אחד ומסמנת אותו ככזה: ייתכן שהתוקף ביקש מעוזר קוד מבוסס AI לייצר פלטפורמת CTF ב-React, ורק אחר כך הכניס לתוכה את הרכיבים הזדוניים.

מה שקשר את PollCat לקבוצה הוא דווקא הפרטים הקטנים בערוץ השליטה. היא מצפה לתשובת HTTP 400 כאישור שהרישום הצליח, מוציאה ממנה מזהה session וממשיכה איתו, בדיוק כמו הדלת האחורית הנייטיב הישנה של הקבוצה שמוכרת בשם Retrograde או MiniFast. גם ברירות המחדל של קצב הפנייה זהות: 120,000 מילישניות בין פנייה לפנייה, 5,000 מילישניות של הסחה אקראית ו-60,000 מילישניות לניסיון חוזר. ואפילו מספרי הפקודות חופפים, כולל שתי פקודות ש-PollCat מצהירה עליהן אך לא מימשה, בעוד שב-MiniFast שתיהן עובדות. על בסיס אלה קספרסקי מייחסת את הפעילות ל-Mirage Kitten ברמת ביטחון גבוהה.

מה זה אומר בפועל

אם אתם מפתחים בארגון תעופה, ביטחון או פינטק, ארכיון שהגיע ממגייסת בלינקדאין הוא קוד לא מהימן לכל דבר, ומקומו הוא מכונה וירטואלית או קונטיינר חד-פעמי, לא תחנת העבודה שמחוברת לרשת הארגונית. שווה גם להציץ בקוד לפני ההרצה: חבילה שיושבת בתוך node_modules ואין לה זכר במאגר הציבורי היא כל מה שצריך לראות. ולמי שכבר הריץ פרויקט כזה בחודשים האחרונים, קספרסקי פרסמה בסוף הדוח את רשימת הכתובות והחתימות המלאה, ובנוסף למשימות המתוזמנות ולמפתחות ההרצה כדאי לבדוק את תיקיית התוספים של VS Code ואת ה-hooks במאגרי הגיט המקומיים.