IPv6 Frag Escape: איך באג של בייט אחד בקרנל מפיל שרת שלם
הקדמה
אני מניח שחלקכם כבר נתקלתם בכותרת "פרצה חדשה בקרנל לינוקס" ופשוט גיללתם הלאה, ובצדק, כי הן מתפרסמות בקצב מסחרר. אבל הפעם כדאי לעצור לרגע.
הפרצה שזכתה לכינוי IPv6 Frag Escape אינה עוד privilege escalation שדורשת מרוץ תזמון מסובך וסבלנות של שעות. מדובר בבאג דטרמיניסטי, כלומר אחוז הצלחה גבוה מאוד ללא תלות בתזמון, בתוך קוד ה-IPv6 של הקרנל, וספציפית בפונקציה ()__ip6_append_data. הבאג מאפשר לתהליך לא מורשה בתוך קונטיינר רשתי מבודד להשיג shell root אינטראקטיבי במרחב ה-namespace הראשי של המארח. כלומר, לא root בתוך הקונטיינר, אלא root על השרת עצמו ועל כל מה שרץ בו.
הפרצה נסגרה ב-upstream בקומיט 38becddc, קוד PoC ציבורי עלה ל-GitHub בשם ipv6_frag_escape, ו-CVE רשמי עדיין לא הוקצה נכון לזמן כתיבת שורות אלו. הבאג עצמו קיים בקוד ה-IPv6 של הקרנל ורלוונטי לכל הפצת לינוקס שעדיין לא קיבלה את התיקון.
אז מה בעצם התגלה?
כדי להבין את הבאג צריך רקע קצר על האופן שבו לינוקס מנהלת חבילות רשת (packets) בזיכרון. כשהקרנל בונה חבילת IPv6, הוא מאחסן אותה במבנה שנקרא socket buffer (skb): מבנה נתונים שמכיל header בזיכרון רצוף, ואחריו מערך של "פרגמנטים" (skb_shared_info.frags[]) שיכולים להצביע על דפי זיכרון שונים.
הבאג בפונקציה ()__ip6_append_data הוא overflow של בייט יחיד בתוך ה-slab. בנסיבות מסוימות ניתן לכתוב בייט אחד מחוץ לגבולות ה-skb head, ואותו בייט הוא בדיוק השדה nr_frags שבתוך skb_shared_info, השדה שמגדיר כמה פרגמנטים יש ל-skb.
למה זה מסוכן?
כשה-skb משתחרר, הפונקציה ()skb_release_data עוברת על frags[0..nr_frags) ומורידה reference count לכל דף שם. אבל המערך אינו מאותחל, כלומר הוא עשוי להכיל מצביעים ששרדו מ-allocation קודמת.
אנלוגיה: דמיינו ספרן שמשחרר ספרים לפי רשימה. מישהו שינה בשקט את המספר שבסוף הרשימה, ועכשיו הספרן "משחרר" ספרים שכלל לא היו אמורים להיות ברשימה, כולל ספרים שמישהו אחר עדיין מחזיק בידיו. כתוצאה מכך אותם ספרים הופכים ל"חופשיים" בספרייה בזמן שהקורא עדיין קורא בהם, ומישהו אחר יכול לשבת ולכתוב עליהם.
שרשרת הניצול בפועל: מ-slab overflow ועד shell בשרת המארח
קוד ה-PoC הציבורי מדגים שרשרת ניצול בת שבעה שלבים, שבסופה מתקבל shell root אינטראקטיבי על השרת המארח, מבלי שאי פעם יוצאים רשמית מהקונטיינר:
- In-slab overflow שהופך ל-Use-After-Free: הגדלת nr_frags ב-1 גורמת לקרנל להוריד reference על דף שהתוקף עדיין מחזיק (pipe buffer page) ולשחרר אותו. ה-overflow הופך ל-page UAF.
- Page UAF שהופך ל-Dirty Pagetable: הדף המשוחרר "נלכד" מחדש כ-last-level page table באמצעות fault של anonymous mapping. כעת אותו דף פיזי הוא גם page table חי וגם הדף שה-pipe עדיין קורא וכותב עליו. כתיבת שמונה בייטים ל-pipe מתורגמת להתקנת PTE מזויף.
- שבירת KASLR: דרך ה-physical read שמתאפשר, התוקף סורק את ה-SMP trampoline page table שלעולם אינו מוזז על ידי KASLR. משם מוצא התוקף את הכתובת הפיזית של הקרנל ומשלים מפת "virtual ל-physical" מלאה.
- arbitrary read/write ללא הגבלה: PTE מזויף שמצביע על ה-page table עצמו הופך את ה-PTEs לזיכרון רגיל, ומעניק גישת קריאה וכתיבה לכל הזיכרון הפיזי של השרת, ללא מגבלות.
- השגת root credentials: ה-offsets של struct members נקראים בזמן ריצה מ-/sys/kernel/btf/vmlinux (ולא מקודדים קשיח בתוכנה), ה-task_struct מאותר דרך vmemmap, ומזהי ה-credentials מאופסים, כולל כל ה-capability sets.
- השתקת SELinux בשקט: ה-prologue של ()avc_denied מוחלף ב-xor eax,eax ; ret. כל בדיקת SELinux מחזירה "מורשה", בלי שהסטטוס של getenforce משתנה. ה-audit log נשאר נקי לחלוטין.
- בריחה דרך core_pattern: המשתנה הגלובלי core_pattern מוחלף ב-handler עם קידומת | שמצביע על הבינארי של התוקף. ה-handler מורץ על ידי הקרנל כ-usermodehelper, כלומר root במרחב ה-namespace הראשי ועל ה-root filesystem של המארח. הבריחה מהקונטיינר הושלמה.
מה שמפחיד באמת: שום קובץ על הדיסק לא השתנה. כל השינויים חיים אך ורק בזיכרון. לכלי File Integrity Monitoring מסורתיים אין שום דרך לזהות זאת. אתחול מנקה את הזיכרון, אבל עד אז התוקף כבר מזמן root.
מי בסיכון?
שורש הבאג נמצא בקוד ה-IPv6 של הקרנל, קוד שמשותף לכל הפצות לינוקס. הפרצה רלוונטית לכל שרת שמריץ קרנל שעדיין לא קיבל את התיקון בקומיט 38becddc. קוד ה-PoC הציבורי תוכנן ונבדק על גרסאות kernel 6.12.x, אך הבאג עצמו קיים גם בגרסאות ישנות יותר שספריות ה-IPv6 שלהן לא עודכנו.
הסביבות החשופות ביותר הן בדיוק אלו שבהן תהליכים לא מורשים רצים לצד תהליכים רגישים:
- Shared Hosting ו-VPS: כל לקוח שמקבל shell, בין אם מורשה, נגוע בווירוס או דרך web shell, הופך לאיום פוטנציאלי על השרת כולו.
- Kubernetes, Docker וסביבות קונטיינרים: קונטיינרים חולקים את ה-kernel של המארח. ניצול מתוך קונטיינר משמעו root על המארח.
- CI/CD runners: סביבות build שמריצות קוד מ-pull requests של צד שלישי חשופות מעצם טבען.
- שרתי multi-tenant: כל ארגון שמאכסן לקוחות מרובים על אותה מכונה פיזית.
תנאי הניצול: התוקף זקוק לגישה כתהליך לא מורשה עם גישה ל-unprivileged user namespaces, יכולת שמופעלת כברירת מחדל ב-Debian, ב-Fedora וברוב הסביבות המודרניות. ב-Ubuntu 24.04 ומעלה, AppArmor מגביל יצירת namespaces ובכך מעלה את הרף, אך אין זו חסימה מוחלטת. בסביבת Shared Hosting הכניסה יכולה להגיע בקלות דרך פלאגין WordPress פרוץ, web shell, או קוד PHP לא מאובטח.
⚠️ מה לעשות עכשיו: פתרונות עוקפים זמניים
הפתרון האמיתי הוא עדכון הקרנל. אבל אם לא ניתן לאתחל מיידית, יש כמה צעדים שמצמצמים את משטח התקיפה.
1. הגבלת user namespaces לא מורשים:
sysctl -w kernel.unprivileged_userns_clone=0
# לקביעה קבועה:
echo "kernel.unprivileged_userns_clone=0" >> /etc/sysctl.d/99-ipv6fragesc.conf
זה חוסם את המסלול הנפוץ שבו תהליך לא מורשה יוצר namespace חדש ומשיג הרשאות רשת.
2. מניעת טעינת מודולי הקרנל הרלוונטיים (אם אינם נחוצים):
echo "install esp4 /bin/false" >> /etc/modprobe.d/ipv6fragesc.conf
echo "install esp6 /bin/false" >> /etc/modprobe.d/ipv6fragesc.conf
echo "install rxrpc /bin/false" >> /etc/modprobe.d/ipv6fragesc.conf
⚠️ שימו לב: חסימת esp4 ו-esp6 תשבור רשתות VPN מבוססות IPsec (כגון StrongSwan). חסימת rxrpc תשבור חיבורי AFS. ודאו את הצורך לפני יישום בסביבת Production.
3. בסביבות קונטיינרים, הקשחה נוספת:
- הגבילו שימוש ב-–privileged וב-–net=host
- הפעילו seccomp profiles שמגבילים יצירת AF_RXRPC sockets
- שמרו על SELinux או AppArmor במצב Enforcing
שורה תחתונה: אלו בקרות זמניות בלבד, ואינן תחליף לתיקון.
התיקון ועדכוני הקרנל
התיקון הוכנס ל-upstream בקומיט 38becddc ב-kernel mainline, ועבר backport לענפי ה-stable וה-LTS. ה-patch זמין בכל ההפצות המרכזיות דרך עדכוני הקרנל הרגילים. עדכנו את הקרנל בכלי הסטנדרטי של ההפצה שלכם (apt, dnf או zypper) ואתחלו את השרת כדי שהתיקון ייכנס לתוקף.
נקודה עקרונית: זו כבר הפגיעות החמישית בנתיב הקוד של ה-networking stack בחודשים האחרונים, אחרי Copy Fail, Dirty Frag, Fragnesia ו-ptrace exit-race. המגמה ברורה: ניהול מהיר ושיטתי של עדכוני קרנל אינו מותרות. זה ההבדל בין שרת מוגן לשרת פרוץ.
וכאן בדיוק נכנסת SPD
הבעיה הקלאסית עם פרצות קרנל היא הפער שבין רגע פרסום ה-PoC לבין חלון התחזוקה הבא שבו אפשר לאתחל את השרת. בעידן ה-AI, סריקות עוינות מתחילות לעיתים תוך דקות מרגע פרסום הפרצה, הרבה לפני שהספקתם לתזמן Restart.
SPD Kernel Shield מאפשר להחיל Kernel Patches קריטיים בזמן אמת, בזיכרון, ללא אתחול וללא Downtime, ולסגור את חלון החשיפה מיד עם פרסום הפרצה, בדיוק בתרחיש כמו IPv6 Frag Escape.
הפתרון מציע:
- אפס Downtime: תיקוני אבטחה מותקנים תוך כדי עבודה, ללא ניתוק שירותים.
- סגירת חלון החשיפה: ה-patch מותקן מיידית עם פרסום הפרצה, ולא בחלון התחזוקה הבא.
- תמיכה רחבה: Ubuntu, RHEL/CentOS/Alma/Rocky/Oracle, Debian, Amazon Linux ועוד.
- שילוב עם סורקים: Nessus, Qualys ו-Rapid7 יציגו את מצב האבטחה האמיתי ללא False Positives.
- FIPS-140 ו-UEFI Secure Boot: עמידה מלאה ברגולציות ההצפנה.
עדיין מבצעים Restart בשביל Kernel Patches? בואו נדבר.
מקורות:
GitHub – sgkdev/ipv6_frag_escape (PoC): קישור
CloudLinux Blog – IPv6 Frag Escape Mitigation and Kernel Update: קישור
Linux Kernel upstream fix – commit 38becddc: קישור
Berkeley Information Security Office – Dirty Frag Advisory: קישור


