ב-1 באוקטובר שלח אריק דומאזה, מהמתחזקים הוותיקים של מחסנית הרשת של לינוקס, תיקון קצר לרשימת התפוצה של מפתחי הרשת בקרנל. התיקון מחליף שתי קריאות שחרור זיכרון רגילות (kfree) בגרסה הדחויה שלהן (kfree_rcu), בשני קבצים: net/ipv4/af_inet.c ו-net/ipv4/tcp_ipv4.c. ב-5 באוקטובר הוא נכנס לעץ הראשי, וב-15 באוקטובר קיבל מזהה רשמי, CVE-2026-98432, בציון 9.8.

הבאג שהתיקון סוגר ישב בקוד מאז גרסה 4.4 של הקרנל, כלומר מאז 2016. מי שדיווח עליו, לפי שורת ה-Reported-by בקומיט, הוא Xinyang Ge מ-Anthropic, ובפוסט מחקר שהחברה פרסמה ב-27 באוקטובר היא כותבת שמודל Claude כתב ניצול עובד: שרת לינוקס מרוחק, בלי פרטי אימות, בלי שאף משתמש לוחץ על משהו, ובסוף shell שרץ כ-root. בהמשך נראה מה בדיוק קורה בלחיצת היד של TCP שמאפשר את זה, מה הצליחו להוציא מזה ב-Anthropic, ולמה שלושה שבועות אחרי פרסום ה-CVE רוב הפצות הלינוקס הגדולות עדיין בלי תיקון.

מה נשמר בצד בזמן לחיצת היד

כשלקוח פותח חיבור TCP לשרת הוא שולח חבילת SYN, והשרת עונה ב-SYNACK. הקרנל לא בונה socket מלא כבר בשלב הזה, כי זה בדיוק מה שהיה הופך הצפת SYN למתקפה זולה. במקום זה הוא יוצר מבנה קטן וזמני שמחזיק את מה שצריך כדי לענות, ושנקרא request socket.

חבילת IPv4 יכולה לשאת שדות רשות בכותרת שלה, IP options. חלקם ותיקים מאוד: Record Route, שבו כל נתב בדרך כותב את הכתובת שלו, ו-Timestamp, שבו כל נתב מוסיף חותמת זמן. אם ה-SYN הגיע עם שדות כאלה, השרת חייב לזכור אותם, כי התשובה שלו צריכה לחזור באותו מסלול הפוך. הפונקציות ip_rcv_options ו-tcp_v4_save_options שומרות אותם בשדה בשם ireq_opt שתלוי ב-request socket.

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

מי משחרר, ומי עדיין קורא

כאן נכנס לתמונה שינוי מ-2016. מאז הקומיט שהכניס request sockets לטבלת ה-ehash, הם מטופלים בלי להחזיק את הנעילה של ה-socket המאזין. בפועל זה אומר ששני מעבדים יכולים לגעת באותו request socket באותו רגע.

התרחיש שמנצל את זה לא דורש שום דבר אקזוטי. הלקוח שולח SYN, ואז שולח אותו שוב. שידור חוזר של SYN הוא התנהגות רגילה לגמרי, כל פעם שה-SYNACK אובד בדרך. מעבד אחד משלים את לחיצת היד, יוצר את ה-socket החדש, מקבל בעלות על שדות הרשות, והחיבור נסגר, ואז inet_sock_destruct משחררת את inet_opt. מעבד שני, בינתיים, עדיין מטפל ב-SYN החוזר ובונה SYNACK דרך tcp_v4_send_synack, וקורא את אותם שדות בדיוק.

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

התוצאה היא קריאה מזיכרון משוחרר, use-after-free, בתוך ip_options_build. ומה שהופך אותה למסוכנת במיוחד הוא פרט קטן בקוד: הפונקציה קוראת את שדה האורך, opt->optlen, פעמיים. אם בין הקריאה הראשונה לשנייה הזיכרון שוחרר והוקצה מחדש למשהו אחר, האורך שהיא מקבלת בפעם השנייה כבר לא שלה.

הקוד הקורא לא עשה שום דבר שגוי. פשוט נשלף לו המבנה מתחת ליד.

לווריאנט שני של אותה בעיה יש תנאי מוקדם: הגירה של request sockets, שמופעלת עם net.ipv4.tcp_migrate_req=1 או דרך תוכנית BPF_SK_REUSEPORT_SELECT_OR_MIGRATE. שם הקרנל משכפל את ה-request socket, השכפול והמקור חולקים את אותו ireq_opt, וגם tcp_v4_reqsk_destructor משחררת אותו ב-kfree רגיל.

ולמה IPv6 יצא מזה נקי? התשובה נמצאת בשורה אחת של הבדל. הפונקציה המקבילה ב-IPv6, tcp_v6_syn_recv_sock, מעתיקה את שדות הרשות ל-socket החדש במקום להעביר מצביע. אין אובייקט משותף, ואין מה לשחרר מתחת לרגליו של קורא אחר.

רשומת ה-CVE של צוות הקרנל כן מציינת שווריאנט ההגירה קיים גם ב-IPv6, דרך ipv6_opt, אבל רק כשמשתמשים ב-CALIPSO, ושהוא יטופל בתיקון נפרד.

