Prototype Pollution
El Prototype Pollution es una vulnerabilidad propia de JavaScript (Node.js) que aprovecha la herencia por prototipos. En JavaScript, casi todos los objetos heredan de Object.prototype; si el atacante logra escribir una propiedad en ese prototipo global, esa propiedad aparece de repente en todos los objetos que no la definían explícitamente. La consecuencia típica es una escalada: contaminar Object.prototype con admin=true hace que cualquier objeto de usuario, al consultarse user.admin, herede ese true.
El vector clásico es una función de merge/clone recursivo (habitual al combinar el body de una petición con un objeto de configuración) que copia claves sin filtrar __proto__. Enviando un JSON con esa clave especial, la propiedad no se escribe en el objeto, sino en su prototipo:
POST /api/message HTTP/1.1
Content-Type: application/json
{"email":"a@a.com","message":"hi","__proto__":{"admin":true}}Tras el merge, Object.prototype.admin vale true, así que un usuario que no tenía el campo admin ahora lo hereda y pasa a ser administrador. Además de __proto__, según cómo se recorra el objeto también sirven las rutas constructor.prototype. El impacto va de la escalada de privilegios a la RCE cuando la propiedad contaminada acaba influyendo en child_process o en plantillas del lado servidor.
# mismos vectores por query-string / form anidado (qs, express)
?__proto__[admin]=true
?constructor[prototype][admin]=true
a[__proto__][admin]=true
# gadget de RCE: contaminar opciones que node pasa a child_process (execSync/spawn)
{"__proto__":{"shell":"/proc/self/exe","argv0":"console.log(require('child_process').execSync('id')+'')//","NODE_OPTIONS":"--require /proc/self/environ"}}Requisitos. La aplicación debe (1) fusionar o clonar datos controlados por el atacante en un objeto mediante una operación recursiva insegura (merge, extend, clone caseros o de librerías antiguas) y (2) leer después una propiedad de un objeto que no la define, heredándola del prototipo contaminado. Si el merge ignora __proto__/constructor o usa Map/objetos sin prototipo, el ataque no prende.
Cómo detectarlo. En el tráfico, la firma es la presencia de las claves __proto__, constructor o prototype en cuerpos JSON o en parámetros anidados (a[__proto__][admin]=true). Del lado de la aplicación, un comportamiento anómalo global (propiedades que aparecen en objetos que no las tenían) tras una petición con esas claves confirma la contaminación.
Defensa. Sanear las claves peligrosas (__proto__, constructor, prototype) antes de cualquier merge, usar Object.create(null) para objetos que reciben datos externos o estructuras Map, congelar el prototipo (Object.freeze(Object.prototype)) y preferir librerías de merge que ya bloqueen estas claves. Validar la entrada contra un esquema estricto. Comparte fin (escalar a admin) con el Mass Assignment.