Zanieczyszczenie prototypu po stronie klienta

Reading time: 9 minutes

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Odkrywanie za pomocą narzędzi automatycznych

Narzędzia https://github.com/dwisiswant0/ppfuzz, https://github.com/kleiton0x00/ppmap i https://github.com/kosmosec/proto-find mogą być używane do znajdowania luk w zanieczyszczeniu prototypu.

Ponadto, możesz również użyć rozszerzenia przeglądarki PPScan do automatycznego skanowania stron, które odwiedzasz w poszukiwaniu luk w zanieczyszczeniu prototypu.

Debugowanie, gdzie używana jest właściwość

javascript
// Stop debugger where 'potentialGadget' property is accessed
Object.defineProperty(Object.prototype, "potentialGadget", {
__proto__: null,
get() {
console.trace()
return "test"
},
})

Znalezienie przyczyny zanieczyszczenia prototypu

Gdy luka w zanieczyszczeniu prototypu zostanie zidentyfikowana przez którykolwiek z narzędzi, a kod nie jest zbyt skomplikowany, możesz znaleźć lukę, przeszukując słowa kluczowe takie jak location.hash, decodeURIComponent lub location.search w Narzędziach Dewelopera Chrome. Takie podejście pozwala na zlokalizowanie wrażliwej sekcji kodu JavaScript.

Dla większych i bardziej złożonych baz kodu, prostą metodą na odkrycie wrażliwego kodu są następujące kroki:

  1. Użyj narzędzia do zidentyfikowania luki i uzyskaj ładunek zaprojektowany do ustawienia właściwości w konstruktorze. Przykład podany przez ppmap może wyglądać następująco: constructor[prototype][ppmap]=reserved.
  2. Ustaw punkt przerwania na pierwszej linii kodu JavaScript, która zostanie wykonana na stronie. Odśwież stronę z ładunkiem, wstrzymując wykonanie w tym punkcie przerwania.
  3. Gdy wykonanie JavaScript jest wstrzymane, wykonaj następujący skrypt w konsoli JS. Ten skrypt zasygnalizuje, kiedy właściwość 'ppmap' zostanie utworzona, co pomoże w zlokalizowaniu jej pochodzenia:
javascript
function debugAccess(obj, prop, debugGet = true) {
var origValue = obj[prop]

Object.defineProperty(obj, prop, {
get: function () {
if (debugGet) debugger
return origValue
},
set: function (val) {
debugger
origValue = val
},
})
}

debugAccess(Object.prototype, "ppmap")
  1. Wróć do zakładki Sources i wybierz „Wznów wykonywanie skryptu”. JavaScript będzie kontynuował wykonywanie, a właściwość 'ppmap' zostanie zanieczyszczona zgodnie z oczekiwaniami. Wykorzystanie podanego fragmentu ułatwia identyfikację dokładnej lokalizacji, w której właściwość 'ppmap' jest zanieczyszczona. Analizując Call Stack, można zaobserwować różne stosy, w których wystąpiło zanieczyszczenie.

Decydując, który stos zbadać, często warto skupić się na stosach związanych z plikami bibliotek JavaScript, ponieważ zanieczyszczenie prototypu często występuje w tych bibliotekach. Zidentyfikuj odpowiedni stos, sprawdzając jego powiązanie z plikami bibliotek (widoczne po prawej stronie, podobnie jak na dostarczonym obrazie). W scenariuszach z wieloma stosami, takimi jak te w liniach 4 i 6, logicznym wyborem jest stos w linii 4, ponieważ reprezentuje on początkowe wystąpienie zanieczyszczenia, a tym samym pierwotną przyczynę podatności. Kliknięcie na stos przeniesie cię do podatnego kodu.

https://miro.medium.com/max/1400/1*S8NBOl1a7f1zhJxlh-6g4w.jpeg

Znajdowanie gadżetów skryptowych

Gadżet to kod, który będzie wykorzystywany, gdy zostanie odkryta podatność PP.

Jeśli aplikacja jest prosta, możemy szukać słów kluczowych takich jak srcdoc/innerHTML/iframe/createElement i przeglądać kod źródłowy, aby sprawdzić, czy prowadzi do wykonania javascriptu. Czasami wspomniane techniki mogą w ogóle nie znaleźć gadżetów. W takim przypadku czysta analiza kodu źródłowego ujawnia kilka ciekawych gadżetów, jak w poniższym przykładzie.

Przykład znajdowania gadżetu PP w kodzie biblioteki Mithil

Sprawdź ten artykuł: https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/

Rekompilacja ładunków dla podatnych bibliotek

Ominięcie sanitariuszy HTML za pomocą PP

To badanie pokazuje gadżety PP do ominięcia sanitizacji zapewnianych przez niektóre biblioteki sanitariuszy HTML:

  • sanitize-html
https://research.securitum.com/wp-content/uploads/sites/2/2020/08/image-7.png
  • dompurify
https://research.securitum.com/wp-content/uploads/sites/2/2020/08/image-9.png
  • Closure
html
<!-- from https://research.securitum.com/prototype-pollution-and-bypassing-client-side-html-sanitizers/ -->
<script>
Object.prototype['* ONERROR'] = 1;
Object.prototype['* SRC'] = 1;
</script>
<script src=https://google.github.io/closure-library/source/closure/goog/base.js></script>
<script>
goog.require('goog.html.sanitizer.HtmlSanitizer');
goog.require('goog.dom');
</script>
<body>
<script>
const html = '<img src onerror=alert(1)>';
const sanitizer = new goog.html.sanitizer.HtmlSanitizer();
const sanitized = sanitizer.sanitize(html);
const node = goog.dom.safeHtmlToNode(sanitized);

