Prototype Pollution
En JavaScript casi todos los objetos heredan de Object.prototype. Si una aplicación mezcla datos controlados por el atacante en un objeto sin filtrar claves como __proto__, constructor o prototype, el atacante puede escribir en el prototipo global y, con ello, inyectar propiedades que aparecen en todos los objetos del proceso. Eso convierte un merge inocente en control de flujo: desde XSS hasta RCE en Node.
Modelo de amenaza
Sección titulada «Modelo de amenaza»El bug es un escritura de propiedad no saneada que escala a propiedad global. El atacante no modifica un objeto: contamina la clase base de la que todos heredan. Lo peligroso es el “gadget”: algún código legítimo que lee una propiedad que normalmente no existe (options.isAdmin, config.shell, una opción de plantilla) y ahora la encuentra porque está en el prototipo.
Dónde surge
Sección titulada «Dónde surge»Funciones que fusionan/clonan/parsean objetos recursivamente sin bloquear claves peligrosas: merge, extend, clone, defaultsDeep (lodash antiguo), parseo de query strings anidados, JSON.parse + merge, deserializadores.
Red Team
Sección titulada «Red Team»Client-side (navegador) → XSS/DOM
Sección titulada «Client-side (navegador) → XSS/DOM»# vía query/hash que un script fusiona en un objeto de confighttps://target/#__proto__[html]=<img src=x onerror=alert(1)>https://target/?__proto__[innerHTML]=...Si una librería (jQuery antiguo, sanitizadores, templating) lee una opción contaminada, se obtiene XSS. Detección: en la consola, tras cargar, comprueba Object.prototype.polluted.
# prueba manual en consola({}).__proto__.test = "pwned";({}).test; // -> "pwned" => contaminableServer-side (Node) → DoS / bypass / RCE
Sección titulada «Server-side (Node) → DoS / bypass / RCE»# JSON malicioso a un endpoint que hace merge recursivoPOST /api/profile{"__proto__":{"isAdmin":true}}- Auth/logic bypass: contaminar
isAdmin,role, flags que luego se leen por defecto. - DoS: contaminar propiedades que rompen el runtime.
- RCE: gadgets conocidos — contaminar opciones que acaban en
child_process.spawn(shell,NODE_OPTIONS,env), o en motores de plantillas (EJS, Pug, Handlebars) cuyas opciones compiladas se leen del prototipo → ejecución de código. El RCE depende de encontrar el gadget adecuado en las dependencias.
Claves a probar
Sección titulada «Claves a probar»__proto__ constructor.prototype obj[__proto__][x]constructor ?__proto__[x]=y {"constructor":{"prototype":{"x":"y"}}}__proto__ suele filtrarse pero constructor.prototype a veces no.
Herramientas
Sección titulada «Herramientas»- PPScan / ppmap — detección client-side automatizada.
- Burp Suite — extensión de prototype pollution (server-side) para probar gadgets.
- DOM Invader (Burp) — detecta sources/sinks de PP en el navegador.
Impacto
Sección titulada «Impacto»XSS en cliente; en servidor, bypass de autorización, DoS y, con el gadget correcto, RCE completa.
Blue Team
Sección titulada «Blue Team»Detección
Sección titulada «Detección»- Payloads con
__proto__,constructor,prototypeen JSON, query o path. - Comportamiento anómalo global (propiedades que aparecen donde no deberían).
Telemetría
Sección titulada «Telemetría»Loguea cuerpos JSON con claves peligrosas; monitoriza versiones de dependencias con CVE de PP conocido.
Hardening
Sección titulada «Hardening»- Rechaza/limpia las claves
__proto__,constructor,prototypeantes de cualquier merge. - Usa estructuras sin prototipo:
Object.create(null)para mapas de datos;Mapen vez de objeto plano. Object.freeze(Object.prototype)donde sea viable;--disable-proto=throwen Node.- Valida el input contra un schema estricto (JSON Schema, Zod) que no permita claves extra.
- Actualiza librerías de merge (lodash ≥ 4.17.12, etc.); usa
structuredCloneen vez de merges caseros. - Evita leer opciones “por si acaso” del objeto sin comprobar que son propias (
hasOwnProperty).
Respuesta
Sección titulada «Respuesta»Parchea la función de merge/dependencia, audita qué gadgets existían, rota lo que el bypass pudiera haber tocado.
CVEs y casos reales
Sección titulada «CVEs y casos reales»- CVE-2019-10744 (lodash
defaultsDeep) — prototype pollution masivo; una de las referencias. - CVE-2018-3721 / CVE-2020-8203 (lodash) — más casos en
merge/set. - CVE-2019-11358 (jQuery) — prototype pollution en
jQuery.extend(true, …)(anterior a 3.4.0); afectó a innumerables sitios. - Kibana / various Express apps — cadenas de PP→RCE documentadas por investigadores (Michał Bentkowski, PortSwigger).
Checklist de prueba
Sección titulada «Checklist de prueba»- ¿Hay merge/clone recursivo de input (query, JSON, form anidado)?
- ¿
({}).__proto__.xse contamina tras la petición? (prueba en consola/server) - ¿
constructor.prototypefunciona donde__proto__está filtrado? - Client-side: ¿la contaminación llega a un sink de XSS (innerHTML, src)?
- Server-side: ¿se puede contaminar
isAdmin/roley afectar la lógica? - ¿Hay gadgets hacia plantillas o
child_process(RCE)? - ¿Las claves peligrosas se filtran antes del merge?