השרת מוסיף סיומת משלו לשם הקובץ, ובייט אפס אחד מוחק אותה: החולשה בציון 9.8 ב-FortiSIEM שלא משאירה שורת לוג
על כל התקנת FortiSIEM יושב קובץ קטן בשם redishb.sh. הוא בודק ששירות ה-redis עדיין חי, אף אחד לא מסתכל עליו, וה-cron מריץ אותו כל 60 שניות. ב-20 באוקטובר פרסמו חוקרי Horizon3 ניתוח שמראה איך תוקף שלא התחבר לשום דבר ולא הציג שום פרטי אימות יכול להחליף את תוכנו מרחוק, ואז פשוט לחכות עד דקה.
שבוע קודם לכן, ב-13 באוקטובר, פרסמה Fortinet הודעת אבטחה שכותרתה שורה אחת: כתיבת קובץ שרירותית ברכיב grpc phQueryMaster. זאת CVE-2026-106173, שמסווגת כ-path traversal (יציאה מתיקייה מותרת אל מקום אחר במערכת הקבצים, CWE-22), והיא נוגעת לגרסה אחת בלבד, FortiSIEM 7.5.0. את החולשה מצא צוות אבטחת המוצר של Fortinet עצמה. ההודעה אומרת מה נשבר, והניתוח של Horizon3 מסביר למה זה נגמר בהרצת קוד. בהמשך נראה איך השירות הזה בכלל פתוח בלי אימות, איך כתיבה לתיקייה שרירותית הופכת לכתיבה לקובץ מדויק, ולמה דווקא במוצר הזה קשה אחר כך לדעת אם מישהו השתמש בזה.
הדלת הרביעית באותו מוצר
FortiSIEM הוא ה-SIEM של Fortinet, המערכת שאוספת את היומנים מכל השאר, מחומות האש ועד השרתים, ומריצה עליהם כללי זיהוי. ב-Horizon3 מכירים אותו מקרוב: שלוש חולשות קריטיות שהם פרסמו בו לאורך השנים, CVE-2023-34992, CVE-2024-23108 ו-CVE-2025-64155, ישבו כולן באותו רכיב, שירות בשם phMonitor שמאזין ב-tcp/7900. ההודעה של אוקטובר הצביעה הפעם על שירות אחר לגמרי, כזה שהם לא בדקו קודם.
הרכיב הזה נקרא phQueryMaster, ומה שמעניין בו מתחיל במקום שבו הוא מאזין. הוא מרים שרת gRPC (פרוטוקול של גוגל שבו שירותים קוראים זה לפונקציות של זה דרך הרשת, נפוץ מאוד בתקשורת בין רכיבים פנימיים של מוצר), והפורט נגזר בקוד מערך הגדרה בשם command_port שערכו 7918, שעובר דרך פונקציה שממפה אותו ל-tcp/27918. הכתובת שנבנית בסוף היא [::]:27918, כלומר כל ממשקי הרשת של המכונה.
וכאן השורה שעושה את ההבדל. שרת gRPC יכול לדרוש מהלקוח להציג תעודה משלו כתנאי לשיחה, וזאת אפשרות שצריך להגדיר במפורש. בקוד של phQueryMaster היא נשארה במצב שבו השרת לא מבקש תעודת לקוח כלל. המשמעות, כפי שכותב Zach Hanley מ-Horizon3, היא ש"כל לקוח מרוחק יכול להתחבר לשירות ה-gRPC בפורט tcp/27918 ללא אימות ובלי להציג תעודת לקוח". מי שמגיע לפורט, מדבר.
איך כתיבה לתיקייה הופכת לכתיבה לקובץ מדויק
בין המתודות שהשירות חושף יש אחת בשם SendQueryResult, שנועדה לקלוט קובץ תוצאות של שאילתה ולשמור אותו בדיסק. הקריאה אליה נושאת ארבעה ערכים בכותרות, query-id, src-ip, period-start ו-period-end, ואחריהם זורם תוכן הקובץ עצמו. השרת מפרסר את הארבעה ומרכיב מהם נתיב.
שם מחכות שתי התנהגויות נפרדות. הראשונה: את תיקיית היעד השרת בונה בשרשור מחרוזות פשוט, /query/result/ ואחריו ה-query-id שהגיע מהלקוח, בלי שום בדיקה על מה שהתקבל. query-id שמכיל רצפי יציאה מתיקייה מוציא את הכתיבה לכל מקום אחר במערכת הקבצים. השנייה דווקא פועלת לטובת המגן: את שם הקובץ השרת מרכיב בעצמו, בתבנית result-<src>-<start>-<end>-<id>.dat, וה-id שבסוף הוא מונה פנימי שהלקוח לא יכול לקבוע.
אז מה יש לתוקף בשלב הזה? תוכן שהוא בוחר, תיקייה שהוא בוחר, ושם קובץ שהוא כמעט לא שולט בו. זה מעניין, וזה עדיין לא מספיק.
הדרך מסביב לא נוגעת ב-traversal בכלל, אלא באופן שבו השרת מעתיק את הערכים מהכותרות. הוא מעתיק כל ערך לפי האורך שהלקוח הצהיר עליו, ולא עד לבייט האפס הראשון שבו הוא נתקל. בגלל זה אפשר לשתול בייט אפס באמצע ה-query-id, והשרת יעביר אותו הלאה בשלמותו.
מה שקורה אחר כך הוא עניין של מוסכמה ישנה: ברגע שהנתיב מגיע לקריאת המערכת שפותחת את הקובץ, המחרוזת נקראת עד בייט האפס ולא מעבר לו. כל מה שבא אחריו נעלם, כולל הסיומת שהשרת הקפיד להוסיף בעצמו. הכתיבה נוחתת בדיוק על הנתיב שהופיע לפני בייט האפס, והשליטה החלקית בשם הקובץ מתבטלת.
לפרט אחד כאן יש משמעות לחקירה: כשהיעד הוא קובץ שכבר קיים, הכתיבה דורסת אותו במקום ומשאירה את הבעלים ואת ההרשאות המקוריות שלו על כנם. לא נוצר קובץ חדש עם בעלות חשודה. Horizon3 מתארים גם וריאנט שני, שבו שתי בקשות יוצרות קובץ חדש לגמרי בשם מדויק.
מה שכן, יש חיכוך אחד: curl וספריות ה-gRPC הסטנדרטיות לא מרשות לשתול בייט אפס בתוך כותרת, ולכן את הודעת ה-gRPC צריך להרכיב ידנית. זה חסם בפני מי שמנסה להדביק פקודה מוכנה, לא בפני מי שבאמת מתכוון לנצל את זה.
אז למה דווקא סקריפט של redis?
כתיבה לקובץ אינה הרצת קוד, וצריך משהו שיגשר על הפער. ב-Horizon3 חיפשו קובץ שמישהו אחר כבר מריץ עבורם, וב-FortiSIEM יש לא מעט כאלה, כי שירותים שונים בו נשענים על משימות cron שקוראות קבצים מהדיסק. הבחירה נפלה על /opt/charting/redishb.sh, אותו סקריפט בדיקת תקינות ל-redis, שרץ כל 60 שניות ושאפשר לכתוב אליו בהקשר ההרשאות שבו הכתיבה מתבצעת.
השרשרת המלאה, בניסוחם, היא יציאה מהתיקייה, שליטה בשם הקובץ ודריסה של סקריפט שרץ שוב ושוב. אין כאן שחיתות זיכרון, אין עקיפה של הגנות ניצול ואין צורך בדליפת כתובות מהזיכרון. יש בקשת רשת אחת, ואחריה המתנה של עד דקה.
ואיך יודעים אם זה קרה?
כאן מגיע החלק שהכי כדאי לעצור עליו. Horizon3 מצאו שאין ביומני המוצר שום רישום שמעיד על ניצול החולשה. המוצר שכל תפקידו לאסוף את היומנים של הארגון לא מתעד אצלו את הפעולה הזאת.
מה שנשאר הוא חותמות זמן. ההמלצה של החוקרים היא לעבור על זמני הכתיבה והשינוי של קבצים ולחפש כאלה שלא מתיישבים עם הקבצים שסביבם, כששני הקבצים שראוי להתחיל מהם הם /opt/charting/redishb.sh ו-/opt/phoenix/bin/phLicenseTool. בדיקה של בעלות והרשאות, לעומת זאת, לא תעזור כאן, בדיוק מפני שהדריסה במקום משאירה אותן כפי שהיו.
מי חשוף, ומה אומרים שני הציונים
היקף החולשה צר מהרגיל. לפי ההודעה של Fortinet מדובר ב-FortiSIEM 7.5.0 בלבד, התיקון הוא מעבר ל-7.5.1 ומעלה, גרסה 7.6 אינה מושפעת וגם FortiSIEM Cloud אינו מושפע. למי שלא יכול לעדכן מיד, Fortinet כותבת שהגבלת הגישה לפורט tcp/27918 מונעת את הבעיה.
על הציון כדאי לומר מילה, כי מסתובבים שניים. בעמוד ההודעה של Fortinet מופיע 9.4, וב-רשומת NVD מופיע 9.8 שמקורו מסומן [email protected]. אלה לא שתי דעות חלוקות אלא שתי שאלות שונות: 9.8 הוא ציון הבסיס של הווקטור, גישה מהרשת בלי הרשאות ובלי מעורבות של משתמש, ו-9.4 הוא אותו ווקטור אחרי התאמה לנסיבות, ביניהן העובדה שכבר קיים תיקון רשמי. הרשומה ב-NVD עדיין מסומנת Awaiting Analysis, כך שהציון שמופיע בה הוא זה ש-Fortinet הגישה ולא ניתוח עצמאי של NIST.
נכון לקטלוג ה-KEV של CISA שפורסם ב-20 באוקטובר, CVE-2026-106173 אינה מופיעה בו, ו-Fortinet מסמנת בהודעה שהחולשה אינה ידועה כמנוצלת בשטח. גם זה מסתדר עם אופי המוצר: SIEM לא אמור לשבת חשוף לאינטרנט, הוא יושב עמוק בפנים ואוסף מכל מקום. מי שמגיע לפורט 27918 נמצא כבר ברשת. זה לא הופך את החולשה לפחות מעניינת, אלא ממקם אותה בשלב אחר של השרשרת, זה שבו תוקף שכבר נכנס מחפש את המערכת שמחזיקה את היומנים של כל השאר.
מה זה אומר בפועל
אם יש אצלכם FortiSIEM, הבדיקה הראשונה היא מספר הגרסה: רק 7.5.0 חשופה, ומעבר ל-7.5.1 סוגר את זה. עד שתספיקו לעדכן, הגבלת הגישה ל-tcp/27918 כך שרק מארחים מוכרים יוכלו להגיע אליו היא המענה שהיצרן עצמו מציע. ומי שמעדכן באיחור כדאי שלא יסתפק בעדכון עצמו: מכיוון שאין יומן שיספר לו מה קרה, הדבר היחיד שעשוי להעיד על ביקור מוקדם יותר הוא זמן השינוי של אותם שני קבצים.
תגובות