זיהום פרוטוטייפ - Prototype Pollution - תרגיל¶
תרגיל 1: זיהום בסיסי¶
הקוד הבא מכיל פונקציית מיזוג פגיעה:
function merge(target, source) {
for (let key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
if (!target[key]) target[key] = {};
merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
let userInput = JSON.parse(getUserInput());
let config = {};
merge(config, userInput);
// בהמשך הקוד
let user = {};
if (user.role === 'admin') {
showAdminPanel();
}
א. כתבו JSON payload שיגרום ל-user.role להחזיר 'admin'.¶
ב. הסבירו מדוע JSON.parse לא מונע את הזיהום.¶
ג. תקנו את פונקציית merge כך שתהיה מוגנת מפני זיהום פרוטוטייפ.¶
תרגיל 2: מציאת gadgets¶
הקוד הבא קיים בדף:
function createWidget(options) {
let defaults = {
width: 300,
height: 200
};
let config = {};
merge(config, defaults);
merge(config, options);
let widget = document.createElement('div');
widget.style.width = config.width + 'px';
widget.style.height = config.height + 'px';
if (config.innerHTML) {
widget.innerHTML = config.innerHTML;
}
if (config.src) {
let script = document.createElement('script');
script.src = config.src;
document.body.appendChild(script);
}
document.body.appendChild(widget);
}
createWidget({});
א. זהו את כל ה-gadgets בקוד.¶
ב. כתבו URL payload שינצל כל gadget בנפרד.¶
ג. איזה gadget הכי מסוכן ומדוע?¶
תרגיל 3: זיהום בצד השרת¶
שרת Express הבא פגיע:
const express = require('express');
const ejs = require('ejs');
const app = express();
app.use(express.json());
app.set('view engine', 'ejs');
function deepCopy(target, source) {
for (let key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = {};
deepCopy(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
app.post('/api/settings', (req, res) => {
let settings = {};
deepCopy(settings, req.body);
res.json({ status: 'ok' });
});
app.get('/dashboard', (req, res) => {
res.render('dashboard', { title: 'Dashboard' });
});
א. כתבו בקשת HTTP שתבצע זיהום פרוטוטייפ.¶
ב. כתבו בקשת HTTP שתנצל את ה-gadget של EJS כדי להשיג RCE.¶
ג. איזו פקודה תרוץ בשרת? הדגימו עם curl לשרת התוקף.¶
תרגיל 4: טכניקות זיהוי¶
א. זיהוי JSON spaces¶
כתבו בקשת HTTP שתשנה את ה-JSON spaces ב-Express. הסבירו כיצד תוכלו לזהות האם הזיהום הצליח על סמך התגובה.
ב. זיהוי status code¶
כתבו בקשת HTTP שתשנה את קוד הסטטוס של תגובות Express. מה קוד הסטטוס שתצפו לראות?
ג. כתבו סקריפט Python שבודק אוטומטית אם endpoint פגיע לזיהום פרוטוטייפ בצד השרת.¶
תרגיל 5: מעבדות PortSwigger¶
מעבדה 1: זיהום פרוטוטייפ בצד הלקוח דרך URL¶
- כתובת: Client-side prototype pollution via browser APIs
- רמה: Practitioner
- משימה: מצאו מקור זיהום פרוטוטייפ ו-gadget, ונצלו אותם ל-XSS
- רמז: בדקו איך האתר מפרסר פרמטרים מה-URL
מעבדה 2: זיהום פרוטוטייפ דרך DOM XSS¶
- כתובת: DOM XSS via client-side prototype pollution
- רמה: Practitioner
- משימה: נצלו זיהום פרוטוטייפ כדי להריץ
alert(document.cookie)
מעבדה 3: זיהום פרוטוטייפ דרך flawed sanitization¶
- כתובת: Client-side prototype pollution via flawed sanitization
- רמה: Practitioner
- משימה: עקפו מנגנון הגנה חלקי ובצעו זיהום פרוטוטייפ
מעבדה 4: זיהום בצד השרת עם זיהוי¶
- כתובת: Detecting server-side prototype pollution without polluted property reflection
- רמה: Practitioner
- משימה: השתמשו בטכניקות זיהוי (status, JSON spaces, charset) כדי לאשר זיהום
מעבדה 5: זיהום בצד השרת ל-RCE¶
- כתובת: Remote code execution via server-side prototype pollution
- רמה: Expert
- משימה: נצלו זיהום פרוטוטייפ בצד השרת כדי להשיג RCE
- רמז: חפשו gadgets ב-child_process או במנוע התבניות
תרגיל 6: כתיבת כלי סריקה¶
כתבו סקריפט Python שמבצע את הבדיקות הבאות:
- שולח בקשת POST עם JSON שמכיל
__proto__עם תכונות זיהוי - בודק שינוי ב-status code
- בודק שינוי ב-JSON spaces
- בודק שינוי ב-charset
- מדווח על ממצאים
תרגיל 7: תיקון קוד פגיע¶
הקוד הבא הוא חלק מאפליקציית Node.js. מצאו את כל נקודות הפגיעות ותקנו אותן:
const express = require('express');
const app = express();
app.use(express.json());
// פגיע?
function extend(target, source) {
Object.keys(source).forEach(key => {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = target[key] || {};
extend(target[key], source[key]);
} else {
target[key] = source[key];
}
});
}
app.post('/api/user/preferences', (req, res) => {
let prefs = {};
extend(prefs, req.body);
// שמירה ב-DB
db.updatePreferences(req.user.id, prefs);
res.json({ status: 'ok' });
});
app.get('/api/user/profile', (req, res) => {
let user = db.getUser(req.user.id);
if (user.isAdmin) {
// ...
}
res.json(user);
});