Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Schwachstellenklassen Glossar

Prototype Pollution - JavaScript-Objekt-Manipulation

Prototype Pollution ist eine JavaScript-spezifische Schwachstelle bei der Angreifer die Prototyp-Kette von Objekten manipulieren. Da alle JavaScript-Objekte von Object.prototype erben, können kontrollierte Eingaben in __proto__- oder constructor.prototype-Schlüsseln globale Objekteigenschaften überschreiben. Resultat: Denial of Service, Property-Injection für Privilege Escalation, in Node.js häufig Remote Code Execution. Betroffen: lodash, jQuery (historisch), alle deepmerge/cloneDeep-Implementierungen ohne Schutz.

Prototype Pollution ist eine Schwachstellenklasse die JavaScript-spezifische Eigenheiten ausnutzt. Fast jede JavaScript-Anwendung war zu einem Zeitpunkt anfällig - insbesondere durch populäre Bibliotheken wie lodash (vor 4.17.12), jQuery (vor 3.4.0), und Dutzende weitere npm-Pakete. Die Schwachstelle ist subtil: normaler Code sieht harmlos aus, bis ein Angreifer __proto__ als Schlüssel verwendet.

JavaScript-Prototypen - Grundlage der Schwachstelle

Jedes JavaScript-Objekt hat einen internen [[Prototype]]:

const obj = {};
obj.__proto__ === Object.prototype   // true
Object.prototype.isPrototypeOf(obj)  // true

Eigenschaft-Lookup-Chain:

obj.toString()
// → obj selbst: kein toString → schaue in __proto__
// → Object.prototype: hat toString! → gefunden!

Das Problem - Mutation von Object.prototype:

const maliciousInput = JSON.parse('{"__proto__": {"polluted": true}}');

// Wenn dieser Input in eine merge-Funktion fließt:
function merge(target, source) {
  for (let key in source) {
    target[key] = source[key];  // ← GEFÄHRLICH ohne Schlüssel-Validierung
  }
}
merge({}, maliciousInput);

// Jetzt ist JEDES neue Objekt vergiftet:
const anyObj = {};
console.log(anyObj.polluted);  // true ← PROTOTYPE POLLUTION!

Drei Angriffspfade für __proto__-Manipulation:

// 1. Direkte __proto__-Zuweisung:
obj["__proto__"]["isAdmin"] = true;

// 2. constructor.prototype:
obj["constructor"]["prototype"]["isAdmin"] = true;

// 3. __proto__ als JSON-Key:
JSON.parse('{"__proto__": {"isAdmin": true}}')
// → Object.prototype.isAdmin = true → JEDES Objekt hat jetzt isAdmin!

Ausnutzbare Szenarien

1. Privilege Escalation (häufigster Impact)

// Anwendungs-Code (vereinfacht):
function checkAdmin(user) {
  return user.isAdmin === true;
}

// Normale Anfrage:
const user = { name: "Alice" };
checkAdmin(user);  // false (isAdmin nicht gesetzt)

// Nach Prototype Pollution:
// Angreifer hat {"__proto__": {"isAdmin": true}} übergeben
const user = { name: "Bob" };  // NEUES Objekt!
user.isAdmin   // true ← via Prototype Chain!
checkAdmin(user);  // true ← PRIVILEGE ESCALATION!

// Realistisches Beispiel - RBAC-Bypass:
if (!req.user.roles.includes('admin')) { return 403; }
// Nach Pollution: jeder User hat roles via Prototype-Chain!

2. Remote Code Execution in Node.js (via Template-Engines)

Wenn Prototype Pollution auf Server-Seite ausgenutzt wird, können Template-Engines wie EJS, Pug oder Handlebars betroffen sein: Diese nutzen Object.prototype-Properties intern. Die Pollution bestimmter Properties (outputFunctionName, escapeFunction) kann zu Code-Ausführung führen wenn ein Template gerendert wird. Bekannte Gadget-Chains sind für EJS, Pug und Handlebars dokumentiert (CVE-2021-23358, CVE-2022-24999 und ähnliche Advisories).

3. DoS via Objekt-Manipulation

Object.prototype.toString = null;
// JEDER JSON.stringify-Aufruf wirft TypeError!
// → Denial of Service der gesamten Anwendung

4. Prototype Pollution → XSS (Client-Side)

