בקשת HTTP אחת הגיעה ב-20 בדצמבר לשרת SharePoint מקומי, נגעה ב-endpoint הניהולי של רכיב ToolPane, וחזרה. היא לא ניסתה לפרוץ אלא רק לבדוק אם אפשר, והמקור שלה היה ONYPHE, פלטפורמה שסורקת את האינטרנט ובודקת בפועל אם מערכת חשופה לחולשה ספציפית. שתים-עשרה דקות אחר כך נכנס חיבור שני, הפעם מכתובת של ספק אחסון שמוכר כ-bulletproof hosting, וגם הוא לא עשה כלום. אחריו נפלה שתיקה של שלושה ימים, ובלי שום המשך שיקשר את הסריקה למשהו, האירוע סומן כעדיפות נמוכה.

ב-24 בדצמבר, ערב חג המולד, כשבחברות רבות צוות האבטחה מצומצם, קיבל אותו endpoint בקשה אחרת לגמרי. חוקרי קספרסקי אלכסנדר רודצ'נקו ורודריגו ונדרסון פרסמו היום את הניתוח של מה שקרה מהרגע הזה, והפרט שמחזיק את כל הדוח הוא בדיוק מה שלא נמצא בו: קובץ. ה-webshell שהתוקפים הריצו על השרת לא נכתב לדיסק באף שלב. נעבור על איך הוא נכנס לתוך תהליך העבודה של IIS ונשאר שם, איך הוא מתחפש לתעבורת HTTP רגילה, ואיך משם הקבוצה הגיעה להרשאות SYSTEM ולניסיון להריץ כופרה.

ToolShell, ולמה שרת SharePoint מקומי הוא יעד נוח

ToolShell הוא השם שדבק בשתי חולשות ב-SharePoint המקומי שנחשפו ביולי 2025. הכבדה מהן, CVE-2025-53770, קיבלה ב-NVD ציון CVSS 9.8, והיא עובדת על עיקרון פשוט. השרת מקבל בבקשה מבנה נתונים ארוז ומפרסר אותו בחזרה לאובייקטים בזיכרון, אלא שהנתונים שנכנסים הם אלה שקובעים אילו טיפוסי אובייקטים ייבנו ואילו שלבי חיים שלהם יופעלו בדרך. מי ששולט בקלט שולט במה שה-runtime בונה, ובמה שמורץ תוך כדי. לזה קוראים deserialization של קלט לא מאומת, וכאן זה נותן הרצת קוד על השרת בלי שום חשבון מקדים. השנייה, CVE-2025-53771, חמורה פחות בציון 6.5 ומשמשת בשרשרת כדי לעקוף את בדיקת הזהות.

מיקרוסופט פרסמה ב-22 ביולי 2025 ניתוח של הניצול בפועל וייחסה חלק מהפעילות לקבוצה שהיא מסמנת Storm-2603, שלדבריה הפעילה בעבר את הכופרות Warlock ו-LockBit אחרי ניצול שרתי SharePoint מקומיים. קספרסקי לא נוקבת בשנה של האירוע שהיא מתארת, אלא רק בתאריכים שבדצמבר, והדוח מוצג כניתוח רטרוספקטיבי שנעשה אחרי גל ToolShell של 2025.

למה דווקא השרת הזה? כי הוא חשוף לאינטרנט, כי תהליך העבודה של IIS שמריץ אותו מחזיק את ההרשאה שתהיה חשובה בהמשך, וכי הוא מפרסר נתונים ארוזים במנגנונים שמיקרוסופט עצמה מגדירה כלא בטוחים. את BinaryFormatter היא מתארת כמשהו שאי אפשר להפוך לבטוח, ומ-.NET 9 המימוש המקורי שלו הוסר משימוש רגיל, ובכל זאת הוא ממשיך להופיע במוצרים שלה, SharePoint ביניהם, וכך גם LosFormatter שבו משתמש הדף הפגיע.

איך מריצים webshell בלי לכתוב קובץ לדיסק

דף ב-ASP.NET לא חייב להיות קיים על הדיסק. יש שכבת קומפילציה שיודעת לקבל "נתיב וירטואלי", לבקש ממישהו את התוכן שמאחוריו, ולקמפל ממנו דף בזמן ריצה כאילו היה קובץ רגיל. המישהו הזה הוא רכיב שנרשם לשרשרת של ספקי נתיבים, ושמו VirtualPathProvider. התוקפים הבינו שאם הם רושמים ספק משלהם, הם מקבלים קוד שמורץ בתוך גבול האמון של האפליקציה בלי שיהיה לו ייצוג בדיסק.

