אתגר CTF - צד לקוח - תרגיל¶
סקירה כללית¶
האתגר הזה מורכב מארבעה שלבים שמשלבים את כל הטכניקות שנלמדו בפרק 4. כל שלב מבוסס על השלב הקודם. המטרה הסופית היא לגנוב את ה-flag מחשבון ה-admin.
שלב 1: עקיפת CSP והרצת XSS¶
תיאור¶
אתר הבלוג https://target-blog.ctf מאפשר הוספת תגובות. התגובות מנוקות עם DOMPurify ומוצגות בדף. האתר משתמש ב-CSP הבא:
Content-Security-Policy: default-src 'none'; script-src 'self' https://cdnjs.cloudflare.com 'nonce-{RANDOM}'; style-src 'self'; img-src *; connect-src 'self'; frame-src 'none'; base-uri 'self'
משימה¶
הריצו alert(document.domain) בדף הבלוג.
רמזים¶
- בדקו אילו ספריות נטענות מ-cdnjs
- חפשו ספריית JavaScript שמאפשרת template injection
- זכרו - DOMPurify מאפשר HTML אבל חוסם event handlers
- שלבו CDN bypass עם template injection
שאלות מנחות¶
- מה הגרסה של AngularJS שזמינה ב-cdnjs?
- האם AngularJS sandbox escape עובד בגרסה הזו?
- איזה attribute צריך כדי להפעיל AngularJS על אלמנט?
- האם DOMPurify מאפשר את ה-attributes האלה?
שלב 2: זיהום פרוטוטייפ להסלמה ל-Stored XSS¶
תיאור¶
אחרי שמצאתם XSS ב-self context, אתם צריכים להפוך אותו ל-stored XSS שישפיע על משתמשים אחרים. האתר כולל מערכת "תגובות" שמשתמשת ב-API הבא:
// קוד הלקוח
async function loadComments(postId) {
let response = await fetch('/api/comments/' + postId);
let comments = await response.json();
let config = {};
comments.forEach(function(comment) {
mergeConfig(config, comment.metadata || {});
});
comments.forEach(function(comment) {
renderComment(comment, config);
});
}
function mergeConfig(target, source) {
for (let key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
if (!target[key]) target[key] = {};
mergeConfig(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
function renderComment(comment, config) {
let div = document.createElement('div');
div.className = 'comment';
let template = config.commentTemplate || '<b>{author}</b>: {text}';
let html = template.replace('{author}', escapeHtml(comment.author))
.replace('{text}', escapeHtml(comment.text));
div.innerHTML = html;
document.getElementById('comments').appendChild(div);
}
משימה¶
השתמשו בזיהום פרוטוטייפ דרך metadata של תגובה כדי לשנות את commentTemplate ולהזריק XSS ל-stored context.
רמזים¶
- הפונקציה
mergeConfigפגיעה לזיהום פרוטוטייפ - שדה
metadataשל תגובה מגיע מה-API - אם תשלחו תגובה עם metadata שמכיל
__proto__, תוכלו לזהם אתObject.prototype config.commentTemplateייקרא מהפרוטוטייפ אם לא מוגדר ישירות
שאלות מנחות¶
- מה ה-JSON שצריך לשלוח ב-POST לפי API?
- איך ה-template שלכם יכול לכלול XSS אם
escapeHtmlמופעל על author ו-text? - האם יש חלק ב-template שלא עובר escape?
שלב 3: שיבוש DOM לשיבוש הגדרות האפליקציה¶
תיאור¶
ה-admin panel באתר משתמש בקוד הבא לטעינת הגדרות:
// admin.js
(function() {
let appConfig = window.APP_CONFIG || {};
let apiEndpoint = appConfig.apiEndpoint || '/api';
let adminPanel = appConfig.adminPanelUrl || '/admin';
// טעינת נתוני admin
fetch(apiEndpoint + '/admin/data', {
credentials: 'include',
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
}
})
.then(function(r) { return r.json(); })
.then(function(data) {
document.getElementById('admin-data').textContent = JSON.stringify(data);
});
// טעינת סקריפט נוסף
if (appConfig.extraScript) {
let s = document.createElement('script');
s.src = appConfig.extraScript;
document.body.appendChild(s);
}
})();
משימה¶
השתמשו ב-DOM clobbering (דרך ה-stored XSS מהשלב הקודם) כדי:
1. לשבש את window.APP_CONFIG
2. לגרום לטעינת סקריפט מהשרת שלכם
3. הסקריפט שלכם יאסוף את ה-CSRF token מהדף
רמזים¶
- הזריקו HTML שיוצר אלמנטים עם
id="APP_CONFIG" - השתמשו ב-collection clobbering (שני אלמנטי
<a>עם אותו id) appConfig.extraScriptצריך להחזיר URL - השתמשו ב-<a>עםhref
שאלות מנחות¶
- איזה HTML payload ידרוס
window.APP_CONFIG.extraScript? - האם DOMPurify מאפשר אלמנטי
<a>עםidו-href? - מה הסקריפט שלכם צריך לעשות אחרי שנטען?
שלב 4: הזרקת CSS לחילוץ CSRF Token של ה-admin¶
תיאור¶
אחרי שלב 3, אתם צריכים את ה-CSRF token של ה-admin כדי לבצע פעולת "Export All Data" שמחזירה את ה-flag. ה-CSRF token נמצא ב:
ה-CSP מגביל סקריפטים, אבל style-src 'self' מאפשר CSS מהאתר עצמו. יש נקודת הזרקה ב-CSS:
משימה¶
- השתמשו בהזרקת CSS כדי לחלץ את ה-CSRF token תו אחר תו
- השתמשו ב-token כדי לשלוח בקשת POST ל-
/admin/export - קבלו את ה-flag
רמזים¶
- הזריקו CSS דרך פרמטר ה-color
- השתמשו ב-attribute selectors על meta tag
- צרו שרת שמקבל את התווים ומחזיר CSS חדש
- השתמשו ב-
@importלטעינה סדרתית
שאלות מנחות¶
- איך אפשר להפוך meta tag ל-visible כדי שהדפדפן יטען background?
- מה ה-CSS payload שיחלץ את התו הראשון?
- איך מפעילים את החילוץ מהדפדפן של ה-admin?
משאבים נוספים¶
מעבדות PortSwigger מומלצות¶
| מעבדה | נושא | רמה |
|---|---|---|
| CSP bypass | עקיפת CSP | Expert |
| Prototype pollution to XSS | זיהום פרוטוטייפ | Practitioner |
| DOM clobbering | שיבוש DOM | Expert |
| DOM XSS via postMessage | ניצול postMessage | Practitioner |
| WebSocket vulnerabilities | תקיפות WebSocket | Practitioner |
אתגרי HackTheBox ו-CTF¶
| אתגר | תיאור |
|---|---|
| HackTheBox - CSP Bypass challenges | אתגרי עקיפת CSP ברמות שונות |
| XSS Game by Google | אתגרי XSS מרמת מתחיל עד מתקדם |
| alert(1) to win | אתגרי XSS מתקדמים |
| Cure53 XSS challenges | אתגרי mXSS ו-DOM clobbering |
| CTFtime.org - Web challenges | אתגרי CTF בתחום web |
טיפים כלליים לפתרון¶
מתודולוגיה¶
1. סקרו את קוד המקור בקפידה
2. זהו את ה-CSP ומצאו חולשות
3. מצאו את כל ה-sinks (innerHTML, eval, location, etc.)
4. מצאו את כל ה-sources (URL params, postMessage, WebSocket, etc.)
5. שרטטו את זרימת המידע מ-source ל-sink
6. חפשו gadgets (prototype pollution, DOM clobbering)
7. בנו את שרשרת הניצול שלב אחר שלב
8. בדקו כל שלב בנפרד לפני שמחברים
כלים שימושיים¶
- Burp Suite - יירוט ומניפולציה של תעבורה
- DevTools Console - בדיקת payloads
- DevTools Application > Service Workers - ניטור SW
- CSP Evaluator - ניתוח מדיניות CSP
- DOMPurify test page - בדיקת payloads נגד DOMPurify
שגיאות נפוצות¶
- שכחה לקודד (encode) תווים מיוחדים ב-URL
- אי התחשבות ב-Content-Type של תגובות
- אי בדיקה שה-payload עובר DOMPurify
- שכחה שצריך HTTPS ל-Service Workers
- אי התחשבות ב-SameSite cookies