גלישת חוצץ

במחשבים, גלישת חוצץ (באנגלית: Buffer overflow) היא שגיאת תכנות המתבטאת בכך שתוכנית מחשב כותבת לחוצץ – אזור בזיכרון המחשב – יותר מידע ממה שהוא מסוגל להכיל. כתוצאה מכך חלק מהמידע "גולש" אל מחוץ לגבולות החוצץ, ומשנה נתונים שלא היו אמורים להשתנות. המידע שנמחק לעיתים קרובות הכרחי להמשך ריצתה התקינה של התוכנית, ובשל כך גלישת חוצץ עלולה לגרום לתוכנית להחזיר תוצאות לא נכונות, לקרוס לחלוטין, או אף לאפשר הרצה של קוד זדוני הגורם לתוכנית לפעול באופן שלא תוכנן מראש. בשל כך, גלישות חוצץ מהוות בסיס לפרצות אבטחה רבות. ברוב המקרים, גלישת חוצץ תגרום לפחות לקריסה של התוכנית המשתמשת בחוצץ. חוצצים הם תחומים בזיכרון המחשב שמשמשים לאחסון מידע - בפרט, את המשתנים בהם משתמשת התוכנית. מכיוון שגודלו של החוצץ חסום ומוגדר מראש, כתיבה של מידע רב יותר מאשר הוא מסוגל להכיל תגרום לגלישה - כתיבה לאזור בזיכרון שמשמש למטרות אחרות, ובמיוחד - אזור בזיכרון שמכיל פקודות למעבד. הדוגמה הנפוצה ביותר לגלישת חוצצים מתרחשת כאשר תוכנית מבצעת כתיבה של קלט לתוך חוצץ בלי לוודא שגודל הקלט קטן מגודל החוצץ. לדוגמה, אחת מפונקציות הספרייה הסטנדרטית של C (הפונקציה gets) מבצעת כתיבה לתוך חוצץ של קלט שגודלו אינו מוגבל - כלומר, כל שימוש בה עשוי לגרום לגלישת חוצץ, בהתאם לקלט שמועבר אליה. מרגע שהתרחשה גלישת חוצץ, מהלך התוכנית "איננו מוגדר", כלומר לא ניתן להבטיח דבר לגבי המשך ריצת התוכנית: היא עשויה לקרוס, לרוץ באופן לא תקין, להמשיך לרוץ ולתת תוצאות שגויות, או לגרום נזק. לחלופין היא יכולה לרוץ באופן תקין לחלוטין ללא שום סימפטום בעקבות הגלישה. לעיתים קרובות מאוחסן המידע הדרוש להבטחת ריצתה התקינה של תוכנית בסמוך לחוצצים בה היא משתמשת. גלישת חוצץ עשויה לגרום למחיקת מידע זה והחלפתו במידע אחר. פרט לפגיעה בריצתה התקינה של התוכנית, הדבר עלול לסייע לווירוס מחשב "להשתלט" על ריצת התוכנית ולהריץ קטעי קוד זדוניים וזאת על ידי כתיבה על אזור בזיכרון המכיל קוד תוכנה שמתבצע. גלישת חוצץ הגורמת לקריסה של התוכנית, ללא הרצת קוד זדוני, מכונה "מניעת שירות" (Denial of Service). במקרה שהחוצץ נמצא במחסנית קריאות, הזיכרון שמוקצה לחוצץ נמצא בסמיכות לתא הזיכרון שמכיל את כתובת הזיכרון שאליה יש לחזור לאחר סיום הפונקציה, לצורך המשכה התקין של התוכנית. אם ידוע כי בתוכנית קיים כשל של גלישת חוצץ, ניתן לעיתים לגרום לכך שתוכנו של תא זיכרון זה ישתנה, והוא יצביע על כתובתו של קטע קוד זדוני. סוג זה של גלישת חוצץ נקרא גם "גלישת חוצץ במחסנית" (Stack buffer overflow). כאשר החוצץ נמצא בערימה, הוא לרוב אינו נמצא בסמיכות לכתובות חזרה כל שהן, ולכן באופן כללי ניתן לומר כי קשה יותר לנצל גלישת חוצץ בערימה לטובת הרצת קוד זדוני מאשר במחסנית. סוג זה של גלישת חוצץ נקרא גלישת ערימה (Heap overflow). על ידי ניצול של כל אחת משתי הפרצות, ניתן לשתול סוס טרויאני בקבצים שאינם קובצי הרצה, המכילים מידע בלבד. ישנן מספר דרכים למנוע גלישות חוצץ, המתחלקות לשני סוגים עיקריים: הגנות בזמן הידור והגנות בזמן ריצה. שימוש בשפות בטוחות. ישנן שפות שמנהלות בעצמן את הזיכרון, כך שהמתכנת לא צריך לעשות זאת. בשפות כאלו המתכנת לא צריך לבדוק בעצמו אם בחוצץ יש מספיק מקום. דוגמאות לשפות המנהלות בעצמן את הזיכרון הן: C#, Java ו-Python. כתיבת קוד בטוח. לתוכנות שנכתבות ונבדקות ביסודיות יש סיכוי נמוך יותר להיפגע מגלישת חוצץ, פשוט על ידי ווידוא שהתוכנה בודקת את הקלט ומוודאת שאינו גדול יותר מהחוצץ שמוקצב לו. שימוש בפונקציות בטוחות. גם כאשר משתמשים בשפות שמאפשרות גלישת חוצץ, ניתן להשתמש בפונקציות בטוחות שמונעות גלישת חוצץ. לדוגמה, בשפת C, במקום להשתמש בפונקציה strcpy שאינה בטוחה, ניתן להשתמש בפונקציה strncpy שמקבלת כפרמטר נוסף את כמות התווים שאנו רוצים להעתיק. הגנה על המחסנית. על מנת להגן על המחסנית המהדר מציב מספר אקראי בין המשתנים של פונקציה מסוימת לכתובת החזרה שלה. מערכת ההפעלה בודקת את הערך לפני תחילת ריצת הפונקציה ולפני החזרה שלה. אם כתובת החזרה של הפונקציה תידרס, הערך האקראי יידרס גם כן, ומערכת ההפעלה תוכל להתריע על גלישת חוצץ. שיטה זו נקראת קנרית המחסנית (Stack Canary). מניעת הרצת נתונים (DEP - Data Execution Prevention). בטכניקה זו אנו מסמנים עמודי זיכרון כבלתי ניתנים להרצה. כך אנחנו יכולים להגדיר שלא יהיה ניתן להריץ קוד מאזורי זיכרון המיועדים למידע. בצורה כזו גם אם התוקף יבצע גלישת חוצץ אל המחסנית או הערימה, הוא לא יוכל להריץ משם קוד. חיסרון בשיטה הזו הוא שהתוקף עדיין יכול לבצע התקפת Return to libc. בהתקפה הזו התוקף גורם לגלישה אל פונקציה מהספריות הסטנדרטיות. מכיוון שמדובר בפונקציה שכבר קיימת, היא נמצאת באזור זיכרון שניתן להריץ ממנו קוד ולכן ניתן להריץ אותה על אף מנגנון DEP. רנדומיזציה של מרחב הכתובות (ASLR - Address Space Layout Randomization). כל פעם שאנחנו מריצים תהליכון אנחנו מפזרים מחדש את הספריות הסטנדרטיות בזיכרון באופן אקראי, כך שהתוקף לא יכול לדעת את הכתובות של הפונקציות הסטנדרטיות. בצורה כזו הוא לא יכול "לקפוץ" ולהשתמש בפונקציות כאלו. כך ניתן להתגונן מהתקפת Return to libc. אף על פי שקיימות שיטות רבות למניעה של ניצול גלישת חוצצים להרצת קוד זדוני, לא קיימת כיום הגנה מוחלטת מניצול כזה. להלן קטע קוד בשפת C, שבמצבים מסוימים עלול ליצור גלישת חוצץ: int B=0; char A[8]={}; strcpy(A, "excessive"); המערך והמספר מאותחלים לאפס ואז מבוצעת העתקה. פריסה אפשרית של הזיכרון עבור הקוד המתואר, לפני ביצוע ההעתקה: לאחר פקודת ההעתקה: גלישה נומרית (מדעי המחשב) הזרקת SQL גלישת מחסנית Stallings, William and Brown, Lawrie, Computer Security: Principles and Practice, USA: Prentice Hall Press, 2014, עמ' 359-365 alvinashcraft, Data Execution Prevention - Win32 apps, docs.microsoft.com (באנגלית אמריקאית)