תהליך רגיל בתוך container, בלי הרשאות root ועם פרופיל seccomp סטנדרטי, פותח socket מקומי ומעביר דרכו כמה file descriptor-ים. שום שלב בפעולה הזאת לא נראה חשוד, כי זאת בדיוק התקשורת הבין-תהליכית שמפעילה את systemd, את Docker עצמו וכל חיבור למסד נתונים מקומי. בסוף הרצף הקצר הזה התהליך כבר מריץ קוד ברמת הקרנל של המכונה המארחת, ומשם הוא רואה את שאר ה-container-ים שיושבים על אותו שרת.

את זה הדגימו חוקרי depthfirst בדוח שפורסם ב-22 בספטמבר. Zhenpeng (Leo) Lin, חוקר אבטחה בחברה, פרסם ניתוח מלא של CVE-2026-80521, חולשה במנגנון איסוף הזבל של תת-מערכת AF_UNIX בקרנל לינוקס, יחד עם קוד בריחה עובד. מדובר במצב שבו הקרנל ממשיך לגשת למבנה נתונים בזיכרון אחרי שכבר שחרר אותו, ומי שמצליח לשתול משהו משלו באותו שטח זיכרון בינתיים קובע על מה הקרנל יעבוד. לסוג הבאג הזה קוראים use-after-free. הקרנל עצמו קיבל תיקון ב-6 באוגוסט. אובונטו, נכון להיום, עדיין לא.

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

למה container נחשב עד עכשיו גבול אבטחה סביר

כש-Docker או Kubernetes מריצים workload, הם עוטפים אותו בכמה שכבות הגבלה. namespaces מסוג pid, net ו-mnt דואגים שהתהליך לא יראה תהליכים, ממשקי רשת ומערכות קבצים שמחוץ לסביבה שלו. cgroups מגבילים כמה זיכרון ו-CPU הוא יכול לצרוך. פרופילי seccomp-bpf מסננים אילו קריאות מערכת מותר לו בכלל לבצע, וחוסמים את הממשקים האזוטריים יותר של הקרנל. זה עובד מספיק טוב כדי שארגונים ירוצו כך במשך שנים, עם workload-ים של צוותים שונים, ולפעמים של לקוחות שונים לגמרי, זה לצד זה על אותה מכונה פיזית.

מה שכל השכבות האלה לא עושות זה להפריד בין ה-container לקרנל. container אורז לעצמו ספריות, binaries וקוד אפליקציה, אבל הוא לא מביא איתו מערכת הפעלה משלו. הוא מדבר עם אותו קרנל בדיוק שמשרת את המארח ואת כל שאר ה-container-ים על אותו node, דרך קריאות מערכת רגילות לגמרי. חולשה בתת-מערכת של הקרנל שנגישה מתוך ה-container עוקפת בבת אחת את namespaces, את cgroups ואת seccomp, פשוט מפני ששלושתם חיים בצד הלא נכון של החולשה. AF_UNIX היא דוגמה טובה במיוחד לעניין הזה: אף פרופיל container סביר לא חוסם אותה, כי בלעדיה חצי מהמערכת מפסיקה לעבוד.

מה בדיוק אוסף הזבל של AF_UNIX אמור לנקות

סוקטים מסוג AF_UNIX הם הדרך שבה שני תהליכים על אותה מכונה מחליפים ביניהם מידע, וגם, וזה החלק המעניין, מעבירים זה לזה file descriptor-ים בתוך הודעות מסוג SCM_RIGHTS. ברגע שמותר לשלוח הפניה לסוקט בתוך הודעה, אפשר ליצור מעגל: סוקט A מחזיק הפניה ל-B, ו-B מחזיק הפניה ל-A. שניהם נסגרו, אף תהליך לא משתמש בהם יותר, ובכל זאת כל אחד מהם מחזיק את השני בחיים. ספירת ההפניות הרגילה לא תשחרר אותם לעולם.

בשביל להתיר את הקשרים האלה הקרנל מריץ מנגנון שסורק את הגרף הזה ומזהה קבוצות שכבר אין אליהן כניסה מבחוץ. כל סוקט עם הפניות שנמצאות בתעופה מיוצג כצומת, struct unix_vertex, וכל הפניה כקשת, struct unix_edge. כדי לא לסרוק את הגרף כולו מאפס בכל מעבר, הקרנל מקבץ מראש אשכולות של צמתים שכולם מגיעים זה לזה (strongly connected components) ושומר אותם בטבעת מצביעים קבועה בשם scc_entry. הטבעת הזאת היא האופטימיזציה שחוסכת את הסריקה המלאה, והיא גם מה שנשבר כאן.

שני דברים שקורים בסדר הלא נכון

החור נפער משילוב של שתי התנהגויות שכל אחת מהן בפני עצמה לגמרי שפירה.

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

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

המעבר הבא הולך בדרך המהירה, זאת שסומכת על הטבעת השמורה במקום לסרוק את הגרף מחדש, ונכנס ישר לזיכרון שכבר שוחרר. משם והלאה זה כבר ניצול מוכר. depthfirst פרסמה קוד בריחה שלם מול אובונטו 26.04 העדכנית, ועוד אחד שמשתמש בחולשה אחרת, CVE-2026-52910 במנגנון ה-reuseport של BPF, לבריחה מקבילה מאובונטו 24.04.

