0.2 סביבת העבודה פתרון
פתרון - הקמת סביבת העבודה¶
כאן נעבור על כל תרגיל עם הפקודות המדויקות, הפלט הצפוי, ופתרון לתקלות הנפוצות. כל הפלטים הם לדוגמה מ-Ubuntu 22.04 עם glibc 2.35 - אצלכם המספרים המדויקים (כתובות, offsets, גרסאות) עשויים להשתנות, וזה בסדר גמור.
פתרון תרגיל 1 - התקנת הבסיס והמהדר¶
sudo apt update
sudo apt install -y build-essential gdb gcc-multilib git python3 python3-pip python3-venv
gcc --version
gdb --version
הידור והרצה של קובץ ההדגמה:
gcc -o demo demo.c
# gcc will warn: warning: implicit declaration of function 'gets' - this is expected and fine
echo "hello" | ./demo
פלט צפוי:
תקלות נפוצות:
- אם
gcc: command not found- החבילהbuild-essentialלא הותקנה. הריצו שוב את שורת ה-apt. - אם אין לכם הרשאת sudo - צריך משתמש עם הרשאות מנהל. במכונה וירטואלית שהקמתם, המשתמש שיצרתם בהתקנה בדרך כלל בקבוצת sudo.
למה זה עבד: build-essential מביא את gcc ואת כותרות ה-libc, ו-gets עדיין קיים ב-libc (למרות שהוא הוסר מהתקן C11) אז הבינארי מתקמפל ורץ.
פתרון תרגיל 2 - התקנת pwntools ותוסף ה-gdb¶
התקנת pwntools בתוך סביבה וירטואלית:
python3 -m venv ~/pwn-venv
source ~/pwn-venv/bin/activate
pip install --upgrade pip
pip install pwntools
pwn --version
התקנת pwndbg:
בדיקה שהתוסף נטען:
בתוך gdb:
pwndbg> checksec
File: /home/user/work/demo
Arch: amd64
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
pwndbg> quit
עצם ה-prompt שהשתנה ל-pwndbg> הוא הסימן הראשון שהתוסף חי. אם אתם רואים checksec נותן טבלה - מצוין.
תקלות נפוצות:
- אם ה-prompt נשאר
(gdb)ו-checksecנותן "Undefined command" - התוסף לא נטען. בדקו שקובץ~/.gdbinitמכיל שורה שטוענת את pwndbg, ופתחו gdb מחדש. - אם התקנתם גם pwndbg וגם GEF - הם מתנגשים ב-
~/.gdbinit. השאירו רק אחד. - אם
pip install pwntoolsנכשל על ספריות מערכת חסרות, התקינו:sudo apt install -y python3-dev libffi-dev build-essential.
למה זה עבד: setup.sh מוסיף ל-~/.gdbinit שורת source שטוענת את pwndbg בכל הפעלת gdb, אז מאותו רגע כל gdb הוא gdb משודרג.
פתרון תרגיל 3 - הידור בכמה תצורות וקריאת checksec¶
הידור ארבע התצורות:
gcc -o demo_default demo.c
gcc -fno-stack-protector -o demo_nocanary demo.c
gcc -fno-stack-protector -z execstack -o demo_nonx demo.c
gcc -fno-stack-protector -no-pie -static -o demo_nopie_static demo.c
קריאת ה-checksec לכל אחד:
for f in demo_default demo_nocanary demo_nonx demo_nopie_static; do
echo "=== $f ==="; checksec --file=$f
done
פלט צפוי (מקוצר לשדות המעניינים):
=== demo_default ===
RELRO: Full RELRO Canary: found NX: enabled PIE: PIE enabled
=== demo_nocanary ===
RELRO: Full RELRO Canary: No canary NX: enabled PIE: PIE enabled
=== demo_nonx ===
RELRO: Full RELRO Canary: No canary NX: disabled PIE: PIE enabled
=== demo_nopie_static ===
RELRO: Partial Canary: No canary NX: enabled PIE: No PIE
תשובות לשאלות:
- הדגל
-fno-stack-protectorהוא זה שגרם ל-Stack Canary להיעלם. בלעדיו gcc מדליק canary אוטומטית על פונקציות עם buffers. - ב-
demo_nonxהדגל-z execstackהפך את המחסנית לניתנת להרצה, אז NX הוא disabled. זה מסוכן כי עכשיו אפשר לכתוב shellcode ישירות על המחסנית ולקפוץ אליו - בדיוק מה שנעשה בפרק ה-shellcode. - הקובץ
demo_nopie_staticגדול בהרבה (מאות KB עד מגה במקום כמה KB), כי-staticמכניס את כל ה-libc לתוך הבינארי במקום לקשר אותו בזמן ריצה. בגלל זה גם יש בו הרבה יותר גאדג'טים, כפי שנראה בתרגיל הבא.
הערה על 32 ביט: אם תרצו לבנות גרסת 32 ביט לתרגול עתידי, הוסיפו -m32:
למה זה עבד: כל דגל מכבה במדויק הגנה אחת, וה-checksec קורא את המטא-דאטה של ה-ELF (למשל את סגמנט GNU_STACK בשביל NX, ואת נוכחות __stack_chk_fail בשביל canary) ומדווח על התוצאה.
פתרון תרגיל 4 - שאר ארגז הכלים¶
התקנה:
pip install ROPgadget ropper
sudo apt install -y ruby ruby-dev ltrace strace
sudo gem install one_gadget
# optional, for foreign architectures:
sudo apt install -y qemu-user qemu-user-static gdb-multiarch
חיפוש גאדג'ט בבינארי הסטטי:
פלט צפוי (הכתובת תשתנה):
הבינארי הסטטי מלא בגאדג'טים כי -static הכניס את כל קוד ה-libc פנימה - אלפי פונקציות, ואיתן אלפי רצפי הוראות שמסתיימים ב-ret. בבינארי דינמי רגיל הגאדג'טים היושבים בקוד שלנו מעטים בהרבה.
הרצת one_gadget על ה-libc של המערכת:
פלט צפוי (offsets ותנאים ישתנו בין גרסאות glibc):
כל שורה היא offset בתוך ה-libc, ומתחתיה התנאים שחייבים להתקיים ברגע הקפיצה כדי שנקבל shell. נלמד לבחור ולעמוד בתנאים האלה בפרק ret2libc.
מעקב עם ltrace:
פלט צפוי:
printf("input> ") = 7
gets(0x7ffe...) = 0x7ffe...
printf("got: %s\n", "hello") = 11
+++ exited (status 0) +++
רואים במפורש את הקריאה ל-gets בלי בדיקת גבולות - בדיוק החולשה שנתקוף.
תקלות נפוצות:
- אם
gem install one_gadgetנכשל בקומפילציה של הרחבות מקומיות - חסרruby-dev. התקינו אותו והריצו שוב. - אם
ROPgadgetאוropperלא נמצאים אחרי pip, ודאו שה-venvפעיל (source ~/pwn-venv/bin/activate) או הוסיפו את~/.local/binל-PATH.
פתרון תרגיל 5 - כיבוי ASLR ובדיקת גרסת glibc¶
cat /proc/sys/kernel/randomize_va_space # usually 2 (full)
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
cat /proc/sys/kernel/randomize_va_space # now 0
בדיקה חזותית שהכתובות התייצבו - הריצו פעמיים ורואים אותה כתובת מחסנית:
גרסת ה-glibc:
רשמו את המספר הזה. כשנעבוד מול אתגר מרוחק שרץ עם glibc אחר, נצטרך patchelf או pwninit כדי להריץ מקומית מול ה-libc של השרת - כמו שהוסבר בהרצאה.
זכרו: אחרי אתחול המכונה ה-ASLR חוזר לערך ברירת המחדל. אם אתם רוצים כיבוי קבוע זה עניין של הגדרת sysctl, אבל לרוב עדיף להשאיר כיבוי זמני רק לזמן הפיתוח.
פתרון אתגר הסיום - הרצה של הכל דרך pwntools¶
שמרו קובץ solve.py:
from pwn import *
context.binary = ELF('./demo_default') # determines amd64 automatically
context.log_level = 'info'
p = process('./demo_default')
p.recvuntil(b'input> ')
p.sendline(b'hello from pwntools')
print(p.recvall(timeout=1).decode())
הרצה (מתוך ה-venv עם pwntools):
פלט צפוי:
[*] '/home/user/work/demo_default'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process './demo_default': pid 12345
got: hello from pwntools
[+] Receiving all data: Done
למה זה עבד: context.binary = ELF(...) קרא מהקובץ שזה בינארי amd64 והדפיס את פרופיל ההגנות שלו, process הפעיל אותו כתת-תהליך, ו-pwntools ניהל את הצינור (stdin/stdout) בשבילנו. זה בדיוק השלד שנרחיב בכל שאר הקורס - רק שבמקום sendline תמים נשלח payload שדורס את הreturn address.
איך להכליל: החליפו process('./x') ב-remote('host', port) וקיבלתם exploit מרוחק. הוסיפו gdb.debug('./x', gdbscript='b vuln') במקום process כדי לפתוח את הבינארי ישירות תחת pwndbg. השלד הזה - ELF, פתיחת תהליך, שליחה, קבלה - הוא הבסיס לכל מה שנעשה מכאן והלאה.
כשכל שלב עבר, צלמו snapshot נקי של המכונה הווירטואלית. אם בהמשך משהו יישבר, תוכלו לחזור לנקודה הזו בשנייה. הסביבה מוכנה - בהרצאה הבאה נחזור על זיכרון התהליך ונצא לדרך.