עקיפת WAF באמצעות קידוד - כשה-WAF וה-שרת קוראים את אותה מחרוזת אחרת¶
יסודות עקיפת WAF כוללים כמה משפחות טכניקות שונות, אבל אחת מהן חוזרת שוב ושוב בכל מבדק חדירה שכולל WAF - קידוד. הפוסט הזה לא עוסק ביסודות הכלליים, אלא צולל ספציפית לתוך משפחת הטכניקות המבוססות קידוד, כדי להבין מה בדיוק קורה שם ברמת הפרוטוקול, ולמה זו אחת הדרכים האמינות ביותר לעקוף מנועי חתימות.
השורש - פער נורמליזציה, לא באג¶
הנקודה הכי חשובה להבין היא שהטכניקות האלה לא מנצלות באג ב-WAF במובן הקלאסי. הן מנצלות עובדה מבנית - ה-WAF וה-שרת שמאחוריו הם שני תוכנות נפרדות לגמרי, שכל אחת מהן מפענחת קלט לפי כללים משלה, ולא תמיד באותו סדר ובאותה קפדנות. אם ה-WAF בודק את הבקשה לפני שהוא מפענח אותה במלואה, בעוד השרת מפענח אותה עד הסוף לפני שהוא מריץ אותה, נוצר פער - ה-WAF רואה מחרוזת אחת, השרת בסוף מקבל מחרוזת אחרת לגמרי, שהיא בדיוק ה-payload המקורי שהוא ניסה לחסום.
זו בדיוק הסיבה שכל הטכניקות הבאות, למרות שהן נראות שונות זו מזו, חולקות את אותו רעיון בדיוק - להסתיר את הכוונה האמיתית של הקלט מאחורי שכבת קידוד שה-WAF לא מנרמל, אבל שהשרת כן יודע לפתוח.
קידוד URL וקידוד כפול¶
הדוגמה הפשוטה ביותר היא קידוד URL רגיל - תו כמו גרש בודד אפשר לכתוב כ-%27 במקום '. WAF בסיסי שמחפש את התו הגולמי בבקשה לא ימצא אותו, אבל שרת ווב רגיל מפענח קידוד URL כחלק סטנדרטי מטיפול בבקשה, ולכן מקבל את התו המקורי בדיוק.
קידוד כפול הולך צעד אחד קדימה - מקודדים גם את סימן האחוז עצמו, כך שהמחרוזת %27 הופכת ל-%2527. ה-WAF שמפענח פעם אחת רואה %27 תמים לכאורה, בעוד שהשרת, אם הוא מפענח בשכבות שונות של הטיפול בבקשה, בסוף מגיע לתו המקורי.
תעלולי Unicode ו-UTF-8 ארוך מדי¶
תקן UTF-8 מגדיר בדיוק כמה בייטים צריך תו מסוים, אבל בעבר היו מימושים שקיבלו גם ייצוגים "ארוכים מדי" - overlong encoding - שבהם תו שאמור להיות בבייט אחד מיוצג בשניים או שלושה בייטים, תוך ניצול חופש טכני בקידוד. מפענחים מחמירים דוחים ייצוגים כאלה, אבל מפענחים סלחניים יותר, בין אם ב-WAF ובין אם בשרת, עלולים לקבל אותם ולתרגם אותם בחזרה לתו המקורי. כשיש חוסר עקביות בין רמת ההחמרה של שני הצדדים, אפשר להעביר תו מסוכן בייצוג לא סטנדרטי שה-WAF לא מזהה כמתאים לחתימה שלו, בעוד השרת עדיין מפרש אותו נכון.
שינוי רישיות וישויות HTML¶
טכניקה פשוטה אך יעילה נגד WAF שמבוסס על חתימות רגישות לרישיות היא פשוט לשנות אותיות גדולות וקטנות בתוך payload - למשל SeLeCt במקום SELECT. SQL לא רגיש לרישיות במילות מפתח, אבל חתימה שנכתבה בקפדנות רבה מדי עלולה לפספס וריאציה כזו.
באופן דומה, בהקשר של HTML ו-JavaScript אפשר להשתמש בישויות HTML כדי לייצג תווים - למשל < במקום < - כאשר הדפדפן או המפענח בצד השרת יודעים לפרש את הישות בחזרה לתו המקורי, אבל ה-WAF שמחפש את התו הגולמי בבקשה מפספס אותו לגמרי.
תעלולי null byte וקידוד מעורב¶
תו null, שמיוצג כ-%00, שימש בעבר לבלבל מפענחים שהתייחסו אליו כסוף מחרוזת, בעוד שהמשך הבקשה בפועל המשיך אחריו. הרעיון הכללי ממשיך להתקיים גם היום בצורות אחרות. הטכניקה החזקה ביותר בפועל היא לרוב שילוב של כמה שיטות קידוד יחד באותו payload - חלק מהתווים מקודדים ב-URL, חלק בישויות HTML, וחלק בשינוי רישיות - כך שאף חתימה בודדת לא תופסת את כל הצירוף.
למה זה עובד ולא תלוי מזל¶
חשוב להדגיש - הטכניקות האלה לא "מרמות" את השרת. השרת תמיד עשה בדיוק את מה שהוא אמור לעשות - לפענח קלט לפי התקנים שהוא מיישם. הבעיה היא שה-WAF לא תמיד מיישם את אותם תקני פענוח באותה קפדנות ובאותו סדר. כל עוד קיים הפער הזה, טכניקות קידוד ימשיכו לעבוד, בלי קשר לכמה חתימות חדשות יתווספו למאגר.
איך מגנים נכון¶
- נורמליזציה עקבית משני הצדדים - הפתרון האמיתי הוא ש-WAF יבצע נורמליזציה מלאה ורקורסיבית של קלט לפני בדיקתו, באותה רמת קפדנות בדיוק שבה מבצע אותה השרת שמאחוריו.
- WAF לא כשכבת הגנה יחידה - גם WAF מעולה שמבצע נורמליזציה מושלמת לא פוטר מבדיקת קלט תקינה ברמת האפליקציה. הגנה אמיתית תמיד מתחילה מוולידציה נכונה בקוד עצמו, לא בשכבה חיצונית שיושבת מעליו.
- בדיקה אמפירית, לא הנחות - הדרך היחידה לדעת אם קיים פער נורמליזציה מול יעד ספציפי היא לבדוק בפועל, ולא להניח שכל WAF מתנהג אותו דבר.
הבנה מדויקת של פערי פענוח כאלה היא בדיוק סוג הידע הטכני שמייחד בודק חדירה שמבין למה טכניקה עובדת, לא רק שהיא עובדת. אנחנו נכנסים לעומק הזה בקורס פריצת אתרים מתקדמת.
התנסיתם עם טכניקת קידוד שעבדה נגד WAF מסוים ולא הייתם בטוחים למה? שתפו בדיסקורד, יש שם דיון מתמשך בדיוק על זה.
לסיכום¶
עקיפת WAF באמצעות קידוד לא מנצלת חולשה בודדת, אלא פער מבני בין הדרך שבה ה-WAF מפענח בקשה לבין הדרך שבה השרת שמאחוריו עושה זאת בפועל. URL encoding כפול, Unicode ארוך מדי, שינויי רישיות, ישויות HTML וקידוד מעורב הם כולם ביטויים שונים לאותו עיקרון. ההגנה האמיתית מתחילה בנורמליזציה עקבית, אבל בסוף תמיד חוזרת לוולידציה נכונה ברמת האפליקציה עצמה.