זיהום פרוטוטייפ - Prototype Pollution - תרגיל¶
תרגיל 1: pollution בסיסי¶
הקוד הבא מכיל פונקציית מיזוג פגיעה:
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);
// continuing the code
let user = {};
if (user.role === 'admin') {
showAdminPanel();
}
א. כתבו JSON payload שיגרום ל-user.role להחזיר 'admin'.¶
ב. הסבירו מדוע JSON.parse לא מונע את הpollution.¶
ג. תקנו את פונקציית merge כך שתהיה מוגנת מפני pollution פרוטוטייפ.¶
תרגיל 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: pollution בצד השרת¶
שרת 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 שתבצע pollution פרוטוטייפ.¶
ב. כתבו בקשת HTTP שתנצל את ה-gadget של EJS כדי להשיג RCE.¶
ג. איזו פקודה תרוץ בשרת? הדגימו עם curl לשרת התוקף.¶
תרגיל 4: טכניקות זיהוי¶
א. זיהוי JSON spaces¶
כתבו בקשת HTTP שתשנה את ה-JSON spaces ב-Express. הסבירו כיצד תוכלו לזהות האם הpollution הצליח על סמך התגובה.
ב. זיהוי status code¶
כתבו בקשת HTTP שתשנה את הstatus code של תגובות Express. מה הstatus code שתצפו לראות?
ג. כתבו סקריפט Python שבודק אוטומטית אם endpoint פגיע לpollution פרוטוטייפ בצד השרת.¶
תרגיל 5: מעבדות PortSwigger¶
מעבדה 1: pollution פרוטוטייפ בצד הלקוח דרך URL¶
- כתובת: Client-side prototype pollution via browser APIs
- רמה: Practitioner
- משימה: מצאו מקור pollution פרוטוטייפ ו-gadget, ונצלו אותם ל-XSS
- רמז: בדקו איך האתר מפרסר פרמטרים מה-URL
מעבדה 2: pollution פרוטוטייפ דרך DOM XSS¶
- כתובת: DOM XSS via client-side prototype pollution
- רמה: Practitioner
- משימה: נצלו pollution פרוטוטייפ כדי להריץ
alert(document.cookie)
מעבדה 3: pollution פרוטוטייפ דרך flawed sanitization¶
- כתובת: Client-side prototype pollution via flawed sanitization
- רמה: Practitioner
- משימה: עקפו מנגנון הגנה חלקי ובצעו pollution פרוטוטייפ
מעבדה 4: pollution בצד השרת עם זיהוי¶
- כתובת: Detecting server-side prototype pollution without polluted property reflection
- רמה: Practitioner
- משימה: השתמשו בטכניקות זיהוי (status, JSON spaces, charset) כדי לאשר pollution
מעבדה 5: pollution בצד השרת ל-RCE¶
- כתובת: Remote code execution via server-side prototype pollution
- רמה: Expert
- משימה: נצלו pollution פרוטוטייפ בצד השרת כדי להשיג 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());
// vulnerable?
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);
// save to 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);
});