מי מצא את החולשה, ובאיזה כלים

הסיפור של איך החולשה נמצאה הוא חלק ניכר מהטענה של הדוח. לפי ציר הזמן שפרסמה depthfirst, ב-24 ביולי הם ניצלו את המטרה בתחרות kernelCTF של גוגל, תוכנית שמשלמת על ניצולים עובדים מול גרסאות קרנל מוגדרות מראש, וב-5 באוגוסט קיבלו אישור זכייה ב-slot של lts-6.12.95. באותו יום הם דיווחו על הממצא ל[email protected], וקיבלו בחזרה תשובה שאותו באג בדיוק כבר דווח במקביל על ידי Kyle Zeng מ-OpenAI. התיקון עלה ל-upstream יום לאחר מכן. המחקר עצמו פורסם רק ב-22 בספטמבר, יותר מחודש וחצי אחר כך.

את החולשה עצמה, כותבים החוקרים, מצאו בעזרת dfs-large1, מודל בינה מלאכותית פנימי של החברה שאומן לאיתור חולשות, בשילוב עם harness משלהם. כאן כדאי לקרוא את הדוח בעין פקוחה: depthfirst מוכרת פלטפורמת אבטחה, והמסקנה הרחבה שהיא מסיקה מהממצא שלה נוחה לה. "העידן של אמון בקרנל המונוליטי נגמר", כותב Lin, "הגיע הזמן לבנות תשתית שמניחה שהקרנל ייפרץ מתי שהתוקפים ירצו". ההמלצה שנלווית לזה, להעביר workload-ים לא מהימנים ל-microVM-ים כמו Firecracker או Kata Containers, לגיטימית ולא ייחודית להם, אבל היא פרשנות של החברה לנתונים ולא ממצא שנמדד.

אז למה זה עדיין פתוח באובונטו?

דווקא הנתונים שהדוח מביא כדי להסביר את הפער הם החלק שהכי קל לאמת. לפי הספירה של depthfirst, בין 1 בינואר ל-16 בספטמבר 2026 פורסמו 5,976 רשומות CVE ייחודיות בקרנל לינוקס. ינואר סגר בערך 249, אוגוסט לבדו הגיע ל-1,650, כלומר יותר מ-27% מכלל השנה בחודש אחד. ומתוך 36 רשומות CVE שנחשפו פומבית ב-kernelCTF, 13 נגישות מממשקים רגילים ולא מיוחסים, אותם epoll, futex, טיימרים של POSIX וסוקטים של AF_UNIX שכל container נוגע בהם ממילא.

בקצב כזה, ה-backport של תיקונים להפצות הוא צוואר הבקבוק. מעקב האבטחה של Canonical מסמן את חבילת הקרנל של אובונטו 26.04 כ-Vulnerable, work in progress ואת זו של 24.04 כ-Vulnerable, בלי תאריך יעד לאף אחת מהן. רלוונטי עוד יותר לסביבות ענן: קרנלי הענן הייעודיים linux-aws, linux-azure ו-linux-gcp מסומנים לא מתוקנים בשתי הגרסאות. מי שמריץ 22.04 נמצא במצב שתלוי בקרנל ולא בהפצה, כי חבילת הקרנל הרגילה שם מסומנת Not affected, אבל linux-hwe-6.8, שרבים מריצים דווקא כדי לתמוך בחומרה חדשה יותר, מסומנת חשופה. בדיקה לפי מספר גרסת ההפצה בלבד תיתן כאן תשובה שגויה.

הרשומה הרשמית במאגר ה-CVE, שפורסמה ב-26 באוגוסט על ידי גוף הרישום של קרנל לינוקס, מגדירה את הטווחים המושפעים: מ-6.1.141 עד 6.2, מ-6.6.93 עד 6.7, וכל מה שמגרסה 6.10 ומעלה. ציון ה-CVSS שנקבע הוא 7.8, ו-Canonical מסווגת את הדחיפות כ-Medium. הציון הזה משקף ניצול מקומי ולא מרוחק, וזאת בדיוק הנקודה שבה container משנה את התמונה: בסביבה רב-דיירית, מקומי זה כל מי שמריץ אצלכם workload.

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

מי שמריץ container-ים על אובונטו 24.04 או 26.04, ובמיוחד על קרנלי הענן של AWS, Azure ו-GCP, לא יכול לעדכן לשום חבילה מתוקנת כרגע, אז שווה לעקוב אחרי מעקב האבטחה של Canonical ולבדוק את גרסת הקרנל שרצה בפועל, לא את גרסת ההפצה. קוד הניצול פומבי, והמרחק בין פרסום כזה לניסיון ראשון בשטח קצר. בסביבות שבהן רצים זה לצד זה workload-ים של לקוחות שונים או קוד שהגיע מבחוץ, זה הרגע לשאול אם הבידוד צריך להיות עמוק משכבת seccomp, כלומר קרנל נפרד לכל workload. ההנחה שתהליך לא מיוחס בתוך container הוא סיכון מוגבל כבר לא מחזיקה מים.