המספר שהשרת שלח בעוגייה חשף את מפתח החתימה שלו: חולשה ב-HFS מאפשרת לזייף עוגיית מנהל ולהריץ קוד
כשאתם מנסים להתחבר לשרת קבצים מסוג HFS, השרת מחזיר לכם עוגייה. בתוך העוגייה, בטקסט שכל אחד יכול לפענח בלי מפתח ובלי סיסמה, יושב מספר "אקראי" שהשרת הגריל לעצמו רגע קודם. המספר נראה חסר חשיבות לגמרי. בפועל הוא החוליה הראשונה בשרשרת שמאפשרת לתוקף להרכיב לעצמו עוגיית מנהל ולהריץ קוד על השרת, בלי שם משתמש ובלי סיסמה.
את השרשרת הזאת פרסם ב-30 בספטמבר זאק הנלי (Zach Hanley) מחברת Horizon3. היא קיבלה את המזהה CVE-2026-61500 וציון 9.3 בסולם CVSS 4.0, והיא נוגעת לגרסאות 3.0.0 עד 3.2.0 של התוכנה. יומיים אחרי הפרסום דווח על ניסיונות מול שרתים אמיתיים. בהמשך נעבור על המספר שהשרת מוסר מיוזמתו, על הסיבה שהוא מספיק כדי לשחזר את מפתח החתימה, ועל השאלה שמעניינת את החוקרים עוד יותר מהחולשה עצמה: מי מצא אותה.
מה זה HFS, ולמה הוא מטרה חוזרת
HFS, קיצור של HTTP File Server, היא תוכנה חופשית שהופכת מחשב רגיל לשרת שיתוף קבצים דרך הדפדפן. מתקינים, בוחרים תיקיות, ומקבלים כתובת שאפשר לשלוח למישהו אחר. היא פופולרית אצל מי שרוצה לשתף קבצים גדולים בלי להעלות אותם לשירות ענן, וזה אומר שלא מעט מהמופעים שלה פתוחים לאינטרנט מהבית או מרשת קטנה, בלי צוות אבטחה שמנטר אותם.
לתוכנה יש גם עבר. שתי חולשות קודמות בה הגיעו לרשימת החולשות המנוצלות של CISA: אחת נכנסה במרץ 2022, והשנייה, הזרקה למנוע התבניות שהובילה להרצת קוד, נכנסה ביולי 2024. אותה חולשה מ-2024 נגעה לגרסאות 2.X, שנכתבו ב-Delphi. גרסאות 3.X הן שכתוב מלא ב-TypeScript שרץ על Node.js, והשכתוב הוא בדיוק מה שמשך את תשומת הלב של Horizon3: קוד חדש, מערכת אקולוגית חדשה, ומשפחות באגים אחרות לגמרי מאלה שהיו בגרסה הקודמת.
המספר שהשרת מסר בעצמו
מתחת למנגנון ההתחברות של גרסאות 3.X יושבת Koa, אחת מתשתיות הווב הנפוצות ל-Node.js. Koa משתמשת ברכיב בשם keygrip כדי לחתום כל עוגיית session במפתח סודי, וכך השרת יודע אחר כך שהעוגייה שחזרה אליו היא זו שהוא עצמו הנפיק ולא משהו שמישהו המציא. את המפתח הזה אפשר להגדיר מבחוץ, במשתנה סביבה בשם COOKIE_SIGN_KEYS. הנלי מצא שהמשתנה הזה לא מוגדר באף מקום בקוד, ולכן בכל התקנה בפועל HFS מגריל את המפתח בעצמו בעלייה, בפונקציה שקוראת ל-Math.random. המפתח הוא שלושה מספרים רצופים מהגנרטור, שנצרכים בדיוק ברגע שהתהליך עולה.
וכאן נכנס לתמונה הצד השני. בשלב הראשון של תהליך ההתחברות, HFS מגריל מספר נוסף באותה קריאה ל-Math.random ושומר אותו בשדה של ה-session בשם loggingIn. עוגיות ה-session של Koa הן JSON מקודד ב-base64, והן חתומות אבל לא מוצפנות. כלומר הלקוח מקבל את העוגייה, מפרסר אותה, וקורא בתוכה את המספר המדויק, 52 ביט של מספר עשרוני, כפי שיצא מהגנרטור. כדי להגיע לשלב הזה צריך רק שם משתמש קיים שמורשה להתחבר. סיסמה לא נדרשת.
אין פה ניצול של באג בזיכרון ואין פה עקיפה של הצפנה. יש פה שרת שמוסר מספר.
אז למה Math.random לא מספיק?
מחשב לא יודע לייצר אקראיות אמיתית. מה שהוא עושה הוא לשמור מצב פנימי קטן, ובכל קריאה להפעיל עליו פעולה חשבונית קבועה שמוציאה את המספר הבא ומעדכנת את המצב. מכאן נובעות שתי תכונות: מי שיודע מה המצב הפנימי יודע את כל המספרים הבאים, ואם הפעולה הפיכה, מי שראה מספיק מספרים רצופים יכול לשחזר את המצב ואז להריץ אותו אחורה אל מספרים שכבר יצאו. במנוע V8, זה שמריץ את Node.js, הפעולה הזאת היא אלגוריתם בשם xorshift128+. הוא מחזיק שני מספרים שלמים של 64 ביט כמצב, והוא הפיך לחלוטין.
זאת הסיבה ששלושת המספרים של מפתח החתימה חשובים: הם יצאו מאותו זרם שממנו יצא גם המספר שהשרת מוסר בהתחברות. לפי Horizon3, שלושה עד חמישה מספרים רצופים מספיקים כדי לשחזר את המצב הפנימי, בעזרת Z3, מנוע הסקה חופשי שפיתחה מיקרוסופט מסוג SMT solver. כלי כזה מקבל אוסף אילוצים ומחזיר את הערכים שמקיימים אותם, בערך כמו לבקש ממנו למצוא את כל ה-x וה-y שעבורם 4x + 5y = 20. במקרה הזה האילוצים הם המספרים שנצפו, והערך המבוקש הוא המצב שהוליד אותם.
V8 לא מייצר את המספרים אחד-אחד אלא ממלא מחסנית של 64 ערכים וצורך אותה מהסוף להתחלה, מה שמסבך את השחזור. הנלי כותב שזה מסבך ולא מונע: כל מילוי מחדש הוא 64 צעדים קדימה שאפשר לחשב בדיוק, ולכן אפשר לצעוד את המצב אחורה עד לרגע שבו התהליך עלה. ויש גם דרך לבדוק אם ניחוש נכון בלי לגעת בשרת בכלל. לתוקף יש עוגייה חוקית שהשרת הנפיק לו, ולצידה החתימה שלה. כל מפתח מועמד נבדק מול הצמד הזה במחשב של התוקף, בחינם ובלי שאף לוג בצד השרת יראה משהו.
מהעוגייה המזויפת לקוד שרץ על השרת
השרשרת המלאה, כפי ש-Horizon3 מתארת אותה, היא שבעה שלבים. קודם מאמתים שחשבון המנהל המובנה קיים, דרך חולשה שנייה של חשיפת שמות משתמשים שנמצאה באותו מחקר ודווחה בנפרד. אחר כך דוגמים את ה-endpoint שמדליף את המספרים 12 פעמים, מזינים את התוצאות ל-Z3 כאילוצים, משחזרים את המצב של xorshift128+, צועדים אחורה עד למפתח שנוצר בעלייה, ומרכיבים בעזרתו עוגיית session חתומה כהלכה בשם המנהל.
העוגייה המזויפת מכילה שם משתמש admin, חתימת זמן נוכחית, והשדה שמתיר לאותו session לעבור בין כתובות IP. למה זה עובד? כי HFS מכבד את שם המשתמש שרשום ב-session, אלא אם חתימת הזמן שלו קודמת לרשימת הביטולים, שבברירת מחדל ריקה. את הקישור לכתובת ה-IP התוקף מנטרל פשוט בכך שהוא מסמן את השדה הרלוונטי בעצמו, כי הוא זה שחותם. ההרשאה לממשק הניהול נגזרת אצל HFS מהחשבון שרשום בבקשה ולא מבדיקה נוספת, והגדרת הרשת שמגבילה מי בכלל יכול להגיע לממשק הניהול ריקה כברירת מחדל.
מכאן להרצת קוד הדרך קצרה, ולא דורשת חולשה שלישית. לפי Horizon3, ממשק הניהול של HFS מאפשר למנהל להגדיר endpoint-ים משלו שמריצים קוד JavaScript חופשי. זאת תכונה מתוכננת של התוכנה. מי שמחזיק עוגיית מנהל מחזיק אותה.
מי מצא את זה, ולמה זה מטריד את החוקרים
את השרשרת לא מצא אדם שקרא את הקוד. היא נמצאה על ידי Mythos, מודל של Anthropic, בתוך צינור מחקר החולשות של Horizon3. החברה כותבת שהצטרפה ביולי 2026 ל-Project Glasswing של Anthropic, ושה-harness שלה מריץ במקביל סוכנים ייעודיים לכל משפחת חולשות. הסוכן שמחפש כשלים קריפטוגרפיים הוא זה שהחזיר את השרשרת, וסוכן שני, שתפקידו לאמת, חזר לקוד ואישר שמדובר בממצא אמיתי.
מה שמטריד את החוקרים הוא לא החולשה אלא מה שאפשר אותה. הם כותבים שנתקלו בשימוש קריפטוגרפי לא תקין פעמים רבות בעבר, וזנחו את הכיוון משתי סיבות: חוסר ברקע מתמטי עמוק, והעובדה שפיתוח exploit לחולשות כאלה לוקח יותר זמן ממה שהוא שווה. "אנחנו לא זוכרים שראינו אי פעם SMT solver משמש לתקיפת כשל קריפטוגרפי כזה ביישום אמיתי, ולהפקת השפעה כמו עקיפת אימות", הם כותבים. ההערכה שלהם היא שסוגי הבאגים שתוקפים ימצאו כדאי להשמיש בקנה מידה גדול ישתנו, ושמשפחות מסובכות ופחות אמינות, שעד היום לא היו שוות את ההשקעה, ייכנסו לתמונה.
לוח הזמנים כאן שווה תשומת לב בפני עצמו. התיקון, גרסה 3.2.1, יצא ב-13 ביולי 2026, ובהודעת השחרור נכתב רק ש"נמצאו מספר חולשות אבטחה בכל הגרסאות הקודמות, שעלולות לאפשר לתוקף להשיג גישת מנהל ל-HFS", עם קרדיט להנלי. הפרטים התפרסמו שניים וחצי חודשים אחר כך. לפי דיווח של SecurityWeek, חברת VulnCheck זיהתה ב-2 באוקטובר סריקות בהיקף קטן מכתובת של China Telecom, שפגעו במלכודות שלה ביפן ובארצות הברית. VulnCheck מתארת את זה כאיסוף מידע ולא כתקיפה מלאה, ואף גורם לא דיווח עד כה על שרת שנפרץ כך בפועל. ברשימת CISA, במהדורה מ-4 באוקטובר, החולשה הזאת עדיין לא מופיעה.
מה זה אומר בפועל
אם אתם מריצים HFS בגרסה 3.0.0 עד 3.2.0, ובמיוחד אם הוא חשוף לאינטרנט, הגרסה 3.2.1 ומעלה היא התיקון, והיא זמינה מאז יולי. החלפת סיסמת המנהל לא תעזור כאן, כי התוקף לא מנחש סיסמה אלא חותם עוגייה; מה שכן עוזר, אם אתם מנהלים את ההתקנה בעצמכם, הוא להגדיר את COOKIE_SIGN_KEYS לערך חזק משלכם, כך שמפתח החתימה לא ייוצר מ-Math.random.
בניטור, אל תחפשו ניסיונות התחברות שנכשלו. השרשרת הזאת לא מנחשת סיסמאות, ולכן היא לא מייצרת את התמונה שאתם רגילים לחפש. מה שכן אמור להדליק נורה הוא עשרות בקשות התחברות לאותו שם משתמש מאותה כתובת בפרק זמן קצר, ו-endpoint-ים בממשק הניהול שאף אחד אצלכם לא הגדיר.
תגובות