לדלג לתוכן

9.4 מפת גישה לאתגרים הקשים תרגול

תרגול - מפת גישה לאתגרים הקשים

בתרגול הזה לא נפצח אתגר בודד מקצה לקצה - נתרגל את המיומנות שמאפשרת לפצח את כולם: טריאז' וכתיבת תוכנית ניתוח. תלמדו לקחת אתגר קשה מ-pwnable.kr (Grotesque או Hacker's Secret), לסווג אותו למשפחת טכניקה, ולכתוב תוכנית עבודה מסודרת - כלים, השערות, וצעד ראשון. בנוסף תקימו שני בינארי הדגמה מקומיים ותפתרו אותם באמת עם angr, כדי שהכלי החדש של הפרק יהיה בידיים ולא רק בראש.

האתגרים הרלוונטיים ב-pwnable.kr הם כל אתגרי Grotesque ו-Hacker's Secret. מתחברים כרגיל:

ssh <שם-האתגר>@pwnable.kr -p2222

חלק מהאתגרים האלה הם שירות רשת או דורשים סביבה מיוחדת - קראו את עמוד האתגר באתר. אל תריצו fuzzing או עומס על השרת: כל העבודה הכבדה נעשית מקומית, השרת הוא רק היעד.

תרגיל 1 - טריאז' מהריקון

לפני שבונים תוכנית, ממיינים. נניח שקיבלתם אתגר לא מוכר ואלה תוצאות הריקון:

$ file ./chall
./chall: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked

$ strings -n 6 ./chall | grep -i -E 'win|maze|move|lose|flag'
Left or Right?
You lose
Correct! flag:
  1. לאיזו משפחת טכניקה תמיינו את האתגר הזה? נמקו משתי ראיות שונות מהפלט.
  2. איזה פרק בקורס מכסה אותו, ואיזה כלי ריצה/ניפוי תצטרכו?
  3. כתבו את הצעד הראשון הקונקרטי (הפקודה הראשונה שתריצו אחרי הריקון).

רמז: יש כאן שני איתותים נפרדים - אחד בשורת ה-file ואחד בשורות ה-strings. כל אחד לבדו כבר מכריע חלק מהסיווג.

תרגיל 2 - תוכנית ניתוח לפי תבנית

בחרו שניים או שלושה אתגרים מהרשימה (מומלץ אחד מכל טיפוס: אחד סימבולי כמו maze/hunter, אחד kernel/CVE כמו rootkit/towelroot/exynos, ואחד אמולטור כמו softmmu). לכל אחד, כתבו תוכנית ניתוח בכתב לפי התבנית הבאה:

