ה-DLL בתיקיית Poedit נראה כמו רכיב עדכון, ולידו קובץ בלי סיומת: מיקרוסופט מפרקת את NeedyMantis
בתיקיית ההתקנה של Poedit, תוכנת תרגום ותיקה וחופשית, יושב קובץ בשם WinSparkle.dll. זה שם של רכיב אמיתי לגמרי, ספריית העדכונים שבודקת עבור התוכנה אם יצאה גרסה חדשה. אצל חלק מהארגונים שמיקרוסופט בדקה הקובץ בשם הזה לא היה הרכיב האמיתי, ולידו ישב קובץ נוסף, בלי שום סיומת, בשם WinSparkle. שני הקבצים האלה הם ההתחלה של שרשרת שנגמרת בערוץ שליטה מוצפן מול שרת מרוחק.
צוות Microsoft Threat Intelligence פרסם ב-28 בספטמבר ניתוח של משפחת הנוזקות שהוא קורא לה NeedyMantis. לפי הדיווח היא נצפתה במספר מצומצם של תקיפות ממוקדות נגד חברות תקשורת, אוניברסיטאות, עמותות בתחום הרפואה, ארגונים בין-ממשלתיים וקבלנים של גופי ממשל, והפעילות המוקדמת ביותר שמיקרוסופט מזהה מתוארכת לאוקטובר 2025. מה שהופך אותה למעניינת הוא לא רשימת היכולות אלא העיתוי שבו היא מופיעה: NeedyMantis כמעט תמיד מותקנת אחרי שהתוקף כבר נמצא ברשת. היא לא כלי הפריצה. היא מה שמתקינים כדי שהגישה תישאר.
נעבור בהמשך על האריזה שלה, על שרשרת הטעינה שנפרסת שכבה אחרי שכבה, ועל איך בדיוק נראית השיחה שלה עם השרת. נתחיל דווקא מהשאלה איך מיקרוסופט נתקלה בה מלכתחילה.
החוט שנמשך מתוך פרשת DAEMON Tools
החוקרים לא חיפשו את NeedyMantis. הם ישבו על סימני הזיהוי שפרסמה קספרסקי בחקירה של הפריצה לשרשרת האספקה של DAEMON Tools, אותה פרשה שבה גרסאות חתומות דיגיטלית של תוכנת התקנת הכוננים הווירטואליים הופצו מהאתר הרשמי עם דלת אחורית בפנים, וכיסינו אותה כאן במאי. מהסימנים האלה הם המשיכו לכיוונים נוספים, ושם צצה משפחת הנוזקות הזאת.
את הפעילות סביב DAEMON Tools מיקרוסופט מסמנת בשם Storm-3069, וזה השם היחיד שהיא מצמידה בוודאות למי שמפעיל את NeedyMantis. כאן כדאי לקרוא את הניסוח של מיקרוסופט בדיוק כמו שהוא: היא מעריכה שהפעילות מקורה בסין, ובמפורש אינה מייחסת את Storm-3069 לגורם מדינתי סיני. היא גם מציינת שנצפתה פעילות נוספת של NeedyMantis מחוץ לקמפיין הזה, כלומר ייתכן שיותר מגורם אחד מחזיק בכלי. קספרסקי, מצידה, כתבה בזמנו שמצאה בקוד עקבות של דוברי סינית אבל שאינה מייחסת את הפריצה לשחקן מסוים. שלוש הזהירויות האלה שוות יותר מכל כותרת על ריגול סיני.
למה בכלל לטרוח על תוכנה לגיטימית
כשתוכנה בווינדוס עולה, היא טוענת ספריות עזר לפי שם. אם מישהו הצליח להניח בתיקייה שממנה התוכנה טוענת ספרייה קובץ בשם הנכון, התוכנה תטען אותו בלי לשאול שאלות, והקוד הזר ירוץ תחת שם של תהליך מוכר וחתום. לטכניקה הזאת קוראים DLL sideloading, והיא הסיבה שהחוליה הראשונה בשרשרת מתחפשת דווקא לרכיב עדכון של Poedit.
מיקרוסופט מונה רשימה של תוכנות חופשיות ולגיטימיות שהנוזקה נארזה לצידן: Poedit, curl, עורך הטקסט Vim וכלי הגישה מרחוק TightVNC. במקרים אחרים היא התחזתה לרכיבים של מיקרוסופט אופיס, ברודקום, אינטל ו-NVIDIA. גם הנתיבים עצמם מפורסמים במלואם, בין היתר WinSparkle.dll תחת תיקיית Poedit, libcurl.dll תחת ProgramData\USOShared, ו-nvml.dll תחת ProgramData\ics. הארכיון הנלווה נושא תמיד את שם ה-DLL בלי הסיומת, וזה סימן זיהוי נוח: קובץ בשם libcurl ליד קובץ בשם libcurl.dll.
איך הקבצים מגיעים לשם מלכתחילה משתנה מתקיפה לתקיפה, כי בשלב הזה התוקף כבר בפנים. באחד המקרים שמיקרוסופט תיעדה מפעיל אנושי השתמש בערכת Impacket כדי להעתיק את התוכנה הלגיטימית, את ה-DLL הזדוני ואת הארכיון מתוך שיתוף רשת פנימי, ואז להריץ אותם על המכונה שנבחרה.
הארכיון שאין לו פורמט, והקובץ שאין בו PowerShell
הקובץ חסר הסיומת הוא ארכיון בפורמט שהתוקפים המציאו לעצמם, דחוס ומוצפן. השכבה החיצונית מפוענחת ב-XOR ואז נפרסת בעזרת RtlDecompressBuffer, פונקציית פריסה סטנדרטית של ווינדוס. בתוכה יושבות רשומות קבצים, וגם בכל רשומה השם מפוענח ב-XOR והתוכן נפרס בנפרד. ההיסטים בתוך הפורמט ומפתחות ה-XOR משתנים מדגימה לדגימה, מה שאומר שכלי שפוענח דגימה אחת לא בהכרח יפתח את הבאה.
ארכיון WinSparkle שמיקרוסופט פירקה הכיל אחד עשר קבצים, ורובם מסיחי דעת: רכיבים אמיתיים של 7-Zip, רכיבים אמיתיים מחבילת Sysinternals, ועותק אמיתי של kernel32.dll. הנוזקה עצמה מסתתרת בשלושה קבצים שגנבו שמות של ספריות ווינדוס מוכרות. הקובץ שנקרא dnsapi.dll אינו ספריית ה-DNS אלא קובץ ההגדרות. הקובץ שנקרא ws2_32.dll אינו ספריית הרשת של ווינדוס אלא רכיב התקשורת. והקובץ שנקרא msvcrt140.dll מחזיק shellcode שתפקידו לטעון מודולים נוספים. לצידם יושב הקובץ encryptbase64.ps1, ואליו נגיע מיד.
למרות הסיומת, encryptbase64.ps1 אינו סקריפט. בתוכו shellcode בארכיטקטורת x64, והמשימה שלו היא לחלץ את הרכיב המרכזי של הנוזקה מתוך נתונים מוטמעים, לפענח אותם ב-XOR ולפרוס אותם. מה שיוצא בסוף הוא DLL, אבל לא בפורמט הרגיל: מדובר בגרסה מוקטנת של מבנה קובץ PE שהתוקפים תפרו לעצמם, כדי שכלי ניתוח סטנדרטי לא יזהה אותה כקובץ הרצה.
גם כאן הכול עטוף בשכבות שנועדו להאט חוקר. המחרוזות בקוד נבנות תו אחר תו על המחסנית ומפוענחות רק ברגע השימוש, טכניקה שמוכרת בשם obfuscated stack strings, וכך שמות הספריות והפונקציות של ווינדוס לא מופיעים בקובץ בטקסט גלוי. גם ערכים מספריים קבועים מאוחסנים בצורה מעוותת ומשוחזרים בחישוב. השלב השני לא שומר בכלל את שמות הפונקציות אלא רק ערכי גיבוב שלהן, ומשחזר את הפונקציה הנכונה בעזרת אלגוריתם המבוסס על סיבוב ביטים ימינה, עם ערך סיבוב שהיה 11 בדגימה שנבדקה. בשלב הראשון נוספו שתי בדיקות שמטרתן לזהות שמריצים את הקוד תחת דיבאגר, אחת דרך ProcessDebugFlags והשנייה בשיטת ThreadHideFromDebugger.
עד כאן איך הנוזקה נפרסת על הדיסק. הצד השני, איך היא מדברת החוצה, מפורט אפילו יותר.
איך נראית השיחה עם השרת
קובץ ההגדרות המחופש ל-dnsapi.dll הוא מבנה בינארי באורך 3,448 בתים, ומיקרוסופט פרסמה את מפת השדות שלו. משם מגיעים היעד והדרך: פורט 443, השרת corp.tripswithengine[.]com, והנתיב /library/zip/.
הפנייה הראשונה החוצה היא בקשת GET רגילה למראה מעל HTTPS, ופרטי המכונה נשלחים בתוך כותרת Set-Cookie. הערך שם הוא מבנה JSON דחוס ומקודד ב-Base64, ובתוכו שם המחשב, שם המשתמש, שם התהליך, תהליך האב, רשימת התהליכים הרצים ורשימת התיקיות תחת Program Files. אחרי הפנייה הזאת החיבור עובר ל-WebSockets ומשם ממשיך בפרוטוקול בינארי משלו. רכיב התקשורת בגרסה שנבדקה מממש את זה מעל ממשקי WinINet של ווינדוס, ומיקרוסופט מצאה גם גרסה שנייה שלו שמשתמשת בספרייה Libwebsockets במקום. ה-user agent קבוע בקוד ולא משתנה, firefox/21.0, גרסה שיצאה לפני יותר מעשור.
כל הודעה בפרוטוקול הבינארי נפתחת בכותרת של 44 בתים, שהשדה הראשון בה הוא מפתח XOR אקראי באורך 16 בתים שבו מוצפנת שאר הכותרת. הנתונים עצמם נדחסים, ובאופן אופציונלי מוצפנים ב-RC4. חילופי המפתחות שבפתיחה מסודרים בצורה שקשה לקרוא בלי לעצור: השרת שולח 32 בתים, הנוזקה מתעלמת מהם לגמרי, מייצרת חוצץ אקראי באורך 1,024 בתים ולוקחת את 32 הבתים הראשונים שלו כמפתח RC4 להמשך. במקביל היא בונה חוצץ של 256 בתים שמתחיל במחרוזת google.com ואחריה בתים אקראיים, מצפינה אותו ב-RC4 ושולחת אותו יחד עם החוצץ האקראי. השרת מחשב מהחוצץ את אותו מפתח, מפענח ומוודא שהוא מקבל את המחרוזת המוסכמת, ומחזיר לאישור מספר פקודה אקראי שנבחר בין 1 ל-45. אורך ההודעה עצמו נבחר אקראית בין 292 ל-1,282 בתים, כדי שלא יהיה לתעבורה חתימת גודל אחידה.
אוצר המילים של הרכיב המרכזי קטן. החוצה הוא שולח את 1110, שם מחשב ושם משתמש, את 1112, מזהה קבוע כמו 20001, ואת 1150 לשמירת החיבור. פנימה הוא מקבל את 1020 לטעינת מודול, את 1030 להסרתו, את 1050 ו-1150 להעברת נתונים למודול, ואת 1070 לכיבוי דגלים פעילים.
מה המודולים עושים? מיקרוסופט לא יודעת
כאן הדוח נעצר, ובכוונה. ארבע הפקודות שמגיעות מהשרת מוכיחות שהמסגרת בנויה להרחבה, אבל מיקרוסופט כותבת במפורש שהיכולות של המודולים עצמם לא אומתו. היא לא ראתה אותם, ולכן היא לא מתארת אותם. מודול אחד כן הגיע לידיה, מגרסה ישנה יותר של הנוזקה שארוזה בארכיון בשם libcurl: ארכיון קטן של ארבעה קבצים בלבד, שאחד מהם נקרא is ותפקידו שימור אחיזה במכונה דרך מנגנון ה-services של ווינדוס. אותה דגימה ישנה נצפתה לראשונה ב-3 באוקטובר 2025, בעוד שהטוען והארכיון של גרסת WinSparkle נצפו במאי 2026. שבעה חודשים של פיתוח מפרידים בין ארכיון של ארבעה קבצים לארכיון של אחד עשר.
פרט קטן שכדאי להחזיק ממנו: הרכיב המרכזי יוצר במכונה mutex ששמו מורכב משם המשתמש ומשם התהליך המארח, למשל Contoso-Poedit.exe. זה המנגנון שמונע מהנוזקה לרוץ פעמיים, וזה גם אחד המקומות שבהם היא מסגירה את עצמה.
מה זה אומר בפועל
אם אתם מנהלים רשת ארגונית, שלוש הידיות המעשיות שהדוח נותן הן פשוטות לבדיקה. חפשו תעבורה יוצאת אל corp.tripswithengine[.]com, חפשו ברשומות הרשת את ה-user agent הקבוע firefox/21.0 שאין לו שום סיבה להופיע בתעבורה עסקית ב-2026, ועברו על נתיבי הקבצים שמיקרוסופט פרסמה, במיוחד ספריות בשמות מוכרים שיושבות תחת ProgramData ליד קובץ תאום בלי סיומת. מיקרוסופט ממליצה בנוסף להפעיל שני כללי צמצום משטח תקיפה ב-Defender, אחד שחוסם הרצה של קבצים שאין להם היסטוריה או ותק מוכרים, והשני שחוסם הרצה של סקריפטים מעורפלים.
וצריך לזכור מה הממצא הזה מסמן. כשמוצאים NeedyMantis על מכונה, השאלה החשובה היא לא איך היא נכנסה לשם אלא מי כבר היה ברשת מספיק זמן כדי להתקין אותה, ומה עוד הוא עשה בינתיים.
תגובות