הרכיב המרכזי של ה-webshell, שקספרסקי מכנה G.cs, עושה בדיוק את זה. הוא רושם ספק נתיבים משלו דרך מתודה לא ציבורית במחלקת HostingEnvironment, ומאציל את מדיניות אורך החיים שלו לספק הקודם בשרשרת. בעיה אחת נשארת: באפליקציה שעברה קומפילציה מוקדמת הרישום הזה לא היה אמור לעבוד. אז המודול בודק בעזרת reflection אם האפליקציה מקומפלת מראש, שולף מצביעים לשדות מצב פנימיים של המחלקה שמנהלת את הקומפילציה, BuildManager, ומשנה אותם בזמן ריצה כדי לכפות את ההזרקה. קספרסקי ממפה את המהלך הזה ל-T1553 בטקסונומיית MITRE, חבלה במנגנוני האמון.

מה שמתקבל הוא webshell בלי קובץ. אין מה לסרוק. המחיר הוא שהוא חי רק כל עוד חי ה-application domain שבו הוא יושב, ומתאפס ברגע שה-application pool מתרענן או ש-IIS מופעל מחדש.

הערוץ אל התוקף מתחבא במקום דומה. G.cs תופס את הפונקציה GetCacheKey כנקודת היירוט בזרימת הטיפול בנתיבים, בודק את הכותרות של כל בקשה נכנסת, ומתעורר רק כשהוא מזהה User-Agent מסוים. בכל מקרה אחר הוא נשאר רדום, מה שמקשה לזהות את התעבורה שלו. התקשורת עצמה לא נסמכת על beacon מוכר אלא על פרוטוקול בינארי עצמי עם עירפול XOR, והמודול מקים דרכה מנהרת TCP דו-כיוונית מלאה: הוא מצמיד בזיכרון ערוצי קריאה וכתיבה של HTTP לאותו socket, ומאפשר לשרת השליטה והבקרה (C2, השרת שממנו התוקף שולח פקודות) לפתוח חיבורים לכל מכונה אחרת ברשת הפנימית, כשהתעבורה מנותבת דרך שרת ה-SharePoint. בצילומי המסך שבדוח רואים שהם השתמשו ב-suo5 למימוש הפרוקסי. כדי שהמנהרה תעמוד בעומס, G.cs מעלה את מגבלות ה-ThreadPool של האפליקציה.

מ-IIS ל-SYSTEM, דרך צינור בעל שם

עד כאן איך התוקף נכנס ומדבר. הצד השני, איך הוא עולה בהרשאות, מעניין יותר.

השרשרת מתחילה ב-Prog.cs, שמקבל את ארגומנטי שורת הפקודה ומעביר אותם ל-London.cs, הרכיב שמפרסר פקודות ומפעיל את שאר המודולים. FileDownloader.cs מוריד קבצים מכתובות שה-C2 מספק אל תיקיית ProgramData, MSI.cs מתקין חבילות MSI בשקט, ושלושה מודולים נוספים אוספים מי מחובר למכונה ובאיזה סוג session, אילו תהליכים רצים ומאיפה, ואילו חיבורי רשת וממשקים קיימים. הכול נשאר בזיכרון, והתוקף שולף אותו דרך המנהרה של G.cs.

המודול שמעניין מכולם הוא User.cs, שיוצר חשבונות אדמיניסטרטיביים. הוא עושה את זה לא דרך כלי שורת הפקודה המקובלים אלא ישר מול תת-מערכת ה-SAM, בעזרת עטיפת P/Invoke לספרייה הילידית samlib.dll. לפי קספרסקי זה נראה כניסיון גם להתחמק מזיהוי וגם להשאיר גישה מורשית שתשרוד אחרי שה-webshell עצמו ייעלם.

ההסלמה ל-SYSTEM נשענת על עובדה אחת: תהליך העבודה של IIS מחזיק את ההרשאה SeImpersonatePrivilege, כלומר הוא רשאי לפעול בשם חשבון אחר אם הגיע לידיו token שלו. אז צריך רק לשכנע תהליך מורשה יותר להתחבר אליך. London.cs מקים צינור בעל שם זדוני, ומחלקה בשם EfsrTiny, גרסה מוקטנת של הכלי EfsPotato, קוראת לפונקציה EfsRpcEncryptFileSrv בפרוטוקול EFSRPC עם נתיב מפוברק שמצביע ל-localhost. LSASS, התהליך שמחזיק את פרטי האימות במערכת, מתחבר בתגובה אל הצינור של התוקף. משם ImpersonateNamedPipeClient חוטף את ה-token שלו, ו-CreateProcessAsUser מפעיל תהליך חדש בהרשאות SYSTEM. באותו namespace יש גם שכבת לקוח MSRPC מינימלית שמגדירה בעצמה מבני stub ומזייפת ממשק, כדי לדבר ישירות עם ה-runtime של RPC.

ואז הם הפסיקו להסתמך על ה-webshell בכלל

