מה זה one_gadget, ולמה כתובת אחת שווה שרשרת שלמה¶
אחרי שמכירים את ret2libc ואת הרעיון של קריאה ל-system("/bin/sh"), עולה שאלה טבעית - אם כל מה שצריך זה כתובת אחת שמריצה shell, אולי יש כבר כתובת כזאת מוכנה בתוך glibc, בלי צורך להרכיב את הקריאה בעצמכם? התשובה היא כלי בשם one_gadget, וההיגיון מאחוריו הוא אחד הדברים היפים בעולם ניצול הבינארי.
הבעיה ש-one_gadget פותר¶
בניצול קלאסי מסוג ret2libc, אתם צריכים לקפוץ ל-system, אבל גם לוודא שהארגומנט הראשון שלה הוא "/bin/sh". זה דורש שליטה על רגיסטר מסוים או על מקום מסוים בסטאק, בהתאם לארכיטקטורה, וזה לא תמיד פשוט להשיג במרחב הכתיבה המוגבל שיש לכם בחולשה.
מסתבר שבתוך glibc, יש כבר קטעי קוד קיימים שמריצים בעצמם execve("/bin/sh", 0, 0) בלי שאתם צריכים לספק ארגומנטים בכלל - הם כבר חלק מהמימוש הפנימי של הספריה, בדרך כלל שרידים מתוך פונקציות עזר פנימיות. אם אתם מצליחים לקפוץ ישירות לכתובת אחת כזו, אתם מקבלים shell מיידי, בלי להרכיב שום שרשרת ROP וזה כל הרעיון מאחורי השם "gadget יחיד".
למה זה לא תמיד "חינם"¶
הקטע החמקמק הוא שהקטעי קוד האלה נמצאים שם באקראי, כתוצר לוואי של איך הקומפיילר בנה את glibc, ולא תוכננו מראש בתור "דלת אחורית" נוחה. זה אומר שלרוב יש להם תנאים (constraints) שצריכים להתקיים כדי שהם יעבדו כמו שצריך - למשל, רגיסטר מסוים חייב להיות אפס, או מצביע מסוים על הסטאק חייב להצביע על זיכרון תקין וריק. אם התנאים האלה לא מתקיימים, הקפיצה תגרום לתוכנית לקרוס במקום לפתוח shell.
זו הסיבה שהכלי one_gadget לא רק מאתר את הכתובות האלה בתוך קובץ glibc נתון, אלא גם מציג בדיוק את התנאים שצריכים להתקיים לכל אחת מהן, כדי שתוכלו לבחור את זו שהכי קל לכם לספק בהינתן החולשה שיש לכם.
איך משתמשים בו בפועל¶
מריצים את הכלי מול קובץ ה-glibc הרלוונטי (one_gadget libc.so.6), הוא מחזיר רשימה של כתובות אפשריות עם התנאים שלהן. הצעד הבא הוא לבדוק אילו תנאים אתם יכולים לספק בקלות בהתחשב במצב הרגיסטרים והסטאק בנקודת הניצול שלכם. לפעמים צריך לנסות כמה כתובות עד שמוצאים אחת שהתנאים שלה מתקיימים בפועל.
למה הוא הפך כל כך פופולרי בעולם ה-CTF¶
one_gadget הופך ניצול שהיה דורש חישוב זהיר של ארגומנטים לפעולה כמעט מכנית - דלפו כתובת בתוך glibc, חשבו את הבסיס, הריצו את הכלי, בחרו כתובת עם תנאים סבירים, וקפצו אליה. זה חוסך שלב שלם של הרכבת שרשרת ROP מלאה רק בשביל להעביר ארגומנט אחד לפונקציה.
חשוב לזכור שהוא לא תחליף להבנה - הוא כלי שמחליף חלק מהעבודה הידנית, אבל צריך עדיין להבין למה הוא עובד, מה זה glibc offset, ואיך בכלל הגעתם למצב שבו יש לכם כתובת בסיס של glibc.
איך זה מתחבר לשאר עולם ה-ROP¶
one_gadget הוא דוגמה טובה לכך שכלים אוטומטיים לא מבטלים את הצורך בהבנה - הם רק מייעלים שלב שכבר יודעים איך לעשות ידנית. בדיוק כמו כלים לאיתור gadgets בשרשרת ROP רגילה, one_gadget מייעל תהליך, אבל מי שמבין מה קורה מתחתיו יוכל תמיד לדבג כשמשהו לא עובד כמצופה.
בקורס מחקר חולשות אנחנו מלמדים קודם את הדרך הידנית של ret2libc, ורק אחר כך מציגים כלים כמו one_gadget שמזרזים את התהליך, כדי שהבנה תמיד תקדים אוטומציה.
לא הצלחתם להפעיל אף gadget מהרשימה שהכלי החזיר? כנראה שהתנאים לא מתקיימים בדיוק כמו שחשבתם, ובדיסקורד שלנו יש אנשים שישמחו לעבור על זה איתכם.
לסיכום¶
one_gadget הוא דוגמה מצוינת לכלי שנולד מתוך הבנה עמוקה של קוד קיים - חוקרים גילו שבתוך glibc כבר יושבות כתובות שמריצות shell ישירות, ובנו כלי שמוצא אותן ומסביר בדיוק מתי הן עובדות. הוא לא קסם, הוא תוצאה של הבנה טובה של הבינארי שאתם מתמודדים איתו.