Loading...

Cómo hacer un ataque Prototype Pollution

2024-08-10
  • 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()

Captura de pantalla_2022-11-19_21-03-28.png

Captura de pantalla_2022-11-19_21-07-21.png

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:

Captura de pantalla_2022-11-19_21-12-10.png

Captura de pantalla_2022-11-19_21-15-45.png

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.

Captura de pantalla_2022-11-19_21-08-52.png

Esquema de ataque

A continuación se observa las diferentes fases sobre el esquema básico de un ataque de “prototype pollution”.

Untitled

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.

Captura de pantalla_2022-11-19_23-30-09.png

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.

Captura de pantalla_2022-11-19_23-32-19.png

Al hacer una petición al endpoint del listado de usuarios vemos lo siguiente:

Captura de pantalla_2022-11-19_21-57-16.png

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á.

Captura de pantalla_2022-11-19_22-55-50.png

Si ahora volvemos a listar los usuarios veremos como realmente se ha propagado el cambio sobre el usuario.

Captura de pantalla_2022-11-19_22-56-08.png

Con esto ya podemos autenticarnos como admins y obtener el token

Captura de pantalla_2022-11-19_22-56-41.png

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 😑

Captura de Pantalla 2022-12-16 a las 22.03.05.png

Laboratorios

https://portswigger.net/web-security/all-labs#prototype-pollution

Documentación