מה זה Unlink Attack, הטכניקה שלימדה דור שלם לחשוב על heap¶
לפני שהיה tcache, ולפני שההגנות המודרניות על ה-heap הפכו נפוצות, Unlink Attack הייתה אחת הטכניקות המרכזיות לניצול heap. היום היא כמעט לא רלוונטית לניצול ישיר בגלל הגנות שנוספו, אבל היא נשארת אחד השיעורים החשובים ביותר להבנת איך מבני נתונים מבוססי מצביעים יכולים להישבר, וכמעט כל חוקר heap רציני עדיין לומד אותה.
הרקע - רשימה מקושרת כפולה¶
בין הבינים ה"ותיקים" יותר ב-glibc (כמו smallbin) יש chunks פנויים שמאורגנים ברשימה מקושרת כפולה - כל chunk שומר מצביע לזה שלפניו ברשימה (bk - back) ומצביע לזה שאחריו (fd - forward). המבנה הזה מאפשר להוסיף ולהסיר chunks מכל מקום ברשימה ביעילות.
הפעולה של הסרת chunk מהרשימה נקראת "unlink" - ה-chunk שלפניו מתעדכן להצביע קדימה ל-chunk שאחריו, וה-chunk שאחריו מתעדכן להצביע אחורה ל-chunk שלפניו, כך שה-chunk המוסר "נעלם" מהרשימה בלי לשבור את הרצף.
איפה הפגיעות נכנסת¶
בגרסאות ישנות של glibc, פעולת ה-unlink הזאת התבצעה בלי לבדוק שהמצביעים fd ו-bk באמת מצביעים למקומות הגיוניים ברשימה. אם תוקף מצליח לגרום לגלישת חוצץ בתוך chunk, הוא יכול לדרוס את המטא-דאטה של ה-chunk הבא, כולל השדות fd ו-bk שלו, עם ערכים שהוא בוחר בעצמו.
כשה-chunk המזויף הזה מגיע בסופו של דבר לפעולת unlink (למשל כתוצאה של קריאה ל-free על ה-chunk שאחריו, שגורמת לאיחוד של chunks סמוכים פנויים), הקוד הישן פשוט ביצע את הפעולה fd->bk = bk ו-bk->fd = fd בלי בדיקה. אם התוקף שם ב-fd כתובת של יעד כלשהו בזיכרון (נגיד, כתובת ליד GOT entry) פחות היסט קבוע, ו-bk את הערך שהוא רוצה לכתוב שם, פעולת ה-unlink בעצמה הופכת לכתיבה שרירותית - "unlink" אחת שקובעת "כתוב את הערך X לכתובת Y", בלי שום צורך בקריאה נוספת.
זו הייתה טכניקה גאונית בזמנה, כי היא הפכה קוד "תחזוקה" שגרתי לגמרי - ניהול רשימה מקושרת - למנגנון כתיבה שרירותית מלא, בלי צורך לגעת בקוד התוכנית עצמה בכלל.
למה זה מוגן היום¶
מאז שהטכניקה הזאת התגלתה ונוצלה בהרחבה, glibc הוסיפה בדיקות שלמות בתוך קוד ה-unlink עצמו. כיום, לפני שמבצעים unlink, הקוד בודק שהמצביעים fd ו-bk עקביים עם המבנה שמצפים לו - כלומר שה-chunk שאליו fd מצביע, ה-bk שלו באמת מצביע בחזרה ל-chunk הנוכחי, וההפך. אם הבדיקה נכשלת, התוכנית קורסת עם שגיאה מפורשת כמו "corrupted double-linked list", במקום לבצע את הכתיבה השרירותית בשקט.
הבדיקה הזאת לא הפכה חולשות heap לבלתי אפשריות, היא רק סגרה את הדרך הספציפית הזאת, ודחפה חוקרים למצוא טכניקות חדשות - וזה בדיוק מה שהוביל בסופו של דבר לטכניקות מודרניות כמו tcache poisoning.
למה עדיין שווה ללמוד אותה¶
Unlink Attack היא דוגמה מושלמת ללמד "איך חושבים" בעולם ניצול heap - איך מבנה נתונים תמים לכאורה, כשמניחים מראש שהזיכרון תקין, הופך לכלי ניצול ברגע שההנחה הזאת נשברת. ברגע שמבינים את ההיגיון הזה על טכניקה ישנה ומתועדת היטב, קל הרבה יותר להבין טכניקות חדשות שמבוססות בדיוק על אותו עיקרון - ניצול הנחות לא מאובטחות במבני נתונים פנימיים.
איך לומדים את זה נכון¶
הדרך הכי טובה היא לתרגל על גרסת glibc ישנה במיוחד, שבה ההגנה עוד לא קיימת, ולראות בפועל איך פעולת ה-unlink הופכת לכתיבה שרירותית תחת debugger.
בקורס מחקר חולשות אנחנו מלמדים את Unlink Attack כפרק היסטורי חשוב, כדי לבנות אינטואיציה לפני שעוברים לטכניקות המודרניות שדחקו אותה החוצה.
רוצים להבין למה בדיוק ההגנה החדשה מזהה ניצול כזה? בדיסקורד שלנו יש דיונים מעמיקים בדיוק על סוג השאלות האלה.
לסיכום¶
Unlink Attack היא אולי טכניקה שכבר לא עובדת ישירות על גרסאות מודרניות, אבל היא עדיין אחד השיעורים הכי חשובים על איך מבני נתונים מבוססי מצביעים בזיכרון יכולים להפוך לכלי תקיפה, ברגע שהאמון בזיכרון נשבר.