קובץ לוג שהוחלף בקישור סימבולי נתן לתוקפים root על שרת הדואר: מיקרוסופט מתארת את גל הפריצות ל-Zimbra
בין 28 ביולי ל-7 באוגוסט הגיעו לשרתי דואר של Zimbra בקשות שלא ביקשו שום דבר. הן לא הורידו קובץ ולא השאירו מטען. הן רק בדקו אם פקודה שנשתלה בתוך בקשת דואר אכן מורצת על השרת, ודיווחו על התוצאה לכתובת חד-פעמית בשירות חיצוני. חלק מהבקשות נשאו שדה User-Agent במחרוזת ZB73570, שהיא בדיוק מספר החולשה שטרם פורסמה לציבור.
התיקון לאותה חולשה, CVE-2026-73570, שוחרר ב-20 ביולי בגרסה 10.1.20. לציבור היא פורסמה ב-13 באוגוסט. כלומר הסריקות רצו בתוך החלון שבין הרגע שבו התיקון היה קיים לרגע שבו מישהו מחוץ ל-Zimbra ידע מה הוא מתקן, וחוקרי Microsoft Threat Intelligence, שתיארו את הממצאים בדוח שפורסם ב-30 בספטמבר, סופרים שני כלי סריקה נפרדים שעבדו שם על אותו נתיב הרצה בדיוק.
מה שבא אחרי זה היה פחות עדין. בהמשך נראה איך בקשת דואר יחידה מגיעה בכלל לרכיב ניטור של השרת, איך התוקפים הפכו חשבון שירות מוגבל ל-root דרך קובץ לוג, ולמה דווקא הגניבה של מפתח אחד היא החלק שתיקון לא פותר.
למה בקשת דואר מגיעה בכלל לרכיב ניטור
Zimbra Collaboration Suite הוא שרת דואר ושיתוף שעובדים רבים מכירים כממשק הוובמייל של המקום, ומתחתיו רץ מערך שלם של רכיבי לינוקס: שרת LDAP למשתמשים, מסד נתונים MySQL לתיבות, Postfix לדואר. אחת החבילות האופציונליות שם נקראת zimbra-snmp, ותפקידה לדווח לכלי ניטור חיצוניים כשמשהו במערכת משתנה.
הדיווח הזה נבנה בצורה פשוטה מדי. כשרכיב מנטר את בריאות השירותים מזהה שינוי מצב, הוא קורא לכלי שורת פקודה שמוציא את ההתראה, ומשרשר לשורת הפקודה ההיא ערך שהגיע מתוך בקשת הדואר הנכנסת. ערך שבא מבחוץ ולא עבר סינון יוצא ככה מהתפקיד של "נתון" ונכנס לתפקיד של "פקודה", וכל מה שהתוקף צריך הוא לשתול בבקשת ה-SMTP תווים שהמעטפת של לינוקס קוראת כסוף של פקודה אחת ותחילת פקודה חדשה. לסוג החולשה הזה קוראים OS Command Injection, והיא מסומנת ב-CWE-78.
ב-NVD החולשה מקבלת ציון CVSS 8.9, עם וקטור שמציין שאין צורך בהזדהות ואין צורך בשיתוף פעולה של משתמש. הפקודות רצות בהרשאות חשבון השירות zimbra, לא כ-root, וזה מה שהופך את הצעד הבא בשרשרת לקריטי. התנאי להרצה מצומצם: צריך שחבילת zimbra-snmp תהיה מותקנת ושהתראות SNMP יהיו מופעלות. Zimbra עצמה מתארת את 10.1.20 כתיקון הקבוע לחולשת ה-SNMP שהופיעה בהתרעה שלה מ-26 ביוני, לצד שמונה תיקוני אבטחה נוספים באותה גרסה. קטלוג ה-KEV של CISA הכניס אותה ב-21 באוגוסט עם מועד תיקון של 24 באוגוסט לגופים פדרליים, וסימן אותה כדורשת תחקור פורנזי ולא רק עדכון.
ה-web shell שנבנה מחלקים ואז נעלם
ההרצה הראשונה כמעט לא השאירה סימנים, בכוונה. התוקפים פתחו לרגע הרשאות כתיבה לספרייה שמוגשת לאינטרנט, הרכיבו מחדש מטען דחוס ומקודד מתוך חלקים שהועלו בנפרד, כתבו את התוצאה כקובץ JSP אל תיקיות היישום הפומביות, מחקו את החלקים ששימשו לבנייה והחזירו את ההרשאות של הספרייה לקדמותן. מי שיבדוק אחר כך את הרשאות התיקייה לא יראה כלום חריג. מה שנשאר הוא קובץ אחד שמריץ פקודות דרך בקשת HTTP רגילה, כלומר web shell.
ולא אחד. מיקרוסופט מתארת כמה קובצי JSP כאלה על נתיבי Jetty ו-mailboxd, כולל עותקים שהועברו לשרתי תיבות נוספים, כדי שהסרה של אחד לא תסגור את הגישה. במקביל נעשה שימוש בהרצה ישירה בלי קובץ: הורדת תוכן עם wget או curl והעברתו למעטפת, משימות cron ויחידות systemd להרצה חוזרת, ובאחד המקרים הרצה מהזיכרון בלבד דרך memfd_create, בלי קובץ שיישאר על הדיסק.
גם המעטפת ההפוכה שהתוקפים הקימו נבנתה מכלים שממילא מותקנים על השרת: צינור בעל שם תחת /tmp/s חיבר מעטפת אינטראקטיבית אל רכיב הלקוח של openssl, שהחזיק חיבור מוצפן אל endpoint מרוחק. הפקודות נכנסו משם למעטפת והפלט חזר באותו ערוץ. אין כאן נוזקה שאפשר לחתום עליה.
מחשבון השירות ל-root, דרך קובץ לוג
בשלב הזה לתוקפים יש הרצת קוד בהרשאות חשבון zimbra בלבד. הדרך שהם מצאו לעלות מכאן ל-root מנצלת את העובדה ש-Zimbra מגיעה עם כלי עזר משלה שמותר להם לרוץ בהרשאות מוגברות דרך sudo. אחד מהם, מנהל התיבות, כותב לקובץ לוג בספרייה שחשבון zimbra יכול לכתוב אליה.
התוקפים מחקו את קובץ הלוג והשאירו במקומו קישור סימבולי שמצביע על קובץ התצורה של sudo תחת /etc/pam.d. אחרי זה הם הפעילו את מנהל התיבות המורשה. הוא כתב, כמו תמיד, ללוג שלו, אבל הלוג היה קישור, ולכן קובץ התצורה של sudo עבר לבעלות חשבון השירות zimbra. מהרגע הזה התוקף יכול לערוך אותו. הוא הוסיף שם hook שמריץ סקריפט מקומי בכל פתיחת סשן, והפעיל סשן sudo דרך כלי עזר לגיטימי אחר של Zimbra. הסקריפט רץ בהרשאות root והוסיף לחשבון zimbra הרשאת sudo מלאה בלי סיסמה. בסיום הם שחזרו את קובץ ה-PAM המקורי ומחקו את קובצי העזר. ההרשאה נשארה. הראיות לא.
מיקרוסופט מציינת שברכיב השליטה של התוקפים היו באותו שרת גם נתיבי הסלמה חלופיים דרך Postfix, סוכן Java ו-nginx, אבל תהליכי הבן שתועדו בפועל מצביעים על נתיב מנהל התיבות וה-PAM כזה שהצליח. בסביבה אחרת הם פשוט הורידו ערכת כלים ציבורית להסלמת הרשאות מ-GitHub והריצו אותה מזיכרון.
מה הם באמת לקחו: המפתחות, לא הסיסמאות
כאן מגיע החלק שקשה להחזיר לאחור, והוא לא נוגע בסיסמאות של המשתמשים. בעזרת כלי ניהול התצורה של Zimbra התוקפים קראו את פרטי האימות של השירותים עצמם, אלה של LDAP, MySQL, Postfix, Amavis ושירות השכפול. עם פרטי האימות האלה הם פנו לשרת ה-LDAP כמשתמש מורשה ושאלו אותו שאלות ממוקדות מאוד.
מה שהם ביקשו משם היה שלושה מאפיינים. Zimbra חותמת את ה-token-ים שמחזיקים את הסשנים של כל המשתמשים שלה במפתח אחד; מי שמחזיק אותו יכול לייצר token תקף לכל חשבון שירצה, בלי הסיסמה ובלי הקוד מהאפליקציה. המפתח השני מאפשר לבנות כתובות התחברות שכבר כוללות אימות מוכן עבור כל משתמש. השלישי הוא הסוד של האימות הדו-שלבי. זאת הסיבה שהנחיות הטיפול של מיקרוסופט לא מסתפקות בעדכון אלא דורשות להחליף את מפתחות האימות של Zimbra בכל הדומיינים: עדכון הגרסה לא מבטל מפתח שכבר יצא מהשרת.
בסביבה אחת הופיע גם כלי שנכתב במיוחד למשימה. היה זה קובץ הרצה ב-Go שבתוכו נשאר נתיב המודול zimbra-exfil/client-dump, והוא עושה את כל הרצף לבד: קורא את קובץ התצורה המרכזי של Zimbra, מחלץ משם רשימה מוגדרת של סיסמאות שירות, בונה מהן מחרוזות התחברות ל-MySQL ול-LDAP המקומיים דרך socket, ומייצא את תוכן הטבלאות של התיבות, המטא-דאטה שלהן, המכשירים הניידים הרשומים וכללי המענה האוטומטי, לצד כל הטבלאות שתחת המרחב zimbra. פונקציה נפרדת אוספת גם תעודות SSL והמפתחות הפרטיים שלהן, והכול נארז לארכיון ZIP בספרייה עם חותמת זמן תחת /tmp, מוכן לשליחה לכתובת שמתקבלת כפרמטר מקודד בשורת הפקודה.
מהשרת הראשון לכל ה-cluster
השרת הראשון היה רק הכניסה. Zimbra מחזיקה זהות SSH משל עצמה כדי ששרתי ה-cluster יוכלו לדבר ביניהם, והתוקפים השתמשו בה: התחברות לא אינטראקטיבית לשרתים השכנים, עם בדיקת מפתח השרת המרוחק מושבתת, ואחריה העברת חלקי מטען, סקריפטים וקובצי JSP בעזרת rsync. בצד המקבל הם אימתו שהחלקים הגיעו שלמים, ובצד השולח מחקו את המקור.
לשימור האחיזה הם התקינו יחידת systemd בשם zimlog.service. השם נראה כמו רכיב לוגים של Zimbra, אבל הקובץ הושם ב-/etc/systemd/system, מחוץ לספריות היישום, הוקצה לחשבון השירות של Zimbra, הופעל לעלייה באתחול, וחותמות הזמן שלו שונו כדי להתאים ליחידות ותיקות כמו rsync.service ו-sshd.service. במקביל הגיע סוכן גישה מרוחקת מלא בשם zimclient2, שמותקן על ידי כלי שמוריד אותו, מאמת אותו מול גיבוב SHA-256 ובוחר נתיב התקנה ושם קובץ בזמן ריצה. הסוכן נותן מעטפת אינטראקטיבית, העברת קבצים בשני הכיוונים ו-proxy מסוג SOCKS5, מעל WebSocket, TLS או TCP גולמי, כלומר גם דריסת רגל להמשך תנועה ברשת. על חלק מהשרתים נוסף כורה מטבעות שהוסתר תחת שם שנראה כמו תהליך גרעין, וחיסל כורים מתחרים.
ובשרת אחד הם הגיעו למה שכל זה נבנה בשבילו. התוקף ארז את הגיבוי העדכני של התיבות לארכיון בודד, הוריד את כלי ההעתקה AzCopy של מיקרוסופט מהקישור הרשמי שלו, והריץ אותו מול כתובת Azure Blob עם הרשאת גישה שהוא עצמו סיפק. מיקרוסופט מדגישה שהראיות הקיימות לא מאשרות שההעברה הושלמה. (וזאת הפעם השנייה בתוך רבעון שאנחנו מתארים כאן פריצה לשרתי Zimbra, אחרי הקמפיין של יולי שנכנס דרך הדפדפן של המשתמש ולא דרך רכיב הניטור.)
את השורה החשובה בדוח כתבו החוקרים בעצמם, בפרק ההמלצות: "אל תסתמכו רק על זיהויים לפי שם נוזקה. כמה מהתוצאות המשמעותיות ביותר בקמפיין הזה, ובהן הכנת מחסן הדואר השלם לניסיון הוצאת מידע, לא כללו שום משפחת נוזקה, אלא רק מעטפת אינטראקטיבית פשוטה." מיקרוסופט גם מבהירה שתרשים שרשרת התקיפה שלה מרכיב התנהגויות ממספר פריצות מאומתות, ושלא בהכרח היה שרת בודד שהציג את כל השלבים. הארגונים שנפגעו זוהו ביותר מאזור אחד וביותר מענף אחד.
מה זה אומר בפועל
אם אתם מריצים Zimbra בעצמכם, השאלה הראשונה היא הגרסה: כל מה שמתחת ל-10.1.20 חשוף, וכשהעדכון לא יכול לקרות מיד, הסרת חבילת zimbra-snmp וכיבוי התראות ה-SNMP מנטרלים את נתיב ההרצה, לצד הגבלת הגישה ל-SNMP ול-SMTP לשרתים מוכרים בלבד. אם השרת היה חשוף לאינטרנט בחודשים האחרונים, עדכון לבד לא מספיק: צריך להחליף את מפתחות האימות של Zimbra בכל הדומיינים, לחפש קובצי JSP חדשים בתיקיות היישום של כל שרתי התיבות ולא רק בזה שהתריע, ולעבור על יחידות ה-systemd ולחפש בעלות או חותמות זמן שלא מסתדרות. ושווה לזכור שהנתונים הגולמיים של מערכות הציד נשמרים בדרך כלל 30 יום, כך שהפעילות מיולי ומאוגוסט כבר מחוץ לחלון, ואת החיפוש אחריה צריך לעשות בלוגים המאורכבים.
תגובות