מה זה NX ו-DEP, וכיצד הם שינו את עולם ניצול החולשות¶
יש רגע מכונן בהיסטוריה של ניצול חולשות בינארי, שבו הדרך ה"פשוטה" להריץ קוד זדוני על מחשב של מישהו הפכה פתאום למורכבת בהרבה. אחראית לזה, בין השאר, הגנה שנקראת NX - ובעולם המיקרוסופט מכנים אותה DEP. בואו נבין מה היא בדיוק עושה.
הרעיון הבסיסי - הפרדה בין נתונים לקוד¶
בתוכניות "תמימות", אזורי זיכרון כמו הסטאק וה-Heap נועדו לאחסן נתונים - משתנים, מחרוזות, מבני נתונים. הם לא אמורים להכיל קוד שהמעבד מריץ. אבל היסטורית, מערכות הפעלה לא אכפו את ההבחנה הזאת בצורה קפדנית - אזור זיכרון שנועד לנתונים יכול היה, טכנית, להכיל גם קוד שהמעבד מוכן להריץ אם קופצים אליו.
זה בדיוק מה שתוקפים ניצלו במשך שנים - גלישת חוצץ שמזריקה shellcode לסטאק, ואז דורסת את כתובת ההחזרה כדי לקפוץ ישר אליו. הסטאק הוא אזור נתונים, אבל בלי הגנה מיוחדת, שום דבר לא מנע מהמעבד להריץ את מה שנמצא שם.
NX (ר"ת No-eXecute) ו-DEP (ר"ת Data Execution Prevention) הן שתי גרסאות של אותו רעיון - שימוש בחומרת המעבד כדי לסמן אזורי זיכרון כ"לא ניתנים להרצה". כשמעבד מנסה להריץ הוראה מאזור זיכרון שמסומן ככה, הוא פשוט מסרב, וגורם לתוכנית לקרוס באופן מבוקר במקום להריץ קוד לא מורשה.
איך זה עובד ברמה הטכנית¶
מעבדים מודרניים תומכים בביט מיוחד שנקרא בדרך כלל NX bit (או XD bit באינטל), שמוגדר ברמת דפי הזיכרון (memory pages). מערכת ההפעלה מסמנת אזורים כמו הסטאק וה-Heap כ"לא להרצה", בעוד אזור הקוד עצמו של התוכנית (ה-.text section) מסומן כ"מותר להרצה, אסור לכתיבה". זה יוצר הפרדה ברורה - אזורים שכותבים אליהם נתונים לא יכולים לשמש להרצת קוד, ואזורים שמריצים קוד לא יכולים להישתנות בקלות.
ההבדל בין NX ל-DEP הוא בעיקר שמי - NX הוא המונח הכללי והטכני שמתייחס ליכולת החומרה עצמה, בעוד DEP הוא השם שמיקרוסופט נתנה למימוש שלה בוויינדוס. בפועל, שני המונחים מתארים בדיוק אותו רעיון הגנה.
מה זה אומר בשביל תוקפים¶
כשההגנה הזאת הפכה לסטנדרטית (וכיום היא כמעט תמיד מופעלת כברירת מחדל), ניצול "נאיבי" של גלישת חוצץ הפסיק לעבוד. אי אפשר יותר פשוט להזריק shellcode לסטאק ולקפוץ אליו - המעבד יסרב להריץ אותו.
זה בדיוק מה שהוליד את טכניקת ROP - Return Oriented Programming. הרעיון המרכזי מאחוריה הוא לעקוף את NX לא על ידי הרצת קוד חדש, אלא על ידי שימוש בקוד שכבר קיים בתוכנית ומסומן כ"מותר להרצה". אתם לא מפרים את חוקי NX - אתם פשוט מוצאים דרך לעבוד בתוכם.
בנוסף, תוקפים מתקדמים משתמשים לפעמים בטכניקה שנקראת ret2libc, שבה במקום להריץ shellcode משלהם, הם גורמים לתוכנית לקפוץ ישירות לפונקציות שכבר קיימות בספריות המערכת (כמו system), שגם הן, כמובן, מסומנות כמותרות להרצה.
למה זה עדיין לא "פתר" את הבעיה¶
חשוב להבין - NX לא מתקן את הבאג המקורי בקוד. גלישת החוצץ עדיין קיימת, ותוקף עדיין יכול לשלוט על כתובת ההחזרה. NX רק מונע ממנו את הדרך הכי ישירה לנצל את השליטה הזאת. זה בדיוק הדפוס שחוזר על עצמו בכל עולם אבטחת המידע - הגנה חדשה לא מבטלת חולשה, היא מעלה את רף המורכבות הנדרש כדי לנצל אותה.
בשילוב עם הגנות אחרות כמו ASLR ו-Stack Canary, NX הוא חלק ממערכת שלמה שהופכת ניצול חולשות מודרני למשחק אסטרטגי מורכב, ולא רק "לכתוב shellcode ולקפוץ אליו".
למה חשוב להבין את זה כמתחילים¶
אי אפשר להבין ROP, ולמעשה את רוב עולם הניצול הבינארי המודרני, בלי להבין קודם למה NX קיים ומה הוא בדיוק חוסם. בקורס מחקר חולשות אנחנו מלמדים את ההגנות האלה בהקשר - קודם מראים ניצול "קלאסי" בלי הגנות, ואז מפעילים NX ומראים בדיוק למה הוא נשבר, ומה עושים כדי לעקוף אותו.
מתלבטים איך לבדוק אילו הגנות מופעלות על בינארי ספציפי? זה שאלה נהדרת לדיסקורד שלנו.
לסיכום¶
NX ו-DEP הם הגנות שמפרידות בין אזורי זיכרון שמריצים קוד לבין אזורים שמאחסנים נתונים בלבד, וסתמו את הדרך ה"קלה" ביותר לניצול גלישת חוצץ. הבנה שלהן היא תנאי בסיסי להבנת טכניקות מודרניות כמו ROP - ובלעדיה, קשה להבין למה עולם ניצול החולשות בכלל התפתח לכיוון שהוא נמצא בו היום.