0.1 מה זה מחקר חולשות הרצאה
ברוכים הבאים לקורס מחקר חולשות. בקורס הקודם, ליבת-המחשב, למדתם איך המכונה עובדת מבפנים - אסמבלי, שפת C, המחסנית, מבנה קובץ ELF, ואיך לנתח תוכנית ב-GDB. עכשיו אנחנו הופכים את נקודת המבט. עד היום המטרה הייתה לכתוב תוכנית שעובדת נכון. מהיום המטרה היא למצוא את הנקודה שבה תוכנית עושה משהו שהמתכנת שכתב אותה מעולם לא התכוון שיקרה - ולהוכיח שאפשר לנצל את זה. זה הלב של מחקר חולשות.
בהרצאה הזו נבנה את התמונה הגדולה: מה זה בעצם התחום הזה, איך חושבים בו, אילו סוגי חולשות קיימים, איך חולשה עוברת מרגע הגילוי ועד התיקון, ומה ההבדל בין באג לחולשה לאקספלויט. נדבר גם על קריירה, על אתיקה ועל החוק - כי הכוח הזה מגיע עם אחריות. בסוף נפרוש את מפת הדרכים של כל הקורס.
מה זה מחקר חולשות - Vulnerability Research¶
מחקר חולשות הוא הדיסציפלינה של מציאה, הבנה והוכחה של פגמי אבטחה בתוכנה ובחומרה. אנחנו לוקחים תוכנית - לפעמים עם קוד המקור שלה, לרוב רק כבינארי - ושואלים שאלה אחת פשוטה: איך אני גורם לה לעשות משהו שהיא לא אמורה לעשות. "משהו שהיא לא אמורה" זה טווח רחב: להריץ קוד שלנו, לקרוא סוד שלא שייך לנו, לעקוף בדיקת הרשאות, או להפיל שירות שאמור לרוץ.
תת-התחום שבו נתמקד נקרא ניצול בינארי - binary exploitation, או בקיצור pwn. כאן העניין הוא קוד מקומפל שרץ ישירות על המעבד, ולכן כל מה שלמדתם על אוגרים, מחסנית וזיכרון הופך פתאום לכלי עבודה יומיומי.
חשוב להגיד את זה כבר עכשיו, בדיוק כמו בהרצאת המבוא של הקורס הקודם: הידע הזה הוא כלי. אנחנו לומדים טכניקות התקפה כדי להבין עומק, לבנות הגנות טובות יותר, ולמצוא באגים לפני שהרעים ימצאו אותם. זה תחום לגיטימי לחלוטין שמפרנס אנשים - חוקרי אבטחה בחברות, צוותים אדומים, ציידי באגים בתוכניות bug bounty, וחוקרים באקדמיה ובתעשייה.
באג, חולשה ואקספלויט - bug, vulnerability, exploit¶
שלוש המילים האלה נשמעות דומה, ואנשים מבלבלים ביניהן כל הזמן. ההבחנה ביניהן היא הבסיס לכל מה שנעשה בקורס, אז בואו נחדד אותה.
- באג - bug: כל התנהגות של התוכנית שסוטה ממה שהמתכנת התכוון אליו. כפתור שמציג טקסט שגוי הוא באג. חישוב שמחזיר תוצאה לא נכונה הוא באג. רוב הבאגים בעולם לגמרי לא מעניינים מבחינה בטחונית.
- חולשה - vulnerability: באג שתוקף יכול לנצל כדי לחצות גבול אבטחה. כלומר, באג עם השלכה - להשיג הרצת קוד, לדלוף מידע, להעלות הרשאות, או לגרום למניעת שירות. כל חולשה היא באג, אבל לא כל באג הוא חולשה.
- אקספלויט - exploit: הקלט או התוכנית הקונקרטיים שהופכים את החולשה מרעיון תיאורטי לנזק אמיתי. זו ההוכחה. הבדל חשוב: יש הבדל בין הוכחת היתכנות - PoC, שרק מדגימה שהבאג קיים (למשל קריסה), לבין אקספלויט ממשוקלל - weaponized, שהוא אמין, עוקף הגנות, ופותח לכם shell בסוף.
בואו נראה את זה בקוד. הנה באג שהוא גם חולשה:
#include <stdio.h>
void login() {
char name[64];
gets(name); // reads without checking the length - a classic vulnerability
printf("Hello, %s\n", name);
}
הפונקציה gets לא בודקת כמה בתים נכנסים. אם המשתמש מקליד 200 תווים, 136 מהם ידרסו את מה שמעל name על המחסנית, אם תזכרו מהקורס ליבת המחשב, בstack frame של פונקציה, מעל המשתנים הלוקלים של הפונקציה- יש את הreturn address שלה- תוקף יכול לנצל את החולשה הזו כדי לדרוס את הreturn address של הפונקציה לערך שרירותי שהוא בוחר.
זה לא סתם באג. זו חולשה, כי תוקף יכול להשתמש בה כדי לשלוט במה שהתוכנה עושה.
ולעומת זה, הנה באג שהוא לא חולשה:
הפונקציה מחזירה תוצאה שגויה, וזה מעצבן, אבל אין כאן שום גבול אבטחה שנחצה. אף תוקף לא ישיג מזה כלום. באג - כן. חולשה - לא.
הלך הרוח של מנצל חולשות - the exploitation mindset¶
אם יש דבר אחד שאני רוצה שתיקחו מההרצאה הזו, זה הלך הרוח. תוכנה נשענת על ערמה של הנחות שהמתכנת לקח כמובנות מאליהן - נקרא להן שמורות - invariants. למשל:
- "הקלט אף פעם לא יהיה ארוך מ-64 בתים."
- "האורך תמיד חיובי."
- "המצביע הזה עדיין תקף אחרי ה-free."
- "המשתמש לא ישלח את השדה הזה פעמיים."
- "הקובץ הזה תמיד קיים ותמיד שלנו."
המתכנת בנה את התוכנית מתוך אמונה שההנחות האלה נכונות. העבודה שלנו היא לשאול על כל הנחה שאלה אחת: מי בדיוק אוכף אותה? אם התשובה היא "כלום, זה פשוט תמיד ככה" - מצאנו נקודת כניסה.
תסתכלו על הקוד הבא. המתכנת חשב שהוא בטוח:
void copy_data(char *src, int len) {
char buf[64];
if (len > 64) return; // looks like a valid bounds check
memcpy(buf, src, len);
}
השמורה שהמתכנת הניח: "len קטן או שווה ל-64, אז memcpy בטוח". אבל len הוא int חתום. מה קורה אם התוקף שולח len = -1? הבדיקה len > 64 נכשלת (מינוס אחד קטן מ-64), אז אנחנו ממשיכים ל-memcpy. אבל הפרמטר השלישי של memcpy הוא size_t - טיפוס לא חתום. מינוס אחד הופך שם למספר ענק, וה-memcpy מנסה להעתיק בערך ארבעה מיליארד בתים לתוך buffer של 64. הoverflow עצומה. השמורה נשברה כי אף אחד לא אכף שהאורך יהיה חיובי.
זה הכל. זה כל המשחק. במקום לשאול "מה התוכנית עושה", אנחנו שואלים "מה התוכנית מניחה, ומה קורה כשההנחה לא נכונה".
סוגי באגים - classes of bugs¶
חולשות מתחלקות לשתי משפחות גדולות. חשוב להכיר את שתיהן כי הן דורשות הלך רוח קצת שונה.
קורפשן זיכרון - memory corruption¶
כאן התוכנית כותבת או קוראת זיכרון מחוץ למקום שהיה אמור. אלה הכוכבות של pwn, ורוב הקורס עוסק בהן:
- גלישת חוצץ במחסנית - stack buffer overflow: כתיבה מעבר לbuffer שיושב על המחסנית (הסטאק), שדורסת את saved rbp ואת הreturn address. זו החולשה הקלאסית שראיתם למעלה. - אם זה נשמע לכם סינית, עליכם לחזור על הפרקים שמדברים על הstack frame של פונקציות בקורס ליבת המחשב.
- גלישת חוצץ בערמה - heap overflow: אותו רעיון, אבל הbuffer הוקצה ב-heap עם malloc. הoverflow דורסת נתונים או metadata של המקצה. (כלומר, דריסה של מידע באלוקטור שמספק לנו את הפונקציה malloc שאנחנו לא אמורים לדרוס)
- שימוש לאחר שחרור - Use-After-Free: שימוש במצביע אחרי שכבר קראנו עליו free. הזיכרון כבר לא שלנו, ואם הוקצה מחדש למשהו אחר - יש בעיה.
- חולשת מחרוזת פורמט - format string: כשהמשתמש שולט במחרוזת הformat של printf. עם
%xקוראים מהמחסנית, ועם%nאפילו כותבים לזיכרון. - גלישת מספר שלם - integer overflow: כשחישוב מספרי גולש מעבר לטווח הטיפוס. לרוב הוא לא הבעיה בעצמו, אלא מוליד בעיה אחרת. למשל:
int n = read_count(); // the attacker controls n
char *arr = malloc(n * sizeof(item)); // n * 16 can overflow past 2^32
// ... the loop writes n items, but the allocation is too small -> heap overflow
אם n גדול מספיק, המכפלה n * 16 גולשת ומתגלגלת למספר קטן. malloc מקצה buffer זעיר, ואז הלולאה כותבת לתוכו יותר בתים ממה שהוקצה. הoverflow מספר שלם שהפכה לoverflow heap.
באגים לוגיים - logic bugs¶
כאן אין שום קרופשן זיכרון. התוכנית עושה בדיוק מה שהיא קודדה לעשות - פשוט הלוגיקה עצמה שגויה מבחינה בטחונית:
- עקיפת הרשאות - authorization bypass: השוואה שגויה שמאפשרת כניסה בלי סיסמה נכונה.
- אקראיות צפויה - predictable randomness: שימוש ב-rand עם seed קבוע, כך שהתוקף מנחש את ה"אקראי".
- הזרקת פקודות - command injection: העברת קלט משתמש ישירות ל-system.
- מרוץ תנאים - race condition: חלון זמן בין בדיקה לשימוש (TOCTOU - time of check and time of use) שתוקף מנצל.
באגים לוגיים לא דורשים ידע עמוק באסמבלי, אבל הם נפוצים מאוד באתגרים אמיתיים ובקוד ייצור. נתחיל דווקא מהם בפרק 1, כי הם דרך מצוינת להתרגל להלך הרוח בלי המורכבות של דריסת זיכרון.
מחזור החיים של חולשה - the vulnerability lifecycle¶
חולשה לא נולדת כאקספלויט מוכן. היא עוברת מסע. הכרת המסע הזה עוזרת להבין איפה בדיוק אנחנו יושבים בכל שלב של הקורס:
discovery --> triage --> root-cause --> exploit dev --> disclosure --> patch
discovery triage root-cause exploit dev disclosure patch
- גילוי - discovery: מוצאים משהו חשוד. יכול להיות סקירת קוד ידנית, fuzzing שהפיל את התוכנית, או ניתוח בינארי.
- מיון - triage: האם הקריסה הזו בכלל מעניינת? האם היא ניתנת לexploit או שזה סתם באג שמפיל? כאן מפרידים את הזהב מהחצץ.
- ניתוח שורש - root-cause analysis: מבינים בדיוק למה זה קורה. איזו שמורה נשברה, ואיפה בקוד. בלי זה אי אפשר לבנות אקספלויט אמין.
- פיתוח אקספלויט - exploit development: הופכים את ההבנה לקוד שמשיג אימפקט אמיתי, תוך עקיפת ההגנות שפעילות (NX, ASLR, PIE, canary).
- גילוי אחראי - disclosure: מדווחים ליצרן ונותנים לו זמן לתקן לפני פרסום פומבי.
- תיקון - patch: היצרן מוציא עדכון, והחולשה נסגרת.
מושג חשוב שנחזור אליו: חולשת יום-אפס - 0-day היא חולשה שהיצרן עדיין לא יודע עליה, ואין לה תיקון. ברגע שיצא תיקון, היא הופכת לחולשת יום-N - n-day. בפרק 10 ממש נכתוב אקספלויט ל-CVE אמיתי שכבר תוקן - זו דוגמה מובהקת לעבודת n-day.
התקן: CVE, CWE ו-CVSS¶
בתעשייה יש שפה משותפת לתיאור חולשות. שלושה ראשי תיבות שחוזרים כל הזמן, ואנשים מבלבלים ביניהם. בואו נסדר:
- מזהה חולשה - CVE - Common Vulnerabilities and Exposures: מזהה ייחודי לחולשה ספציפית במוצר ספציפי. הפורמט הוא
CVE-YYYY-NNNN. הדוגמה המפורסמת ביותר, CVE-2014-0160, ידועה בכינוי Heartbleed - קריאה מעבר לגבול בספריית OpenSSL. דוגמה נוספת, CVE-2021-3156, היא גלישת heap ב-sudo שכונתה Baron Samedit. CVE עונה על השאלה "איזו חולשה בדיוק". - סיווג חולשה - CWE - Common Weakness Enumeration: קטלוג של סוגי חולשות - הקטגוריות, לא מקרים ספציפיים. למשל CWE-121 הוא stack buffer overflow, CWE-416 הוא Use-After-Free, CWE-134 הוא format string, ו-CWE-190 הוא integer overflow. CWE עונה על השאלה "איזה סוג של חולשה זו".
- ציון חומרה - CVSS - Common Vulnerability Scoring System: ציון מספרי בין 0 ל-10 שמתאר כמה החולשה חמורה. הגרסה הנוכחית היא 4.0, וקודמתה 3.1 עדיין נפוצה מאוד. הטווחים: 0.0 ללא, 0.1-3.9 נמוך, 4.0-6.9 בינוני, 7.0-8.9 גבוה, ו-9.0-10.0 קריטי. CVSS עונה על השאלה "כמה זה חמור".
הנה סיכום ההבחנה, כי היא חוזרת בכל דוח אבטחה שתקראו:
| ראשי תיבות | מה זה | על מה עונה | דוגמה |
|---|---|---|---|
| CVE | מזהה חולשה ספציפית במוצר | איזו חולשה בדיוק | CVE-2014-0160 (Heartbleed) |
| CWE | סוג/מחלקת חולשה | איזה סוג של חולשה | CWE-416 (Use-After-Free) |
| CVSS | ציון חומרה 0-10 | כמה זה חמור | 9.8 (קריטי) |
את MITRE, הארגון שמתחזק גם את CVE וגם את CWE, תפגשו הרבה. שווה כבר עכשיו להיכנס פעם אחת ל-cve.org ולראות איך נראית רשומה אמיתית.
מסלולי קריירה - career paths¶
לאן כל זה מוביל בפועל? התחום פתוח לכמה מסלולים, שכולם חולקים את אותה ליבה טכנית:
- מפתח אקספלויטים - exploit developer: כותב אקספלויטים אמינים, לרוב אצל יצרני כלי אבטחה, גופים ממשלתיים, או חברות מחקר התקפי. זה המקצוע שהקורס הזה מכשיר אליכם ישירות.
- לוחם צוות אדום - red team operator: מדמה תוקף אמיתי מול ארגון, ומשלב חולשות בינאריות עם טכניקות רשת וחברתיות.
- ציד באגים בינאריים - binary bug bounty: מוצא חולשות במוצרים אמיתיים ומקבל תשלום עבורן, דרך תוכניות רשמיות של יצרנים.
- חוקר אבטחה - security researcher: חוקר, מפרסם מאמרים, מציג בכנסים, ודוחף את גבול הידע קדימה - באקדמיה או בתעשייה.
- שחקן CTF בקטגוריית pwn: מתחרה באתגרי exploit. זה אולי לא "עבודה", אבל זה מגרש האימונים המרכזי של התחום, והדרך הכי מהירה לבנות שריר. הרבה מהאנשים הכי טובים בתעשייה הגיעו משם.
המסלולים האלה חופפים. אותו אדם יכול לשחק CTF בסופי שבוע, לצוד באגים בערבים, ולעבוד כמפתח אקספלויטים ביום. הבסיס הטכני זהה, וזה בדיוק מה שנבנה כאן.
אתיקה וחוק - ethics and law¶
עכשיו החלק הכי חשוב בהרצאה, ואני רוצה שתקראו אותו לאט. הידע שתרכשו כאן חזק, והוא מגיע עם גבול חד וברור.
הכלל הזהב: בודקים אך ורק מערכות שבבעלותכם, או שקיבלתם עליהן הרשאה מפורשת ובכתב. אין אמצע. אין "רק הצצתי". אין "לא גרמתי נזק".
גישה לא מורשית למחשב היא עבירה פלילית. בישראל זה מעוגן בחוק המחשבים, ובארצות הברית בחוק CFAA. חשוב להפנים: עצם הגישה או החדירה למערכת בלי רשות היא עבירה, גם אם לא מחקתם כלום, גם אם לא גנבתם כלום, וגם אם רק "רציתם לבדוק אם זה עובד". הכוונה לא מגינה עליכם. הרשות היא מה שקובע.
כשמוצאים חולשה במערכת של מישהו אחר - למשל בתוכנית bug bounty - נוהגים לפי גילוי אחראי - responsible disclosure, שנקרא גם גילוי מתואם. הרעיון: מדווחים ליצרן בפרטיות, נותנים לו חלון זמן סביר לתקן, ורק אחר כך מפרסמים בפומבי. ככה החולשה נסגרת לפני שנעשה בה שימוש לרעה. נרחיב על זה בפרק 10.
אז איפה מותר לתרגל בלי לדאוג? בסביבות שנבנו בדיוק בשביל זה:
- המכונה הווירטואלית שלכם, בינארים שאתם מקמפלים בעצמכם.
- אתגרי CTF ופלטפורמות אימון חוקיות.
- הפלטפורמה pwnable.kr - זירת אימונים חוקית ומפורסמת שבנויה כדי שתתקפו אותה. לאורך הקורס נשתמש בה הרבה, ובפרק 0.5 נתחבר אליה לראשונה.
הכלל הפשוט לזכור: אם זה לא שלכם ולא נתנו לכם רשות מפורשת - לא נוגעים. נקודה.
סיכום¶
בהרצאה הזו בנינו את התשתית המחשבתית לכל הקורס:
- מחקר חולשות הוא מציאה, הבנה והוכחה של פגמי אבטחה. תת-התחום שלנו הוא ניצול בינארי - pwn.
- באג הוא סטייה מהתנהגות מכוונת, חולשה היא באג שאפשר לנצל לחציית גבול אבטחה, ואקספלויט הוא ההוכחה הקונקרטית שמפעילה אותה.
- הלך הרוח: כל תוכנית נשענת על שמורות והנחות. העבודה שלנו היא למצוא הנחה שאף אחד לא אוכף, ולשבור אותה.
- סוגי באגים: קורפשן זיכרון (גלישות מחסנית ו-heap, Use-After-Free, format string, integer overflow) מול באגים לוגיים.
- מחזור החיים של חולשה: גילוי, מיון, ניתוח שורש, פיתוח אקספלויט, גילוי אחראי, תיקון. הבחנו בין 0-day ל-n-day.
- המזהה CVE מציין חולשה ספציפית, CWE הוא סוג החולשה, ו-CVSS הוא ציון החומרה מ-0 עד 10.
- מסלולי קריירה: מפתח אקספלויטים, צוות אדום, ציד באגים בינאריים, חוקר אבטחה, ושחקן CTF.
- אתיקה וחוק: בודקים רק מה ששלכם או שהורשיתם עליו במפורש. גישה לא מורשית היא עבירה פלילית. מתרגלים בזירות חוקיות כמו pwnable.kr.
בהרצאה הבאה נלכלך את הידיים ונקים את סביבת העבודה. מכאן זה נעשה מעשי מאוד.