// jQuery < 3.4.0 (historisch):
$.extend(true, {}, JSON.parse(userInput));
// __proto__ Pollution → jQuery-interne Properties vergiftet
// → XSS möglich via Event-Handler-Injection

Betroffene npm-Pakete (historisch)

Bekannte anfällige Bibliotheken (bereits gepatcht!):

PaketBetroffene FunktionCVEFix
lodash < 4.17.12_.merge(), _.defaultsDeep(), _.mergeWith()CVE-2019-10744 (CVSS 9.1)npm update lodash (>=4.17.12)
jQuery < 3.4.0$.extend(true, ...) mit __proto__ InputCVE-2019-11358jQuery >= 3.4.0
hoek < 5.0.3 (hapi.js)hoek.merge() / hoek.clone()CVE-2018-3728Update auf >= 5.0.3
flat < 5.0.1flat.unflatten({'__proto__.polluted': true})-Update auf >= 5.0.1
minimist < 1.2.6CLI-Argument-Parser (verbreitet!)CVE-2020-7598Update auf >= 1.2.6
# Erkennung mit npm audit:
npm audit --audit-level=high
# → Zeigt Prototype-Pollution-Schwachstellen in Dependencies

# Erkennung mit Snyk:
npx snyk test
# → Tiefere Analyse der Dependency-Kette

Erkennung im Pentest

Black-Box Test - JSON-Input mit __proto__

POST /api/settings HTTP/1.1
Content-Type: application/json

{"settings": {"__proto__": {"admin": true}}}

Alternative Syntax:

{"settings": {"constructor": {"prototype": {"admin": true}}}}

Tiefe Merge-Payload:

{"a": {"b": {"c": {"__proto__": {"polluted": "yes"}}}}}

Pollution-Nachweis: GET /api/profile oder GET /api/user - enthält Response neue Properties?

Parameter Pollution in Query-Strings

GET /api?__proto__[admin]=true
GET /api?constructor[prototype][admin]=true

Automatisiertes Scanning

  • Burp Suite Intruder: alle JSON-Keys auf __proto__-Varianten testen
  • PPMap / PPScan npm-Tools für automatische Black-Box-Erkennung

Code Review (White-Box)

# Unsichere Merge-Patterns ohne Schlüssel-Validierung:
grep -r "Object.assign\|\.merge\|\.extend" src/
grep -r "for.*in.*source" src/  # for..in iteriert __proto__!

Schutzmaßnahmen

1. Schlüssel-Validierung vor Objekt-Zuweisung

// Unsicher:
target[key] = source[key];

// Sicher - gefährliche Schlüssel ausschließen:
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
if (!FORBIDDEN_KEYS.has(key)) {
  target[key] = source[key];
}

// Oder mit Object.hasOwn() (kein Prototype-Chain-Lookup):
if (Object.hasOwn(source, key)) {
  target[key] = source[key];
}

2. Object.create(null) für Dictionaries ohne Prototype

// Normales Objekt: erbt von Object.prototype (anfällig)
const obj = {};

// Null-Prototype-Objekt: keine Prototype-Chain (sicher!)
const safeMap = Object.create(null);
// safeMap.__proto__ → undefined
// Pollution nicht möglich!

3. structuredClone() statt manueller Merge (Node.js 17+)

// Unsicher (wenn merge keine Schlüssel-Validierung hat):
const config = customMerge({}, userInput);

// Sicher (behandelt __proto__ korrekt):
const config = structuredClone(userInput);

4. JSON Schema Validation vor Verarbeitung

import Ajv from 'ajv';
const ajv = new Ajv();
const schema = {
  type: 'object',
  properties: { name: { type: 'string' } },
  additionalProperties: false,  // ← Blockt __proto__ und andere!
};
if (!ajv.validate(schema, input)) throw new Error('Invalid input');

5. Object.freeze(Object.prototype) (Notfall-Hardening)

// Verhindert Mutation von Object.prototype:
Object.freeze(Object.prototype);
// Alle Prototype-Pollution-Versuche silently fail oder TypeError!

Achtung: Kann Legacy-Code brechen → erst testen!

6. Dependencies aktuell halten

  • npm audit regelmäßig ausführen
  • Renovate/Dependabot für automatische Updates
  • Snyk in CI/CD-Pipeline für Dependency-Scanning
  • package.json Overrides für transitive Dependencies mit PP-Schwachstellen

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung