מכולה חדשה עולה על אחד השרתים של Cloudflare, כותבת 4 קילובייט לדיסק שלה, ומיד קוראת את אותו אזור בחזרה. מה שחוזר הוא בלוק שלם של 64 קילובייט, ו-60 מהם מכילים משהו שהמכולה הזאת מעולם לא כתבה: מבני ספריות, דפים של מסד נתונים, ולפעמים קובץ SQLite שלם ותקין. הם שייכים ללקוח אחר, שהריץ מכולה על אותו שרת לפני כן וסיים.

את ההתנהגות הזאת דיווח אורן יומטוב, חוקר אבטחה ראשי בחברת Accomplish, ל-Cloudflare ב-4 בספטמבר דרך תוכנית ה-bug bounty. ב-24 בספטמבר פרסמו שתי החברות ניתוח משותף של החולשה, של השורש שלה ושל התיקון, ובמקביל פרסמה Accomplish הודעה קצרה משלה. Cloudflare אומרת שהתיקון הושלם ושלא נדרשת שום פעולה מצד הלקוחות. בהמשך נראה למה הדיסק בכלל החזיק מידע ישן, למה קריאה פשוטה לא הספיקה כדי להגיע אליו, ואיך החוקרים הוכיחו שהבתים שהם ראו אינם שלהם.

מה רץ שם, ועל איזה דיסק

Cloudflare Containers הוא השירות שמריץ מכולות על התשתית של Cloudflare, לצד ה-Workers. כל מכולה חיה בתוך מכונה וירטואלית ייעודית משלה, מבוססת Firecracker, ומקבלת דיסק שורש שניתן לכתיבה, שמבפנים נראה כמו התקן רגיל בשם ‎/dev/vdc. הלקוח לא בוחר על איזה שרת פיזי המכולה שלו תרוץ. Cloudflare משבצת אותה לשרת פנוי, ועל אותו שרת רצות באותו זמן מכולות של לקוחות אחרים לגמרי. זה המודל הרגיל של תשתית מרובת דיירים, והוא עומד על הנחה אחת: מה שדייר אחד כותב לדיסק שלו, דייר אחר לא יכול לקרוא.

על אותה תשתית בנוי גם Cloudflare Sandboxes, השירות שנועד להריץ קוד שאין לכם סיבה לסמוך עליו, ובכלל זה קוד שסוכני AI מייצרים ומריצים בעצמם. Accomplish מוסיפה שגם Browser Run משתמש באותו מימוש דיסק והושפע. זה מה שהופך את הסיפור הזה למשהו יותר מבאג אחסון.

כדי להבין מה השתבש צריך להבין איך המכולה מקבלת את הדיסק הזה. Cloudflare לא מקצה לכל מכולה שטח פיזי בגודל שהיא מצהירה עליו. במקום זה יושב על השרת מאגר אחסון פיזי אחד גדול, והמכולה מקבלת ממנו נתחים רק כשהיא באמת כותבת למקום שעוד לא נכתב אליו. כשהמכולה נמחקת, הנתחים שלה חוזרים למאגר המשותף וממתינים למכולה הבאה, שיכולה להיות של לקוח אחר. המנגנון הזה הוא רכיב סטנדרטי בלינוקס בשם device mapper thin provisioning, או בקיצור dm-thin, והנתח הבסיסי במאגרים המדוברים הוא 64 קילובייט.

ברירת המחדל של dm-thin היא לאפס בלוק לפני שהוא נמסר לבעלים החדש. בהגדרות המאגר שבשימוש הופעלה האפשרות skip_block_zeroing, שמדלגת בדיוק על השלב הזה. ההיגיון מאחוריה הוא ביצועים: איפוס של כל בלוק חדש עולה זמן ופעולות כתיבה, ואם ממילא כל בלוק נכתב במלואו, אין מה לאפס.

אבל לא כל כתיבה היא כתיבה מלאה. כתיבה שממלאת בלוק שלם דורסת את כל מה שהיה בו, וזה בסדר. כתיבה קטנה יותר מחליפה רק את החלק שלה. "השארית עלולה לשמור על נתונים של הבעלים הקודם של הבלוק", כתבו ב-Cloudflare.

למה היה צריך לכתוב כדי לקרוא

הדבר המעניין כאן הוא שקריאה סתמית של הדיסק לא הייתה מגלה כלום. כשמכולה חדשה קוראת אזור שעוד לא הוקצה לו בלוק פיזי, dm-thin פשוט מחזיר אפסים ולא נוגע במאגר. כדי להגיע לזיכרון הישן היה צריך קודם לגרום להקצאה.

וזה בדיוק מה שהחוקרים עשו. הם איתרו על הדיסק אזורים בגודל 64 קילובייט, מיושרים לגבול הבלוק, שמתאימים לשטח פנוי במערכת הקבצים ext4 של המכולה שלהם, וכתבו לתוך כל אחד מהם בלוק אחד של 4 קילובייט. כל כתיבה כזאת אילצה את dm-thin למשוך בלוק פיזי של 64 קילובייט מהמאגר המשותף. ה-4 קילובייט החליפו את החלק שלהם, ו-60 הקילובייט שנותרו נשארו כפי שהם. קריאה גולמית של ‎/dev/vdc אחרי זה החזירה בתים שהמכולה החדשה מעולם לא כתבה.

כדי להפעיל את זה היה צריך חשבון Workers Paid, ולא הרבה יותר מזה. מה שכן, אי אפשר היה לכוון את הטכניקה: לא לקורבן מסוים, לא לשרת מסוים ולא למידע מסוים. מה שחוזר תלוי בשיבוץ ש-Cloudflare עשתה ובאילו בלוקים משוחררים dm-thin בחר למחזר. גם נוכחות של מידע שאריתי לא הייתה מובטחת. זה לא היה כלי מדויק.

איך ידעו שהמידע הוא של מישהו אחר

כאן נכנס לתמונה החלק החכם של המחקר. לקרוא בתים זרים זה דבר אחד, להוכיח שהם זרים זה דבר אחר, ובמיוחד כשלא רוצים לפתוח ולקרוא תוכן של לקוחות. הפתרון הגיע ממערכת הקבצים עצמה: כש-ext4 עובדת עם התכונה metadata_csum, כל בלוק ספרייה נושא סכום ביקורת שמחושב מערכים שקשורים למערכת הקבצים ול-inode שלו. אפשר, לכן, לבדוק למי בלוק ספרייה שייך בלי להסתכל בכלל במה שכתוב בתוכו.

המספרים שיצאו מזה חדים. בשישה שיבוצים בסביבת הייצור נבדקו 5,614 בלוקי ספרייה, ואף אחד מהם לא שויך למערכת הקבצים של החוקרים עצמם. מתוכם זוהו 2,700 inode-ים זרים ושונים. כדי לוודא שהשיטה עצמה עובדת, החוקרים הריצו אותה על 162 בלוקים שהם יצרו ומחקו בכוונה במערכת הקבצים שלהם, והשיטה שייכה את כולם נכון.

בתמונה הרחבה יותר, מידע שאריתי נמצא ב-18 מתוך 24 שיבוצים וב-20 מתוך 22 השרתים שמתחתם, בארבע יבשות. סוגי הבלוקים שחזרו כללו מבני ספריות, דפים של מסד נתונים ומסדי SQLite שלמים מבחינה מבנית. Accomplish מוסיפה לרשימה גם פרופילי Chromium, קבצי ‎.env וקבצים שמחזיקים פרטי אימות. לפי שתי החברות, הסקריפטים של החוקרים החזירו רק ספירות ובדיקות פורמט ולא תוכן קבצים, החומר שהוגש ל-Cloudflare לא כלל שמות, מזהים או ערכים של צד שלישי, והמידע שנאסף נמחק באופן מאובטח אחרי ההגשה.

התיקון, ולמה יום אחד לא הספיק

