לדלג לתוכן

מבוא ל-Fuzzing - איך מוצאים חולשות בלי לקרוא קוד בכלל

אחד הדברים שהכי מפתיעים מתחילים בעולם מחקר החולשות הוא שהרבה חולשות אמיתיות, כולל כאלה בתוכנות ענקיות כמו דפדפנים ומערכות הפעלה, לא נמצאות על ידי מישהו שקורא כל שורת קוד בעיון. הן נמצאות על ידי מחשב שמזין לתוכנה כמויות אדירות של קלט אקראי ומוזר, עד שמשהו נשבר. הטכניקה הזאת נקראת Fuzzing, וזה אחד הכלים החזקים בארסנל של חוקר חולשות.

הרעיון הבסיסי

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

קריסה כזאת, שנקראת crash, היא לא סוף הסיפור אלא ההתחלה. היא סימן שמשהו לא צפוי קרה בתוכנית - ובשביל חוקר חולשות, "לא צפוי" זה בדיוק המקום שבו כדאי לחפש. חלק מהקריסות יהיו לא מעניינות (למשל, קלט לא תקין שנדחה בצורה מסודרת), אבל חלק אחר יכולות להצביע על גלישת חוצץ, use-after-free, או חולשה אמיתית אחרת שאפשר לנצל.

שני סוגי Fuzzing עיקריים

  • Black-box Fuzzing. הכלי לא יודע כלום על המבנה הפנימי של התוכנה. הוא פשוט זורק קלט אקראי ובודק אם היא קורסת. פשוט להריץ, אבל פחות יעיל - הרבה מהקלט האקראי לא מגיע אפילו לחלקים המעניינים בקוד.
  • Coverage-guided Fuzzing. גישה חכמה בהרבה, שבה הכלי עוקב אחרי אילו חלקים בקוד כבר "נבדקו" (coverage), ומתעדף באופן חכם קלטים חדשים שמגיעים לנתיבים לא מוכרים בתוכנית. כלים כמו AFL ו-AFL++ עובדים כך, והם אלה ששינו את המשחק בעולם ה-fuzzing בעשור האחרון.

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

למה זה כל כך אפקטיבי

הכוח האמיתי של fuzzing הוא בקנה המידה. מחשב יכול לבדוק מיליוני קלטים בשעה, במהירות שאף בן אדם לא מסוגל להתקרב אליה. גם אם רק אחד מכל עשרה מיליון קלטים חושף באג, מחשב פשוט ירוץ מספיק זמן כדי למצוא אותו. זו הסיבה שחברות ענק כמו גוגל מריצות fuzzing באופן רציף על קוד פתוח קריטי (הפרויקט OSS-Fuzz שלהם הוא דוגמה מוכרת), ומוצאות כך אלפי חולשות בכל שנה, הרבה לפני שהן מגיעות לניצול בזדון.

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

מה עושים אחרי שמוצאים קריסה

מציאת קריסה היא רק חצי מהעבודה. השלב הבא, ולפעמים המורכב יותר, הוא לנתח את הקריסה - מה בדיוק גרם לה, האם היא נשלטת (כלומר, האם תוקף יכול לשלוט על התנהגות התוכנית באמצעותה), והאם היא ניתנת לניצול בפועל או שהיא רק "רעש". כאן בדיוק נכנסים לתמונה כלים כמו GDB וידע בהנדסה לאחור - fuzzing מוצא לכם את הדלת, אבל אתם עדיין צריכים לפתוח אותה.

איך מתחילים עם Fuzzing בפועל

הכלי הכי מוכר ונגיש למתחילים הוא AFL++, שרץ על תוכניות רגילות בלינוקס ודורש בעיקר קימפול מחדש של התוכנית עם תמיכה ב-fuzzing. זה נושא שכדאי לגשת אליו אחרי שכבר יש לכם בסיס בהבנת זיכרון, C, וקריאת קריסות עם דיבאגר - אחרת קשה להבין מה בכלל הכלי מצא לכם.

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

יש לכם שאלות על הקמת סביבת fuzzing ראשונה? זה בדיוק סוג הדיון שקורה כל הזמן בדיסקורד שלנו.

הצטרפו לקהילה בדיסקורד

לסיכום

Fuzzing הופך את מציאת החולשות ממלאכת יד איטית לתהליך אוטומטי בקנה מידה עצום. זה לא מחליף את ההבנה העמוקה שלכם בזיכרון ובקוד - זה כלי שמכוון אתכם לאן להסתכל. השילוב בין fuzzing חכם להבנה עמוקה של מה שנמצא מאחורי הקריסה הוא בדיוק מה שהופך חוקר חולשות מתחיל למקצועי.