לדלג לתוכן

מה זה Use After Free, ולמה היא אחת החולשות הכי מפחידות היום

אם תסתכלו על רשימת חולשות שהובילו לניצולים מפורסמים בדפדפנים ובמערכות הפעלה בשנים האחרונות, שם אחד יחזור שוב ושוב - Use After Free, או בקיצור UAF. זו חולשה שנשמעת פשוטה כשמסבירים אותה, אבל מסוכנת בצורה יוצאת דופן. בואו נבין למה.

הרעיון הבסיסי

כשתוכנית מבקשת זיכרון דינמי (על ה-Heap) באמצעות פונקציה כמו malloc, היא מקבלת מצביע (pointer) - כתובת שמצביעה על אזור הזיכרון הזה. כשהיא סיימה להשתמש בו, היא אמורה לשחרר אותו באמצעות free, כדי שהזיכרון יוכל לשמש שוב לצרכים אחרים.

הבעיה מתחילה כשהמתכנת משחרר את הזיכרון, אבל שוכח למחוק או לאפס את המצביע שהצביע אליו. המצביע הזה, שנקרא "מצביע מדולדל" (dangling pointer), עדיין מצביע על אותה כתובת - למרות שהזיכרון שם כבר לא "שייך" יותר לתוכנית באופן רשמי. אם מישהו בהמשך הקוד משתמש שוב במצביע הזה, קורה בדיוק מה שהשם מרמז - שימוש בזיכרון אחרי ששוחרר.

למה זה כל כך מסוכן

כאן זה נהיה מעניין. ברגע שזיכרון משוחרר, מנהל הזיכרון (allocator) חופשי להקצות אותו מחדש למישהו אחר בתוכנית - כל בקשת malloc חדשה עלולה לקבל בדיוק את אותה כתובת. אם תוקף מצליח לגרום לתוכנית להקצות מבנה נתונים שהוא שולט על התוכן שלו, בדיוק לאותו מקום שבו נמצא ה-pointer המדולדל, אז כשהקוד המקורי משתמש שוב ב-pointer הישן, הוא בעצם קורא או כותב לנתונים שהתוקף שתל שם.

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

למה זו חולשה נפוצה במיוחד בקוד מורכב

Use After Free לא נובע מטעות בודדת וברורה, אלא בדרך כלל מבעיה במבנה הלוגי של התוכנית - שני חלקים שונים בקוד, שכל אחד חושב שהוא "אחראי" על אותו אזור זיכרון, בלי תיאום נכון ביניהם. בתוכנות גדולות ומורכבות, כמו מנועי דפדפן שמנהלים אלפי אובייקטים בו זמנית (למשל אלמנטים ב-DOM), קל מאוד שחלק אחד ישחרר אובייקט בזמן שחלק אחר עדיין מחזיק בו pointer ומצפה שהוא עדיין תקף. זו הסיבה שדפדפנים כמו כרום ופיירפוקס ממשיכים לפרסם תיקוני אבטחה ל-UAF שוב ושוב, למרות שנות פיתוח ומאמצי אבטחה עצומים.

איך מזהים ומנצלים UAF בפועל

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

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

איך זה מתקשר לשאר עולם ניצול ה-Heap

Use After Free הוא אחת החולשות המרכזיות בתחום ניצול ה-Heap הרחב יותר, יחד עם Double Free ו-Heap Overflow. מה שמייחד אותו הוא שהוא לא דורש חריגה מגבולות זיכרון (overflow) בכלל - הוא מנצל תזמון ולוגיקה שגויה, לא כתיבה מעבר לגבול. זו הסיבה שהוא נחשב למורכב יותר לזהות ולתקן, וגם מעניין יותר ללמוד.

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

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

הצטרפו לקהילה בדיסקורד

לסיכום

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