prototype pollution
| Po angielsku | prototype pollution |
|---|---|
| Którą formą pisać | po angielsku Polski odpowiednik istnieje, ale w praktyce nikt tak nie pisze. Zostaw angielski, polski trzymaj jako synonim. |
| Inne formy | zanieczyszczenie prototypu · zatruwanie prototypu |
| Kategoria | Podatności i klasy błędów |
Prototype pollution to podatność w JavaScripcie, która pozwala atakującemu dodawać dowolne właściwości do globalnych prototypów obiektów; właściwości te dziedziczą potem obiekty tworzone przez aplikację [1]. MITRE opisuje ją jako słabość CWE-1321: produkt przyjmuje dane określające atrybuty obiektu do ustawienia, ale nie kontroluje zmian atrybutów prototypu [2]. Po stronie przeglądarki prowadzi zwykle do DOM-based XSS, a po stronie serwera może skończyć się zdalnym wykonaniem kodu [1].
Jak działa
W JavaScripcie prawie każdy obiekt dziedziczy właściwości po wspólnym prototypie. Jeśli do tego prototypu trafi nowa właściwość, zobaczą ją wszystkie obiekty, które same jej nie mają. Aplikacja, która sprawdza na przykład options.isAdmin albo config.url, dostaje wtedy wartość podstawioną przez atakującego, choć nigdzie jej nie ustawiała.
Według PortSwigger błąd powstaje najczęściej, gdy funkcja rekurencyjnie scala obiekt kontrolowany przez użytkownika z istniejącym obiektem i nie oczyszcza kluczy [1]. Klucz __proto__ nie zostaje wtedy potraktowany jak zwykła nazwa pola, tylko jako odwołanie do prototypu, więc scalanie zapisuje wartości w prototypie zamiast w obiekcie docelowym [1]. MITRE wymienia jako niebezpieczne atrybuty __proto__, constructor i prototype [2].
Dane do scalenia mogą pochodzić z parametrów zapytania i fragmentu adresu URL, z danych JSON przetwarzanych funkcją JSON.parse() albo z komunikatów wymienianych między oknami przeglądarki [1]. W przypadku JSON-u parser traktuje __proto__ jak zwykły klucz tekstowy; specjalnego znaczenia nabiera on dopiero przy późniejszym przypisaniu do obiektu [1]. Dlatego ta sama struktura danych bywa bezpieczna w jednym miejscu kodu i groźna w innym.
PortSwigger wskazuje trzy elementy potrzebne do skutecznego ataku [1].
| Element | Znaczenie | Przykłady |
|---|---|---|
| źródło (source) | dane kontrolowane przez użytkownika [1] | parametry i fragment adresu, JSON, komunikaty między oknami [1] |
| ujście (sink) | funkcja albo element DOM, który pozwala wykonać kod [1] | wstawianie HTML, wykonanie skryptu |
| gadżet (gadget) | właściwość używana niebezpiecznie i dziedziczona po prototypie [1] | domyślna opcja biblioteki |
W WordPressie
WordPress przez lata dołączał do rdzenia bibliotekę jQuery, a z niej korzystały motywy i wtyczki. Wydanie jQuery 3.4.0 z 10 kwietnia 2019 roku naprawiło nieoczekiwane zachowanie funkcji jQuery.extend w trybie głębokiego scalania: jeśli niezaufany obiekt źródłowy zawierał wyliczalną właściwość __proto__, mógł rozszerzyć natywny Object.prototype [3]. Po poprawce jQuery.extend nie zapisuje już właściwości o nazwie __proto__ [3]. MITRE podaje ten błąd jako przykład słabości CWE-1321 pod numerem CVE-2019-11358 [2].
WordPress dołączał wtedy własną, starszą wersję biblioteki. 17 maja 2019 roku do repozytorium WordPressa trafiła zmiana, która przenosi (backport) poprawkę z jQuery 3.4.0 do dołączanej wersji biblioteki [4]. Numer wersji dołączanej biblioteki nie mówi więc sam w sobie, czy poprawka jest obecna. W czerwcu 2020 roku zespół rdzenia ogłosił plan aktualizacji: usunięcie jQuery Migrate 1.x w wersji 5.5 i przejście na najnowsze jQuery w wersji 5.6 [7].
Ryzyko nie kończy się na rdzeniu. Wtyczki i motywy często dołączają własne kopie bibliotek JavaScript, także przestarzałe; zob. podatność we wtyczce, podatność w motywie i Vulnerable and Outdated Components.
Zagrożenia i skutki
MITRE wymienia dwa główne skutki [2]. Pierwszy to wpływ na dane: atakujący wstrzykuje atrybuty używane przez inne składniki aplikacji, co może dać nieuprawniony odczyt albo zmianę danych. Drugi to awaria: nadpisanie istniejącego atrybutu wartością niezgodnego typu może spowodować awarię aplikacji [2], co w usłudze serwerowej oznacza atak odmowy usługi (denial of service).
W przeglądarce prototype pollution prowadzi zwykle do XSS (Cross-Site Scripting) opartego na DOM [1]. Po stronie serwera, w aplikacjach Node.js, może prowadzić do zdalnego wykonania kodu (RCE) [1]. W obu przypadkach sam ładunek (payload) bywa niewinny: to tylko klucz i wartość w JSON-ie. Szkodę wyrządza dopiero kod aplikacji, który tę wartość później odczytuje.
Jak się bronić
Zalecenia PortSwigger, MITRE i OWASP są zbieżne [1] [2] [5]:
- Oczyszczanie kluczy. Przy scalaniu i kopiowaniu obiektów odrzucać klucze
__proto__,constructoriprototype[2]. To odmiana sanityzacji danych wejściowych. - Obiekty bez prototypu. Tworzyć słowniki funkcją
Object.create(null), która daje obiekt niedziedziczący poObject.prototype[5]. - Map i Set. OWASP zaleca używać
new Map()albonew Set()zamiast literałów obiektów do przechowywania danych z zewnątrz [5]. - Zamrożenie prototypu.
Object.freeze(Object.prototype)blokuje zmiany prototypu, choć może psuć zgodność z niektórymi bibliotekami [2] [5]. - Node.js. Flaga
--disable-proto=deleteusuwa właściwość__proto__; OWASP zaznacza, że zanieczyszczenie przezconstructor.prototypejest nadal możliwe, więc to tylko dodatkowa warstwa [5].
Fragment poniżej pokazuje obronne scalanie z filtrem kluczy.
const BLOCKED = new Set(['__proto__', 'constructor', 'prototype']);
function safeMerge(target, source) {
for (const key of Object.keys(source)) {
if (BLOCKED.has(key)) continue;
target[key] = source[key];
}
return target;
}
Przy stronach WordPress najprostszym krokiem jest aktualność rdzenia, wtyczek i motywów razem z bibliotekami, które dołączają. Pomaga w tym SBOM (wykaz komponentów oprogramowania) albo przynajmniej lista zależności JavaScript. CSP (Content Security Policy) ogranicza skutki, gdy zanieczyszczenie prowadzi do XSS.
Historia i przykłady
Sekurak w październiku 2019 roku opisał, że Michał Bentkowski przygotował exploit dla Kibany, aplikacji opartej o Node.js, wykorzystujący podatność prototype pollution CVE-2019-7609 do wykonania dowolnego kodu na systemie operacyjnym [6]. Sekurak określił ją jako dość mało znaną klasę podatności [6]. Pracę zaprezentowano na konferencji OWASP Day [6]. Wśród przykładów podawanych przez MITRE są też CVE-2018-3721, CVE-2019-10744 i CVE-2020-8203, związane ze scalaniem obiektów, przypisywaniem wartości domyślnych albo ustawianiem atrybutów według ścieżki z kropkami [2].
Przypisy
7- [1] Prototype pollution · PortSwigger Web Security Academy
- [2] CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') · MITRE
- [3] jQuery 3.4.0 Released · jQuery Foundation
- [4] jQuery: bring jquery.js back into the WordPress SVN repo and backport the patch from 3.4.0 (commit 1406654) · WordPress (wordpress-develop, GitHub)
- [5] Prototype Pollution Prevention Cheat Sheet · OWASP
- [6] RCE w aplikacji opartej o Node.js (Kibana & Prototype Pollution & CVE-2019-7609) · Sekurak
- [7] Updating jQuery version shipped with WordPress · Make WordPress Core