document.body.append(node);
</script>

Nowe narzędzia i automatyzacja (2023–2025)

  • Burp Suite DOM Invader (v2023.6) – PortSwigger dodał dedykowaną zakładkę Prototype-pollution, która automatycznie mutuje nazwy parametrów (np. __proto__, constructor.prototype) i wykrywa zanieczyszczone właściwości w punktach zrzutu wewnątrz rozszerzenia przeglądarki. Gdy gadżet jest wyzwolony, DOM Invader pokazuje stos wykonania i dokładną linię, w której właściwość została zdereferencjonowana, co czyni ręczne poszukiwanie punktów przerwania niepotrzebnym. Połącz to z fragmentem "Break on property access" pokazanym powyżej, aby szybko przejść z source → sink.
  • protoStalker – wtyczka do Chrome DevTools typu open-source (wydana w 2024), która wizualizuje łańcuchy prototypów w czasie rzeczywistym i oznacza zapisy do globalnie niebezpiecznych kluczy, takich jak onerror, innerHTML, srcdoc, id itp. Przydatne, gdy masz tylko pakiet produkcyjny i nie możesz zainstrumentować kroku budowania.
  • ppfuzz 2.0 (2025) – narzędzie teraz obsługuje moduły ES, HTTP/2 i punkty końcowe WebSocket. Nowy tryb -A browser uruchamia bezgłowy egzemplarz Chromium i automatycznie enumeruje klasy gadżetów, brutalnie forsując API DOM (zobacz sekcję poniżej).

Ostatnie badania nad gadżetami Prototype-Pollution (2022–2025)

W połowie 2023 roku badacze PortSwigger opublikowali artykuł pokazujący, że obiekty wbudowane w przeglądarkę mogą zostać przekształcone w niezawodne gadżety XSS po zanieczyszczeniu. Ponieważ te obiekty są obecne na każdej stronie, możesz uzyskać wykonanie, nawet jeśli kod aplikacji docelowej nigdy nie dotyka zanieczyszczonej właściwości.

Przykład gadżetu (działa we wszystkich przeglądarkach evergreen ≥ 2023-04):

html
<script>
// Source (e.g. https://victim/?__proto__[href]=javascript:alert(document.domain))
// For demo we just pollute manually:
Object.prototype.href = 'javascript:alert(`polluted`)' ;

// Sink – URL() constructor implicitly reads `href`
new URL('#'); // breaks into JS; in Chrome you get an alert, Firefox loads "javascript:" URL
</script>

Inne przydatne globalne gadżety, które potwierdzono jako działające po zanieczyszczeniu (testowane 2024-11):

Klasa gadżetuWłaściwość do odczytuOsiągnięty prymityw
Notificationtitlealert() poprzez kliknięcie powiadomienia
WorkernameWykonanie JS w dedykowanym Workerze
ImagesrcTradycyjne XSS onerror
URLSearchParamstoStringOtwarty przekierowanie oparte na DOM

Zobacz dokument PortSwigger, aby uzyskać pełną listę 11 gadżetów i dyskusję na temat ucieczek z piaskownicy.


Znaczące CVE PP po stronie klienta (2023-2025)

  • DOMPurify ≤ 3.0.8 – CVE-2024-45801 Atakujący mógł zanieczyścić Node.prototype.after przed zainicjowaniem sanitizera, omijając profil SAFE_FOR_TEMPLATES i prowadząc do przechowywanego XSS. Dostawca załatał to, używając sprawdzeń Object.hasOwn() i Object.create(null) dla map wewnętrznych.
  • jQuery 3.6.0-3.6.3 – CVE-2023-26136 / CVE-2023-26140 extend() mogło być użyte na stworzonych obiektach pochodzących z location.hash, wprowadzając dowolne właściwości do Object.prototype w kontekście przeglądarki.
  • sanitize-html < 2.8.1 (2023-10) zanieczyszczenie prototypu Złośliwa lista atrybutów, taka jak {"__proto__":{"innerHTML":"<img/src/onerror=alert(1)>"}}, ominęła listę dozwoloną.

Nawet jeśli podatna biblioteka działa tylko po stronie klienta, wynikowe XSS jest nadal wykorzystywalne zdalnie poprzez odzwierciedlone parametry, obsługiwacze postMessage lub przechowywane dane renderowane później.


Nowoczesne środki obronne

  1. Zamroź globalny prototyp wcześnie (najlepiej jako pierwszy skrypt):
javascript
Object.freeze(Object.prototype);
Object.freeze(Array.prototype);
Object.freeze(Map.prototype);

Bądź świadomy, że może to zepsuć polyfille, które polegają na późnym rozszerzeniu. 2. Użyj structuredClone() zamiast JSON.parse(JSON.stringify(obj)) lub fragmentów "deepMerge" z społeczności – ignoruje to settery/gettery i nie przechodzi przez łańcuch prototypów. 3. Kiedy naprawdę potrzebujesz funkcjonalności głębokiego łączenia, wybierz lodash ≥ 4.17.22 lub deepmerge ≥ 5.3.0, które mają wbudowaną sanitację prototypu. 4. Dodaj politykę bezpieczeństwa treści z script-src 'self' i surowym nonce. Chociaż CSP nie zatrzyma wszystkich gadżetów (np. manipulacja location), blokuje większość zlewów innerHTML.

Odnośniki

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks