מה זה גלישת חוצץ - Buffer Overflow, עם מודל מנטלי פשוט¶
אם יש חולשה אחת שכל מי שנכנס לעולם ניצול החולשות חייב להבין לעומק, זו גלישת חוצץ. היא לא רק הכי ותיקה - היא גם הבסיס להבנת כמעט כל חולשה אחרת בעולם הבינארי. אז בואו נבנה לה מודל מנטלי שיישאר איתכם.
תחשבו על זה כמו כוס מים¶
דמיינו כוס מים בגודל קבוע. אם אתם שופכים לתוכה בדיוק את הכמות שהיא מחזיקה, הכל בסדר. אבל אם ממשיכים לשפוך אחרי שהכוס מלאה, המים גולשים החוצה ומתפזרים על השולחן - ועל כל מה שנמצא לידה.
בדיוק ככה זה עובד בזיכרון של תוכנית. כשמתכנת מקצה "חוצץ" (buffer) - כלומר, אזור זיכרון בגודל קבוע לאחסון נתונים, לדוגמה מערך של 20 בייטים לשם משתמש - התוכנית מצפה שהנתונים שייכנסו לשם לא יעברו את הגודל הזה. הבעיה מתחילה כשהקוד לא בודק את זה, ומרשה לכתוב יותר מידע ממה שהוקצה. הנתונים ה"עודפים" האלה לא נעלמים - הם נכתבים ישר על אזורי הזיכרון הסמוכים.
למה זה מסוכן כל כך¶
כאן זה נהיה מעניין. באזור הזיכרון שנמצא ליד החוצץ, בדרך כלל על הסטאק, יושבים דברים קריטיים לתפקוד התוכנית - משתנים אחרים, ולפעמים גם כתובת החזרה (return address), שהיא בעצם ההוראה למעבד "לאן לחזור אחרי שהפונקציה הנוכחית מסתיימת".
אם תוקף מצליח לגלוש בדיוק עד לכתובת ההחזרה ולדרוס אותה בכתובת משלו, הוא בעצם שולט על הזרימה של התוכנית. במקום שהתוכנית תחזור למקום ה"נכון", היא תקפוץ לאן שהתוקף בחר - למשל, לקוד זדוני שהוא הזריק לזיכרון בעצמו. זה ההבדל בין קריסה מעצבנת לבין השתלטות מלאה על התהליך.
דוגמה מוכרת (בלי הרבה קוד)¶
יש פונקציה קלאסית בשפת C בשם gets(), שקוראת קלט ממשתמש בלי שום הגבלה על האורך שלו. אם מתכנת משתמש בה כדי למלא חוצץ בגודל 50 בייטים, ומישהו מזין 500 תווים, כל 450 התווים העודפים פשוט ממשיכים לזלוג הלאה בזיכרון, ודורסים כל מה שנמצא שם. זו לא תיאוריה - זה בדיוק סוג הבאג שגרם לתולעת המורריס, אחת ההתקפות המוקדמות והמפורסמות ביותר בהיסטוריה של האינטרנט, כבר ב-1988.
למה זה עדיין רלוונטי היום¶
אתם אולי חושבים - רגע, זה נשמע כמו באג ישן, בטח כבר תיקנו את זה. חלקית נכון. שפות מודרניות כמו Python או Java מטפלות בזיכרון בצורה בטוחה יותר ומונעות את רוב סוגי הגלישה האלה אוטומטית.
אבל שפת C ו-C++ עדיין נמצאות בכל מקום - מערכות הפעלה, דרייברים, קושחה (firmware), מכשירי IoT, שרתים קריטיים. בשפות האלה המתכנת אחראי לנהל את הזיכרון בעצמו, ואנושות ממשיכה לעשות אותן טעויות. גם היום, שנים אחרי שהחולשה מוכרת, גלישות חוצץ ממשיכות להתגלות בתוכנות מהמעלה הראשונה, כולל מערכות הפעלה ודפדפנים.
איך מגנים על עצמם מפני זה היום¶
חשוב להבין שהתעשייה לא ישבה בחיבוק ידיים. פותחו כמה שכבות הגנה שהופכות ניצול פשוט של גלישת חוצץ למורכב הרבה יותר:
- Stack Canary - ערך אקראי שמונח לפני כתובת ההחזרה, ואם הוא נדרס, התוכנית קורסת מיידית לפני שהנזק נגרם.
- ASLR - טכניקה שמפזרת אקראית את מיקומי הזיכרון בכל הרצה, כך שתוקף לא יכול לדעת בוודאות לאן לקפוץ.
- NX/DEP - סימון אזורי זיכרון כ"לא ניתנים להרצה", כדי שקוד שהוזרק לתוכנית לא יוכל פשוט לרוץ.
ההגנות האלה לא הפכו את הבעיה לפתורה - הן הפכו אותה למשחק שחמט מורכב יותר, שבדיוק בגללו נולדו טכניקות כמו ROP.
איך ממשיכים מכאן¶
הבנת גלישת חוצץ היא נקודת הכניסה הכי טובה לכל עולם הניצול הבינארי. ברגע שהיא "נכנסת לכם ליד", כל שאר החולשות מתחילות להיראות הרבה פחות מפחידות. בקורס מחקר חולשות אנחנו בונים את זה בדיוק ככה - מתחילים מגלישת חוצץ פשוטה על הסטאק, ומתקדמים משם לניצול הגנות מודרניות ולחולשות מורכבות יותר.
יש לכם שאלות תוך כדי הדרך? זה בדיוק מה שהדיסקורד שלנו נועד בשבילו.
לסיכום¶
גלישת חוצץ היא לא רק חולשה - היא שיעור יסוד באיך זיכרון עובד ואיך תוכנה יכולה להישבר. תבינו את הכוס שגולשת, ותבינו למה זה קורה, ומשם הדרך לכל שאר עולם הניצול הבינארי הרבה יותר קצרה ממה שנראה.