שם האתגר:
קטגוריה (Grotesque / Hacker's Secret):
משפחת טכניקה (מהמיון בהרצאה):
פרק בקורס שמכסה:
הגנות/סביבה משוערות (מה תבדקו ב-checksec / איזו סביבה):
השערת הבאג (מה אתם מנחשים שהחולשה, לפני שראיתם קוד):
שלושת הצעדים הראשונים (פקודות קונקרטיות):
הכלים שתוציאו מהארגז:
מהו תנאי ה"ניצחון" (מה מוכיח שפתרתם):

המטרה היא לא לפתור עדיין - אלא לחשוב כמו חוקר לפני שנוגעים במקלדת. תוכנית טובה חוסכת שעות.

רמז: אם אתם תקועים על "השערת הבאג" לפני שראיתם קוד, הישענו על המיון מההרצאה. אמולטור? הבאג כמעט תמיד בתרגום הכתובת. אתגר CVE? הבאג כבר מתועד - חפשו אותו.

תרגיל 3 - להריץ angr על מבוך אמיתי

הגיע הזמן להוציא את angr לפועל. קודם התקינו (pip install angr), ואז בנו בינארי מבוך מקומי:

// maze.c
#include <stdio.h>
#include <string.h>

int main() {
    char moves[9];
    int x = 0, y = 0;
    puts("Navigate the maze (8 moves: u/d/l/r):");
    if (read(0, moves, 8) < 0) return 1;
    moves[8] = 0;

    for (int i = 0; i < 8; i++) {
        char m = moves[i];
        if (m == 'u') y--;
        else if (m == 'd') y++;
        else if (m == 'l') x--;
        else if (m == 'r') x++;
        else { puts("You lose"); return 0; }
        // walls: going outside the board -> lose
        if (x < 0 || x > 3 || y < 0 || y > 3) { puts("You lose"); return 0; }
    }
    if (x == 3 && y == 3) puts("You win");
    else puts("You lose");
    return 0;
}
gcc -fno-stack-protector -no-pie -o maze maze.c
  1. מצאו בדיסאסמבלי את כתובת ה-call ל-puts של "You win" ושל "You lose". רמז: objdump -d ./maze | grep -B2 -A2 puts, והצליבו עם כתובות המחרוזות ב-.rodata.
  2. כתבו סקריפט angr שמשתמש ב-explore(find=WIN, avoid=LOSE) ומדפיס את הקלט הפותר.
  3. הזינו את הקלט שקיבלתם ל-./maze וודאו שהוא מדפיס "You win".

רמז: ה-find וה-avoid מקבלים את הכתובת של ה-call (או השורה שאחריו), לא של המחרוזת עצמה. את הקלט מחלצים עם state.posix.dumps(0).

תרגיל 4 - שלד AEG על overflow פשוטה

עכשיו נראה איך angr מוצא לבד את ה-offset ובונה payload. בנו בינארי vulnerable:

// vuln.c
#include <stdio.h>
void win() { system("/bin/sh"); }
int main() { char buf[32]; gets(buf); return 0; }
gcc -fno-stack-protector -no-pie -o vuln vuln.c
  1. מצאו את כתובת win (nm ./vuln | grep win).
  2. כתבו סקריפט angr שמריץ עם stdin סימבולי, שומר מצבים בלתי מאולצים (save_unconstrained=True), מריץ עד שיש מצב עם pc סימבולי, מאלץ pc == win, ומחלץ את הקלט ל-payload.bin.
  3. הזינו את הpayload לבינארי (./vuln < payload.bin בהרצה אינטראקטיבית, או דרך pwntools) וודאו שאתם מגיעים ל-win.

רמז: אזהרת ה-gets בזמן הקמפול היא בדיוק הבאג שאתם מנצלים. אם angr לא מוצא מצב בלתי מאולץ, הגדילו את אורך ה-stdin הסימבולי (למשל 8 * 100 ביט) כדי לוודא שהoverflow מגיעה ל-rip.

תרגיל 5 - מחקר CVE בכתב

בחרו את towelroot או את exynos, וכתבו דף מחקר קצר (בלי לממש - רק מחקר):

  1. מהו ה-CVE המדויק, ובאיזו שנה פורסם?
  2. מהו הפרימיטיב שהחולשה נותנת (מה בדיוק אפשר לקרוא/לכתוב)?
  3. מהם שלושת השלבים העיקריים בchain מ-userland ל-root?
  4. מהו האילוץ הסביבתי (איזו גרסת kernel/מכשיר), ולמה הוא חשוב לשחזור?

רמז: התחילו מ-NVD (חיפוש מספר ה-CVE) ומהכתיבה המקורית של החוקר. עבור towelroot חפשו את FUTEX_REQUEUE; עבור exynos חפשו את /dev/exynos-mem.

תרגיל 6 (בונוס) - הגבולות של angr

קחו את בינארי ה-maze מתרגיל 3 והגדילו אותו ל-40 מהלכים ולוח 10x10. הריצו שוב את סקריפט ה-angr.

  1. כמה זמן זה לוקח עכשיו? האם הוא מסיים בכלל?
  2. מה קורה ל-angr כשמספר המסלולים גדל (state explosion)? הסבירו במילים שלכם.
  3. הציעו רעיון אחד לצמצום מרחב החיפוש (למשל: לחתוך מסלולים עם avoid נדיב יותר, או להתחיל את החקירה ממצב קרוב יותר ליעד).

רמז: הרצה סימבולית עולה אקספוננציאלית עם ההסתעפות. זו בדיוק הסיבה שבאתגרים גדולים משלבים ניתוח ידני עם angr במקום לזרוק עליו את כל הבינארי.