הדיווח נכנס ב-4 בספטמבר ב-15:26 UTC. שלוש שעות אחר כך Cloudflare פתחה אירוע אבטחה ואישרה את ההגדרה שגרמה לבעיה, ועד 22:03 באותו לילה כבר מוזגו התיקון וההתאמות למאגרים חדשים וקיימים. ההפצה החלה ב-23:15 והושלמה ב-7 בספטמבר. התיקון עצמו פשוט: להוריד את skip_block_zeroing מהגדרות המאגר ולהחזיר את dm-thin להתנהגות ברירת המחדל, כלומר לנקות כל בלוק חדש לפני שהוא נחשף למכולה.

רק שזה טיפל בהקצאות מכאן והלאה, ולא בבלוקים שכבר היו ממופים. מיפויים כאלה ישבו בדיסקים של מכולות שרצות באותו רגע, וגם במטמון שכל שרת מחזיק של snapshot-ים מוכנים לשכבות של image. מכולה חדשה יכלה לרשת מיפוי משכבה במטמון בלי להקצות אותו מחדש, ולקרוא דרך ‎/dev/vdc בתים שאריתיים באזורים שהיא לא נגעה בהם. לכן Cloudflare גם הוציאה משירות את כל דיסקי המכולות שרצו, הסירה את ה-snapshot-ים שנוצרו לפני התיקון, רוקנה שרתים בשעות שפל, הפעילה מחדש את המכונות הווירטואליות וניקתה את מטמון ה-image בכל שרת. הניקוי הושלם ב-19 בספטמבר. החוקרים דיווחו כבר ב-14 בספטמבר שהוכחת ההיתכנות שלהם הפסיקה לעבוד.

לגבי השאלה אם מישהו אחר השתמש בזה, Cloudflare בנתה חתימות זיהוי מהיחס האופייני בין כתיבה קטנה לקריאה גדולה והריצה אותן על טלמטריית קלט-פלט היסטורית של הדיסקים. היא מצאה רק פעילות של החוקרים ושל מהנדסים שלה עצמה. ראוי לשים לב לניסוח: הקביעה נשענת על הטלמטריה ששמורה אצלה, לא על היעדר אפשרות עקרונית.

מה זה מספר על השכבה הזאת

Accomplish מציינת שזו הבריחה השישית מ-sandbox שהצוות שלה מפרסם מאז יולי 2026, אחרי SharedRoot ב-Claude Cowork, Beltdown ב-Claude Code, Beltdown2 ב-Cursor CLI, ה-VMM של Docker וה-sandbox של OpenAI Codex. הכותרת שהם נתנו לפוסט הנוכחי מסכמת את העמדה: בידוד יכול להישבר בהרבה דרכים.

וזה כנראה הלקח המעניין. הגבול שנשבר כאן לא היה המעבד ולא ה-hypervisor, שני המקומות שמחפשים בהם בריחות מ-sandbox. הוא היה דגל הגדרה אחד במאגר אחסון, שנבחר משיקולי ביצועים ועבד בדיוק כפי שתועד. ככל ששכבת ה-sandbox הופכת למקום שבו רץ קוד שסוכני AI מייצרים, כך היא נבדקת בקצב ובזוויות שלא תכננו עבורם.

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

מי שמריץ Containers או Sandboxes ב-Cloudflare לא צריך לעשות דבר. התיקון בוצע בצד השרת, בלי שינויי תצורה אצל הלקוח, ו-Cloudflare מדווחת שלא נמצאו ראיות לניצול זדוני. מה שכן שווה לקחת מכאן הוא שאלה אחרת: מה בעצם יושב על דיסק המכולה שלכם. קבצי ‎.env, מסדי SQLite ופרופילי דפדפן שנוצרים בזמן ריצה הם בדיוק החומר שחזר בבדיקות, וסוד ששמור במנגנון הסודות של הפלטפורמה ולא נכתב לדיסק לא היה חשוף כאן מלכתחילה. ואם אתם מתחזקים מאגרי dm-thin משלכם, על שרתים שמשרתים יותר מלקוח אחד, שווה לבדוק עכשיו אם skip_block_zeroing מופעל אצלכם.