Acercamiento Estándar
Como ya sabemos, numerosas propiedades están presentes de forma heredada en el prototipo de un objeto en JavaScript. Entre estas, las propiedades constructor y __proto__ destacan como objetivos de explotación. La propiedad constructor apunta a la función que construye el prototipo del objeto, mientras que __proto__ es una referencia al objeto prototipo del que actualmente el objeto hereda directamente.
Regla de Oro
El concepto orbita entorno a la habilidad del atacante para influenciar ciertos parámetros clave, como x y val, en expresiones similares a Person[x][y] = val. Supón que un atacante asigna __proto__ a x. En dicho caso, el atributo identificado por y, es configurado universalmente a lo largo de todos los objetos que comparten la misma clase que el objeto con el valor denotado por val.
En un escenario más complejo, cuando un atacante tiene control sobre x, y y val en una estructura como Person[x][y][z] = val, asignar x como constructor e y como prototype conlleva a que una nueva propiedad definida por z se establezca a lo largo de todos los objetos en la aplicación con el val asignado. Este acercamiento necesita una organización de las propiedades de un objeto más complejas, haciéndolo menos relevante en la práctica.
Algunas Funciones Importantes
Cuando se trata de identificar vulnerabilidades potenciales de contaminación de prototipos, los pentesters deberían enfocarse en vectores/funciones comúnmente susceptibles a la contaminación de prototipos. Es crucial realizar una investigación concienzuda de cómo gestiona la aplicación la manipulación de objetos. Entenderemos unas cuantas funciones que un atacante puede explotar y entonces realizamos la explotación.
- Property Definition by Path: Las funciones que configuran propiedades de objetos basadas en una ruta dada (como
object[a][b][c] = value) pueden ser peligrosas si los componentes de la ruta son controlados por el usuario. Estas funciones deberían ser inspeccionadas para asegurar que no modifican el prototipo de objeto sin avisar. Considera un endpoint que permite a los usuarios actualizar reseñas sobre cualquier amigo.
Estructura de Objeto Inicial
Antes de ninguna actualización, tenemos un array friends inicial que contiene un objeto que representa el perfil de un amigo. Cada perfil incluye propiedades como id, name, reviews y albums.
let friends = [ { id: 1, name: "testuser", age: 25, country: "UK", reviews: [], albums: [{ }], password: "xxx", } ]; _.set(friend, input.path, input.value);
Input Recibido del Usuario
El usuario quiere añadir una review a su amigo. Ofrecen un payload que contiene la ruta donde la review debería ser añadida (review.content) y el contenido de la review (<script>alert(anycontent</script>) .
Un atacante actualiza al ruta para apuntar al prototipo:
{ "path": "reviews[0].content", "value": "<script>alert('anycontent')</script>" };
Usamos la función _set de lodash para aplicar el payload y añadir el contenido de la review a la ruta especificada en el objeto del perfil del amigo.
Estructura de Objeto Resultante
Después de ejecutar el código, el array friends será modificado para incluir la review del usuario. Sin embargo, debido a la falta de correcta validación, el contenido de la review dado por el usuario (<script>alert('anycontent')</script>) fue directamente añadido al objeto de perfil sin la sanitización adecuada.
let friends = [ { id: 1, name: "testuser", age: 25, country: "UK", reviews: [ "<script>alert('anycontent')</script>" ], albums: [{}], password: "xxx", } ];
De forma similar, supón que el atacante quiere insertar una propiedad maliciosa en el perfil del amigo. En dicho caso, ofrecen un payload que contiene la ruta donde la propiedad debería ser añadida (isAdmin) y el valor de la propiedad maliciosa (true).
const payload = { "path": "isAdmin", "value": true };
Después de ejecutar el código, el array friends será modificado para incluir la propiedad maliciosa isAdmin en el objeto del perfil del amigo. El objeto friends tendrá la siguiente estructura:
let friends = [ { id: 1, name: "testuser", age: 25, country: "UK", reviews: [], albums: [], password: "xxx", isAdmin: true // Malicious property inserted by the attacker } ];
Ejemplo Práctico
Ahora veremos de forma práctica cómo los atacantes pueden explotar esta vulnerabilidad. Supón que estás trabajando como pentester en una firma encargado de auditar una plataforma de red social. Puedes acceder a la aplicación en http://IP:5000 con las siguientes credenciales:
- Username:
bob - Password:
bob@123
Después de iniciar sesión, verás un dashboard así:

Para identificar la prototype pollution, exploraremos diferentes funcionalidades de la aplicación, como añadir álbumes, mandar solicitudes de amistad, añadir reviews, etc. Una vez que hayas iniciado sesión, otros amigos pueden ser añadidos a tu lista de amigos. Visita el perfil de cualquier amigo (digamos Sabalenka en este caso). Una vez que visites la página, verás diferentes opciones como añadir una opinión, clonar un álbum, etc.
Exploremos la funcionalidad de subida.

La funcionalidad de subida de opiniones permite a los usuarios subir cualquier review, la cual será guardada en la base de datos. Exploremos el lado del cliente y el del servidor para analizar varias posibilidades de explotación.
<form action="/submit-friend-review" method="post" class="mb-4"> <h2 class="mb-3">Submit a Review</h2> <input type="hidden" name="friendId" value="1"> <div class="form-group"> <textarea class="form-control" name="reviewContent" placeholder="Write your review here" rows="3"></textarea> </div> <button type="submit" class="btn btn-primary">Submit Review</button> </form>
El código del lado del cliente toma la review como parámetro de input y llama al endpoint API /submit-friend-review para añadir y revisar junto con el parámetro oculto friendId. COmo vimos en tareas previas, la contaminación de prototipo sóla es raramente explotable. Sin embargo, cuando es combinada con otros vectores de ataque como XSS, puede ofrecer una superficie de ataque mayor.
Veamos ahora el código del lado del servidor.
let friends = [ { id: 1, name: "Sabalenka", age: 25, country: "UK", reviews: [], albums: [{ name: "USA Trip", photos: "git.thm" }], password: "xxx", }, ... ... app.post("/submit-friend-review", (req, res) => { if (!req.session.user) { return res.redirect("/signin"); } const { friendId, reviewContent } = req.body; const friend = friends.find((f) => f.id === parseInt(friendId)); if (!friend) { return res.status(404).send("Friend not found"); } try { const input = JSON.parse(reviewContent); _.set(friend, input.path, payload.value); } catch (e) { } res.redirect(`/friend/${friendId}`); });
Este código realiza las siguientes acciones:
- Primero, valida si el usuario ha iniciado sesión. Si no, lo redirige a la página de login.
- Entonces, el código extrae el parámetro y valida la conexión entre el usuario con sesión iniciada y el
friendIdrecibido como parámetro de petición. - Después de verificar la conexión del amigo, el código inserta la review en el objeto amigo.
- El código usa la función
_.setde la utilidad Lodash que permite configurar el valor de una propiedad en una ruta dada. Es una forma conveniente de configurar valores en objetos sin tener que comprobar si cada nivel de ruta existe. Si una porción no existe,_setla crea. - El análisis del código muestra que es una buena oportunidad para realizar el ataque de definición de propiedad mediante ruta.
- Siguiendo, añadiremos una amigo y visitaremos su perfil para añadir una simple review y ver si funciona.

- La review es añadida exitosamente. Basado en el conocimiento que obtenemos del código fuente, preparemos un payload para mandar a la review que disparará una alerta. Sabemos que el objeto
friendstiene un arrayreviewsdonde las reviews de cada usuario se guardan. La funcionalidad para añadir una review funciona sólo en el perfil de un amigo.
{"path": "reviews[0].content", "value": "<script>alert('Hacked')</script>"}

Ahora, cada vez que alguien visite el perfil de este usuario, el ataque XSS seguirá ocurriendo. En dichos escenarios, el atacante puede incluso manipular otras propiedades que pueden permitir acceso admin al usuario, como isAdmin, isLoggedIn, etc.
En esta tarea, hemos aprendido cómo el atacante puede lanzar ataques de prototype pollution y XSS usando la técnica de propiedad por definición.
