מה זה ret2libc, הצעד הראשון אחרי NX¶
אחרי שהבנתם גלישת חוצץ ופגשתם את הגנת NX שמונעת הרצת קוד שהוזרק לזיכרון, הטכניקה הראשונה שכדאי להכיר היא ret2libc. היא פשוטה יחסית ל-ROP מלא, אבל היא מבוססת על בדיוק אותו רעיון - להשתמש בקוד שכבר קיים בתוכנית, במקום להביא קוד חדש.
הרעיון הבסיסי¶
כל תוכנית שכתובה ב-C ומקושרת דינמית טוענת לזיכרון גם את הספריה glibc, שמכילה פונקציות סטנדרטיות כמו printf, strcpy, ובין היתר גם system. הפונקציה system היא בדיוק מה שתוקף צריך - היא מקבלת מחרוזת ומריצה אותה כפקודת מעטפת. אם glibc כבר נמצאת בזיכרון התוכנית בכל מקרה, למה לא פשוט לגרום לתוכנית לקפוץ ישירות לפונקציית system הקיימת בתוכה, עם הארגומנט "/bin/sh"?
זה בדיוק מה ש-ret2libc עושה. במקום לדרוס את כתובת ההחזרה עם כתובת של shellcode שהזרקתם, אתם דורסים אותה עם הכתובת של system בתוך glibc, ומכינים על הסטאק את הארגומנטים הנדרשים לה (או ברגיסטרים המתאימים, בהתאם לארכיטקטורה). כשהפונקציה הפגיעה חוזרת, היא בפועל "קופצת" ל-system("/bin/sh"), ופותחת לכם shell.
למה זה עוקף את NX¶
NX מסמן אזורי זיכרון כמו הסטאק כלא ניתנים להרצה, אבל הוא לא נוגע בקוד שכבר קיים בספריות שנטענות עם התוכנית, כי הקוד הזה כן אמור להיות מורשה להרצה - הוא חלק לגיטימי מהתוכנית. ret2libc לא מריץ שום דבר חדש, הוא רק גורם לתוכנית לקפוץ למקום קיים בקוד קיים, ולכן ה-NX פשוט לא רלוונטי.
האתגר האמיתי - ASLR¶
בעולם ללא ASLR, ret2libc קל יחסית - כתובת system בתוך glibc קבועה, ופשוט משתמשים בה. אבל ASLR מערבב את הכתובת שבה glibc נטענת בכל הרצה מחדש, כך שאי אפשר לדעת מראש איפה system נמצאת. כאן נכנס שלב נוסף - קודם צריך לגרום לתוכנית לדלוף כתובת כלשהי בתוך glibc (למשל דרך printf שמדפיסה כתובת מהסטאק, או דרך הקריאה לפונקציה שכבר נפתרה ב-GOT), לחשב מהכתובת הזאת את הבסיס שבו glibc נטענה, ורק אז לחשב את הכתובת המדויקת של system באותה הרצה.
זה בדיוק המקום שבו ret2libc מתחיל להרגיש כמו בלש עבודה - חלק מהניצול, חלק מהחישוב.
היחס בין ret2libc ל-ROP¶
ret2libc הוא בעצם המקרה הפרטי הפשוט ביותר של ROP - שרשרת עם gadget יחיד, שהוא קריאה לפונקציה שלמה במקום הרכבה של פעולות בודדות. זו הסיבה שהוא נלמד ראשון - הוא מלמד את אותו עיקרון (שימוש בקוד קיים במקום קוד מוזרק), אבל בלי המורכבות של הרכבת שרשרת ארוכה של gadgets קטנים. ברגע שmastered את ret2libc, המעבר ל-ROP מלא מרגיש כמו הרחבה טבעית, לא כמו קפיצה למקום חדש לגמרי.
איפה זה מופיע בפועל¶
אתגרי CTF בקטגוריית Pwn משתמשים ב-ret2libc כאבן דרך כמעט קבועה - זה השלב שאחרי ret2win הפשוט, ולפני שרשראות ROP מורכבות עם gadgets מרובים. גם בעולם האמיתי, כשתוקפים נתקלים בבינארי עם NX מופעל אבל בלי הגנות נוספות משמעותיות, ret2libc הוא לרוב הדרך הראשונה שהם בודקים.
איך לומדים את זה נכון¶
ret2libc דורש הבנה של calling convention (איך ארגומנטים מועברים לפונקציה בארכיטקטורה הרלוונטית), הבנה של איך לאתר כתובות בתוך glibc, ותרגול על דליפת כתובות. זה שילוב מצוין בין תיאוריה לתרגול מעשי.
בקורס מחקר חולשות אנחנו בונים ret2libc שלב אחר שלב, כולל התמודדות עם ASLR ודליפת כתובות, לפני שממשיכים לשרשראות ROP מורכבות יותר.
מתמודדים עם ret2libc שכבר כמעט עובד אבל קורס בשלב האחרון? יש קהילה שלמה בדיסקורד שאוהבת לדבג בדיוק את הרגעים האלה.
לסיכום¶
ret2libc מוכיח שלפעמים הפתרון הכי אלגנטי לחולשה הוא להשתמש במה שכבר קיים, ולא להביא כלים חדשים. הוא הצעד הטבעי אחרי הבנת NX, והבסיס המושגי שעליו נבנה כל עולם ה-ROP המודרני.