מה זה Stack Canary, וממה בעצם הוא מגן¶
השם "canary" (כנרית) מגיע ממנהג ותיק של כורי פחם, שהיו לוקחים איתם לתוך המכרה כנרית בכלוב - ציפור רגישה במיוחד לגזים רעילים. אם הכנרית מתה, זה היה סימן מוקדם לסכנה, עוד לפני שבני האדם עצמם נפגעו. בעולם ניצול החולשות הבינארי אימצו את אותו רעיון בדיוק, רק שהכנרית כאן היא ערך בזיכרון.
מה זה בעצם¶
Stack Canary הוא ערך אקראי שהקומפיילר מכניס אוטומטית לסטאק, ממש לפני כתובת ההחזרה, בכל פונקציה שמכילה חוצץ מקומי (buffer) שעלול להיות פגיע לגלישה. הרעיון פשוט - אם תוקף מנסה לבצע גלישת חוצץ קלאסית כדי לדרוס את כתובת ההחזרה, הוא חייב "לעבור דרך" ה-canary כדי להגיע אליה, כי הוא נמצא בדיוק בין החוצץ לכתובת ההחזרה. אם הכתיבה דורסת גם את ה-canary, זה סימן ברור שמשהו לא תקין קרה.
לפני שהפונקציה חוזרת בסיום ריצתה, הקוד שהקומפיילר הוסיף בודק אם ערך ה-canary עדיין זהה לערך המקורי שהוגדר בתחילת הפונקציה. אם הוא שונה - סימן שמישהו כתב מעבר לגבולות החוצץ - התוכנית קורסת מיידית בצורה מבוקרת, במקום להמשיך ולנסות לחזור לכתובת שנדרסה.
איך זה נראה בפועל, ברמת הרעיון¶
חשבו על זה כמו על חותם שעווה על מעטפה. אם מישהו פותח את המעטפה ומנסה לסגור אותה מחדש, החותם נשבר או נראה שונה - וגם אם התוכן בפנים נראה תקין, אתם יודעים שמשהו נגע בו בדרך. ה-canary עובד באותו עיקרון - הוא לא מונע פיזית מתוקף לכתוב לזיכרון, אבל הוא מבטיח שאם מישהו כתב יותר ממה שהיה אמור, זה יתגלה לפני שהנזק האמיתי (קפיצה לכתובת שהתוקף בחר) מתרחש.
מה זה אומר לגבי תוקפים¶
Stack Canary לא סוגר את הבאג - הוא רק מונע את הדרך הפשוטה ביותר לנצל אותו. תוקפים פיתחו כמה גישות להתמודד איתו:
- דליפת ערך ה-canary מראש. אם אפשר לגרום לתוכנית "להדליף" את ערך ה-canary (למשל דרך חולשת מחרוזת פורמט, או דרך התנהגות ספציפית בקוד), אפשר לכלול את הערך הנכון בדיוק בתוך ה-payload, כך שהבדיקה תעבור בהצלחה ולא תזהה כלום.
- דילוג על ה-canary לגמרי. במקרים מסוימים, אפשר לנצל את הזיכרון בדרך אחרת שלא כוללת כתיבה סדרתית מהחוצץ ועד לכתובת ההחזרה - למשל, ניצול שדורס משתנה ספציפי בלי לעבור דרך כל האזור.
- ניצול חולשה שלא תלויה בכתובת החזרה בכלל. לא כל חולשה מטרתה לדרוס כתובת החזרה על הסטאק - חלקן מכוונות למקום אחר בזיכרון שאין לו canary שמגן עליו.
חשוב להבין - ה-canary מייצר ערך אקראי חדש בכל הרצה של התוכנית (בדרך כלל נגזר מ-ASLR), כך שאי אפשר פשוט "לזכור" אותו מהרצה קודמת. זו הסיבה שהוא כל כך אפקטיבי בשילוב עם ASLR.
למה זה לא הגנה מושלמת¶
בדיוק כמו NX ו-ASLR, Stack Canary מקשה על ניצול אבל לא מבטל את הבאג. הוא גם מגן רק על גלישות שקורות באופן סדרתי, בייט אחרי בייט, מהחוצץ לכיוון כתובת ההחזרה. אם תוקף מוצא דרך לכתוב ישירות לזיכרון בכתובת ספציפית בלי לעבור דרך ה-canary (מה שנקרא לפעמים "arbitrary write"), ההגנה הזאת פשוט לא רלוונטית.
זו גם הסיבה שחוקרי חולשות תמיד בודקים איזה הגנות בדיוק מופעלות על בינארי ספציפי (באמצעות checksec) לפני שהם מתכננים אסטרטגיית ניצול - כי הימצאות canary משנה לגמרי את הגישה הנדרשת.
למה חשוב להבין את זה¶
כל אתגר CTF ברמה בינונית ומעלה בקטגוריית Pwn כולל היום כמעט תמיד Stack Canary מופעל, בדיוק כי זה משקף את המצב האמיתי בתוכנה מודרנית. מי שלא מבין איך לזהות ולעקוף canary פשוט ייתקע בהרבה מהאתגרים המעניינים ביותר.
בקורס מחקר חולשות אנחנו מלמדים איך לזהות canary בבינארי, איך לגלות מתי הוא "נשבר" בזמן ניתוח, ואיך משלבים דליפת מידע כדי לעקוף אותו כשצריך - כל זה עם תרגול על אתגרים אמיתיים.
מתמודדים עם canary שמסרב "להישבר" בדרך שציפיתם? בואו לשאול בדיסקורד שלנו, כמעט בטוח שמישהו כבר התמודד עם בדיוק אותו מקרה.
לסיכום¶
Stack Canary הוא מנגנון פשוט ואלגנטי - ערך שמזהיר את התוכנית שמישהו "נגע" באזור שהוא לא היה אמור לגעת בו. הוא לא סוגר חולשות, אבל הוא הופך את הניצול הישיר ביותר שלהן לבלתי אפשרי, ומכריח תוקפים לחשוב על דרכים יצירתיות יותר - וזה בדיוק מה שהופך את התחום הזה למרתק כל כך.