מה אפשר להוציא מקריאה אחת מזיכרון משוחרר

צוות ה-CVE של הקרנל פירט ברשומה עצמה למה נתן לכל רכיב בציון את הערך שנתן, וההסבר הזה הוא בעצם מפת הניצול. בצד של דליפת המידע: אחרי השחרור, ip_options_build מעתיקה את תוכן השדה לפי optlen הישן אל תוך ה-SYNACK שיוצא לרשת, כלומר הקרנל שולח לתוקף, בחבילה לגיטימית לגמרי, את מה שהוקצה מאז באותו מקום בערימה. בצד של הכתיבה: אותו optlen ישן, יחד עם ההיסטים של srr, rr ו-ts, מכוון את ה-memcpy ואת כתיבות הכתובת והזמן, ומאפשר כתיבות מחוץ לתחום בזיכרון הקרנל. ובצד של הזמינות: optlen שהוקצה מחדש יכול לגרום ל-skb_push לחרוג מהמקום שנשמר בראש החבילה ולהפיל את המערכת.

הפוסט של Anthropic, שפרסם צוות ה-Frontier Red Team של החברה ב-27 באוקטובר, מתאר ניצול בשני שלבים. בשלב הראשון התוקף מנצח את המרוץ כך שהקרנל קורא שדה אורך שהוא שולט בו, ומחזיר בתשובה יותר בייטים ממה שהיה אמור. אם באחת החריגות האלה יושב מצביע לזיכרון הקרנל, נפלה ההגנה שמסדרת את הקרנל בכתובת אקראית בכל אתחול (KASLR), והתוקף יודע איפה הכול נמצא. בשלב השני אותה חולשה משמשת להצפת buffer ולדריסת מצביע לפונקציה, שמופנה אל מטען שכבר הושתל בזיכרון הקרנל דרך תכונת פרוטוקול רגילה. כשהקרנל קורא לפונקציה, המטען משרשר קריאות לפונקציות קרנל קיימות ומסיים בהרצת פקודת shell כ-root.

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

נקודה אחת שכדאי לשים לב אליה: הניצול של Anthropic כוון לשרתים שמאזינים ב-multipath TCP. לא רשומת ה-CVE ולא התיקון עצמו מזכירים MPTCP, ואין מקור שאומר אם ההתניה הזאת היא מגבלה של החולשה עצמה או רק של הניצול המסוים שנכתב.

אז 9.8 או 8.1?

הציון שנתן צוות ה-CVE של הקרנל הוא 9.8, עם וקטור שאומר תקיפה מהרשת, מורכבות נמוכה, בלי הרשאות ובלי מעורבות משתמש. Red Hat נתנה 8.1, עם וקטור זהה לחלוטין חוץ ממדד אחד: מורכבות התקיפה, שאצלה גבוהה. כל הפער בין שני הציונים יושב על המדד הזה.

הטיעון של צוות הקרנל מפורש ברשומה: שני הצדדים של המרוץ מופעלים על ידי התוקף, ותזמון בין מעבדים וסידור הערימה אינם תנאים שמחוץ להשפעתו. Red Hat, מצידה, מסווגת את החולשה כ-CWE-825 וכותבת שהיא מובילה בעיקר לקריסת מערכת או לשחיתות זיכרון אפשרית. The Hacker News מדווח ש-Oracle ו-SUSE נתנו אף הן 8.1, ו-Amazon נתנה 7.0.

(48 הדקות שלקחו ל-Anthropic תומכות, אם לדייק, בשני הצדדים בוויכוח: תלוי אם סופרים את הזמן שנדרש או את העובדה שבסוף זה עבד.)

שלושה שבועות, ורוב השרתים עדיין בלי תיקון

במעלה הזרם התיקון כבר קיים בגרסאות 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56, 7.2.10 ו-7.3. הבעיה היא שרוב האנשים לא מריצים קרנל ממעלה הזרם, אלא את מה שההפצה שלהם ארזה להם.

דביאן כבר שחררה: bookworm קיבלה 6.1.190-1, ו-trixie קיבלה 6.12.113-1. אובונטו מסמנת את החולשה בעדיפות גבוהה ובציון 9.8, אבל בטבלת הסטטוס שלה, נכון ל-29 באוקטובר, אף גרסה נתמכת לא קיבלה תיקון: 22.04, 24.04, 26.04 ו-26.10 מסומנות כעבודה בתהליך, ו-16.04, 18.04 ו-20.04 פשוט חשופות. Red Hat כותבת במפורש שאין מעקף זמין, או שהמעקפים הקיימים לא עומדים באמות המידה שלה.

מה שאין, לפחות בינתיים, הוא עדות לניצול בפועל. קטלוג ה-KEV של CISA, בגרסה שפורסמה ב-29 באוקטובר, לא כולל את CVE-2026-98432.

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

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

שווה גם לזכור שהחולשה הזאת לא דורשת שירות מיוחד כלשהו, אלא רק שמישהו יקשיב על TCP מעל IPv4. רשימת המכונות לבדוק היא בפועל כל מה שעונה לחיבורים מבחוץ, ולא רק השרתים שרגילים להיות בראש סדר העדיפויות.