זמן קצר אחרי ההזרקה, תהליך העבודה הנגוע הוליד msiexec.exe שהתקין חבילת MSI מאוחסנת בענן של Supabase. מה שהותקן היה Velociraptor, כלי קוד פתוח לתגובה לאירועים ולחקירות פורנזיות, בגרסה 0.73.4.0 שסוחבת את החולשה CVE-2025-6264 בציון 5.5. טאלוס של סיסקו דיווחה ב-9 באוקטובר 2025 על ניצול לרעה של אותו כלי כמסגרת הרצה אחרי פריצה, וייחסה את הפעילות ל-Storm-2603 בביטחון בינוני. במקרה שקספרסקי מתארת הכלי שימש לשימור אחיזה, לאיסוף מידע ולהרצת מטענים.

דרכו הקבוצה פרסה ארבעה ערוצי גישה בלתי תלויים: סוכן תמיכה מרחוק של Zoho Assist, מנהור דרך ה-CLI של VS Code, Cloudflare Tunnel, וכלי פרוקסי הפוך קטן. ההיגיון ברור: אם אחד הכלים ייתפס ויימחק, הגישה נשארת. בנקודה הזאת הם גם הפסיקו להיות תלויים במחזור החיים של תהליך ה-IIS ובחולשת ToolShell עצמה.

אחר כך הגיע השלב שבו מכבים את ההגנה. קספרסקי זיהתה דרייבר פגיע בנתיב C:\programdata\rsndispot.sys, מהסוג שתוקפים משתמשים בו כדי לחסל מוצרי EDR, ולידו DLL זדוני שהוזרק בטכניקת sideloading וזוהה כרכיב כיבוי ההגנה של כופרת Warlock. הניסיון להריץ את הכופרה עצמה נעשה, ונחסם על ידי מודול ההגנה הפרואקטיבית של Kaspersky Endpoint Security. את הייחוס קספרסקי מנסחת בזהירות. "יחד עם תרחיש ניצול ToolShell, זה מאפשר לנו לייחס את המקרה הזה ל-Storm-2603 בביטחון בינוני", כתבו החוקרים, על בסיס ניצול ה-SharePoint, רכיב כיבוי ההגנה המקושר ל-Warlock, והתנהגות שאחרי הפריצה שמתאימה לחדירות אחרות של אותה קבוצה.

מה מחפשים כשאין קובץ לחפש

לקספרסקי שני סימנים להציע, ושניהם לא קבצים. הראשון הוא רגע ה-deserialization עצמו. בקשה ל-endpoint של ToolPane שמיד אחריה נטענות באותו חלון הרצה גם ההרכבה App_Web_toolpane.aspx וגם ההרכבה העזר Microsoft.GeneratedCode היא דגל. כל אחת מהן לגיטימית בנפרד, אבל הצירוף אומר שה-runtime מרכיב גרף אובייקטים מנתונים שהגיעו בבקשה. מה שכדאי לסרוק בזיכרון ובהרכבות העזר הם שמות כמו ObjectDataProvider, ExpandedWrapper ו-LosFormatter, שמעידים שהקלט הפך לאובייקטים מסוכנים. קספרסקי שחררה לצורך הזה כלי בשם CLRDeserializationExploitFinder.

הסימן השני הוא הנתיב הווירטואלי, ומקור הטלמטריה הטוב ביותר אליו הוא ספק ה-ETW של Microsoft-Windows-IIS-Configuration. אירוע 25 מתעד מיפוי של נתיב תצורה לנתיב פיזי ואירוע 47 מיפוי לתיקייה, ובמקרה של נתיב שמוגש בידי ספק וירטואלי שני האירועים מופיעים בלי שיהיה קובץ בנתיב. לכך מצטרף סימן שלילי: לקובץ אמיתי IIS בונה שרשרת ניטור שינויים שמשתקפת באירועים 17, 18, 19 ו-21, ובנתיב וירטואלי השרשרת הזאת חסרה. נוכחות של 25 ו-47 בלי 17, 18, 19 ו-21 לאותו נתיב היא התבנית, ואת התוצאות מסננים מול רשימת היתר של מימושי VirtualPathProvider לגיטימיים. הפער בין מה ש-IIS חושב שקיים לבין מה שיש בדיסק הוא הסימן.

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

אם אתם מריצים SharePoint מקומי חשוף לאינטרנט ולא החלתם את תיקוני ToolShell של יולי 2025, זה הדבר הראשון, והוא קודם לכל השאר כאן. למי שכבר עדכן, הלקח הוא אחר: ציד webshell-ים לפי קבצים לא היה מוצא את זה. מה שכן, התנהגות חריגה של תהליך העבודה של IIS הייתה נראית, ובפרט חיבורים יוצאים שאין להם סיבה, חיטוף token-ים, כפיית RPC, והופעה פתאומית של כלי ניהול מרחוק או כלי DFIR על שרת אפליקציה. ושימו לב לאסימטריה: רענון של ה-application pool מוחק את ה-webshell מהזיכרון, אבל ארבע המנהרות, חשבונות האדמין שנוצרו דרך SAM והדרייבר הפגיע שנשאר בדיסק לא זזים ממקומם.