Cómo hacer un ataque Prototype Pollution
- js
- hacking
- code injection
En qué consiste este ataque
Prototype pollution es un ataque de inyección de código que se dirige al tiempo de ejecución de JavaScript (js runtime)
Permite al atacante sobrescribir los valores por defecto de objetos javascript pudiendo causar denegaciones de servicio y en casos extremos hacer un ataque RCE (remote code execution)
Por qué se produce
Este ataque se centra en vulnerar la construcción de objetos de JS. Por ejemplo, para crear un objeto se hacer de la siguiente forma:
const obj = {}
Pues bien, este objeto, si lo inspeccionamos, veremos que tiene propiedades y métodos heredados como por ejemplo el método toString()
- Object.prototype.toString(): https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
Esos métodos vienen de la clase padre la cual es el Prototipo, la plantilla base para crear otras clases como son los objetos típicos de javascript.
Y esto es la herencia de toda la vida.
Para poder comprobar que la clase padre es una plantilla para todos los objetos podemos hacer los siguiente:
Y ya con esto, desde una perspectiva maliciosa podremos modificar métodos del prototipo o añadir nuevos y nuevas propiedades para explotar tal vez así otras partes de la aplicación/página web.
Esquema de ataque
A continuación se observa las diferentes fases sobre el esquema básico de un ataque de “prototype pollution”.
Prueba de concepto
El repositorio con todo el ejemplo ya completo lo encontramos aquí:
Creamos una pequeña API REST con express en la que exponemos un método PUT para modificar un usuario según el body de la petición.
El método llama a la función mergeObj para combinar las propiedades del usuario y de lo que llega por la petición.
Esa función es una función custom.
Al hacer una petición al endpoint del listado de usuarios vemos lo siguiente:
El usuario “foo” tiene un “role” de usuario.
Si hacemos una petición para actualizar a ese usuario y le indicamos que queremos crear la propiedad “role” en su prototipo, estaremos haciendo en el fondo que el usuario “foo” sobrescriba el valor de su propiedad “role” por la de su clase prototipo.
Es decir, si el prototipo se convierte en administrador el usuario también lo hará.
Si ahora volvemos a listar los usuarios veremos como realmente se ha propagado el cambio sobre el usuario.
Con esto ya podemos autenticarnos como admins y obtener el token
Ejemplo de payloads
target.com/?__proto__[<vulnerable_property_or_method>]=data:,alert(1);
Cómo mitigar el ataque
Una de las primeras mitigaciones que podemos aplicar como desarrolladores es garantizar la inmutabilidad de los objetos mediante el método Object.freeze()
let user = Object.freeze(users[userIdx])
Aunque una mejor práctica de seguridad al utilizar el método del freeze es hacer lo mismo con el prototipo de los objetos para evitar este tipo de mutaciones en la clase padre.
Object.freeze(Object.prototype)
Esto habría que ponerlo en alguna parte, entrypoint por ejemplo, de nuestro código una única vez.
Otra de las cosas que podemos hacer en lugar de utilizar funciones de mergeo de objetos de forma insegura es utilizar el operador spread.
users[userIdx] = { ...users[userIdx], ...req.body }
Aunque también podemos utilizar soluciones de librerías como lodash.
import **safeMerge** from 'lodash.merge';
app.put('/user', (req, res) => {
const userIdx = req.headers.id-1
let user = users[userIdx]
if(Object.keys(req.body).includes('role') && user.role !== 'admin') {
return res.status(403).send()
}
**safeMerge**(users[userIdx], req.body)
return res.status(200).json(users[userIdx])
})
UPDATE: soy vulnerable a prototype pollution 😞
El otro día recibí el fatídico email de Github avisándome que dos de mis repositorios son vulnerables a este ataque por dependencias obsoletas.
Maravilloso 😑
Laboratorios
https://portswigger.net/web-security/all-labs#prototype-pollution
Documentación
- Prototype pollution attack in NodeJS application → https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf
- Silent Spring: Prototype Pollution Leads to Remote Code Execution in Node.js → https://arxiv.org/pdf/2207.11171.pdf
- Node Prototype Pollution Guide: https://book.hacktricks.xyz/pentesting-web/deserialization/nodejs-proto-prototype-pollution
- What is prototype pollution?: https://learn.snyk.io/lessons/prototype-pollution/javascript/
- Client-Side Prototype Pollution: https://github.com/BlackFan/client-side-prototype-pollution