vm2 היא ספרייה ל-Node.js שתפקידה להריץ קוד שאי אפשר לסמוך עליו בתוך ארגז חול, סביבה מבודדת שממנה הקוד לא אמור לצאת. חולשה שתוקנה בגרסה 3.12.1 מאפשרת לקוד כזה לפרוץ מהבידוד ולהריץ פקודות על השרת עצמו. הציון הוא 10 מתוך 10, המקסימום בסולם.
מה החולשה?
כשתוכנה צריכה להריץ קוד שמישהו אחר כתב, למשל תוסף שלקוח העלה או נוסחה שהוזנה בממשק, היא רוצה לתת לקוד לרוץ בלי לתת לו את השרת. הפתרון הרגיל הוא לעטוף אותו בשכבה שמתווכת כל בקשה שלו החוצה. ב-vm2 השכבה הזאת נקראת bridge, גשר, והיא זו שמעבירה פונקציות מצד המארח אל הקוד המבודד ועוטפת כל דבר שחוזר.
הבעיה מתחילה כשהקוד המבודד קורא לפונקציה כזאת בלי האובייקט שהיא שייכת אליו: למשל קריאה בסגנון fn() סתם, או fn.call() בלי ארגומנטים. במצב כזה הערך שמתאר את "מי קרא" הוא undefined, ו-vm2 העביר אותו הלאה אל המארח כמו שהוא.
כאן נכנס לתמונה מנוע JavaScript עצמו. כשפונקציה שאינה כתובה במצב strict מקבלת undefined במקום הזה, המנוע מחליף אותו באובייקט הגלובלי של הסביבה שבה הפונקציה הוגדרה, כלומר של המארח. vm2 קיבל בחזרה את האובייקט הזה, עטף אותו לפי הכללים הרגילים שלו והחזיר אותו אל תוך ארגז החול. מהרגע הזה יש לקוד המבודד ידית חיה אל הסביבה שמחוצה לו.
מה עושים עם ידית כזאת מתואר ברשומה: מגיעים דרכה אל process, האובייקט שמייצג את התהליך שרץ על המכונה, ומשם מריצים פקודות מערכת. הרשומה אפילו נוקבת בנתיב המדויק, process.getBuiltinModule('child_process').execSync. זו כבר לא בריחה מארגז החול, זו שליטה בשרת.
מי מושפע?
כל הגרסאות של vm2 עד 3.12.0 כולל, והתיקון הוא 3.12.1. המושפעים אינם משתמשי קצה אלא שירותים שמריצים קוד של אחרים: פלטפורמות שמאפשרות ללקוח לכתוב סקריפט משלו, מנועי אוטומציה, כלים שמריצים תוספים של צד שלישי.
תנאי אחד מגביל את הניצול והוא כתוב ברשומה במפורש: האפליקציה שמשתמשת ב-vm2 צריכה לחשוף אל ארגז החול לפחות פונקציה אחת שאינה במצב strict. פונקציות במצב strict ופונקציות שמגיעות ממודולים מסוג ES אינן מושפעות. האם התנאי מתקיים אצלכם תלוי בקוד שלכם ולא בהגדרה של vm2, וזו גם הבדיקה הראשונה שכדאי לעשות.
בישראל יש לא מעט חברות SaaS שמריצות לוגיקה של לקוחות בתוך התהליך שלהן עצמן, וזה בדיוק הדפוס שבו ספרייה כזאת נכנסת לשימוש.
האם מנוצלת בפועל?
נכון לעדכון האחרון החולשה אינה מופיעה בקטלוג ה-KEV של סוכנות הסייבר האמריקאית CISA, רשימת החולשות שידוע כי תוקפים מנצלים אותן בפועל, ואין ברשומה מידע על ניצול שנצפה.
מה שכן יש הוא הודעת אבטחה שפורסמה במאגר של vm2, ובה תיאור מלא של המסלול כולל צורות הקריאה שמפעילות אותו. כשהמסלול פומבי ברמת פירוט כזאת, הפער בין היעדר ניצול שנצפה לבין ניצול הוא פער של זמן.
מה עושים?
1. בדקו אם vm2 נמצא אצלכם בכלל, גם בעקיפין דרך ספרייה אחרת. הפקודה npm ls vm2 מציגה את הגרסה ואת מי שמושך אותה.
2. עדכנו לגרסה 3.12.1 ומעלה. זו הגרסה שבה התיקון נכנס.
3. עברו על הפונקציות שאתם חושפים אל הקוד המבודד ובדקו אילו מהן אינן במצב strict. לפי הרשומה פונקציות strict ומודולי ES אינם מושפעים, כך שזו גם הדרך לצמצם את החשיפה בצד שלכם.
4. אם אתם מריצים קוד של לקוחות ולא עדכנתם, הניחו שכל מי שיכול היה להזין קוד יכול היה גם להריץ פקודות על המכונה. מעבר על יומני ההרצה והחלפה של סודות ששמורים על אותו שרת הם הצעד שבא אחרי העדכון, לא במקומו.