Menu
About me Kontakt

Prototype pollution - an example of an advanced XSS attack method (film, 45 minutes)

Michał Bęckowski from Securitum discussed the concept of Prototype Pollution in his presentation, specifically within the context of JavaScript. Prototype Pollution is a specific vulnerability that can lead to significant security issues in web applications. Michał, as an expert in security testing, thoroughly explained how prototype inheritance works in JavaScript and its implications for security. He presented interesting examples of how the manipulation of prototypes can alter application logic, potentially leading to XSS attacks. His talk not only delved into theoretical knowledge but also showcased concrete cases where this vulnerability was exploited in well-known libraries, such as DOMPurify and jQuery.

As he continued his presentation, Bęckowski illustrated how minor code changes can be leveraged to execute malicious code. By utilizing Prototype Pollution techniques, he provided examples that demonstrated how libraries could be susceptible to manipulations. He emphasized the importance of monitoring the security of popular libraries used in projects, enabling the detection of potential vulnerabilities essential for modern programming.

Michał also shared insights from his research and projects he was involved in, offering the audience training on OWASP Top 10, which piqued their interest. He discussed practical security aspects, highlighting that many techniques could enhance understanding of prototypes and security in JavaScript. He encouraged attendees to actively seek out bugs and report them in bug bounty programs.

At the end of his talk, Michał Bęckowski drew attention to the statistics of his video. At the time of writing, the presentation has reached 1366 views and received 28 likes. This indicates a strong interest in the topic, emphasizing the need for understanding and securing web applications against emerging threats. Understanding issues such as Prototype Pollution seems crucial for developers working in the JavaScript environment.

Toggle timeline summary

  • 00:00 Introduction to Prototype Pollution.
  • 00:08 Michal Beckowski introduces himself and his work at Securitum.
  • 00:49 Discussion on Bug Bounty success and personal achievements.
  • 01:26 Presentation begins without slides, transitioning directly to a demo.
  • 01:34 Overview of Prototype Pollution vulnerability in JavaScript.
  • 01:59 Explaining JavaScript inheritance model to set context.
  • 03:56 Demonstrating object creation and property inheritance in JavaScript.
  • 04:27 Illustrating how property lookup occurs in JavaScript.
  • 06:13 Explaining how modifying prototypes affects object properties.
  • 09:44 Providing practical examples of how Prototype Pollution can lead to security vulnerabilities.
  • 10:23 Demonstrating an application that renders HTML input.
  • 10:45 Examining the flaws in using libraries that mishandle prototypes.
  • 15:47 Explaining how to exploit Prototype Pollution to perform XSS attacks.
  • 23:12 Practicing the exploitation of a vulnerable application with example code.
  • 24:15 Summarizing the risks of Prototype Pollution and the importance of securing code.
  • 39:54 Conclusion of the presentation and invitation for questions.
  • 41:15 Explaining specific bugs related to DOM Purify and Document Mode.
  • 43:05 Affirming that Prototype Pollution can affect various JavaScript applications.
  • 44:35 Discussing a recent CTF challenge related to Prototype Pollution.

Transcription

OK, zatem zaczynamy. Porozmawiamy sobie właśnie dzisiaj o Prototypeolution. Ja już zostałem przedstawiony. Nazywam się Michał Bęckowski, pracuję w firmie Securitum, czyli w tej samej firmie, w której pracuje też Michał Sajdak, który miał prezentację przede mną. W Securitum zajmuję się przeprowadzeniem testów bezpieczeństwa, prowadzę też szkolenia, a jak mam trochę wolnego czasu, to zajmuję się różnego rodzaju researchem, głównie gdzieś w okolicach przeglądarek, czyli badam sobie bezpieczeństwo samych przeglądarek i badam sobie bezpieczeństwo aplikacji webowych właśnie w tej części, która jest po stronie przeglądarki. O tym zresztą będzie też ta prezentacja. A propos Bug Bunty, to w latach od 2016 do 2019 byłem w najlepszej dziesiątce osób, które zgłaszały błędy w Bug Bunty Google. Ostatnio, no, wyleciałem z tego, jestem gdzieś w okolicy trzydziestego miejsca, już przez ponad rok niczego do Google nie zgłaszałem. Jeżeli ktoś miałby ochotę śledzić moje jakieś znaleziska czy ciekawostki, którymi się dzielę, to na Twitterze mam konto pod nikiem SecurityMB. Zapraszam do śledzenia. OK, i tutaj na tej prezentacji zasadniczo nie będzie slajdów. Od razu przejdziemy sobie do demo. OK, czyli porozmawiamy sobie o podatności Prototype Pollution. Prototype Pollution jest podatnością bardzo specyficzną dla Javascriptu i to, co mam tutaj uruchomione, może to pokażę, to jest interfejs z linii komend z Node.js i kilka pierwszych dem zrobię właśnie w tym miejscu. Zanim sobie porozmawiamy o tym, czym jest Prototype Pollution, czyli ta podatność bezpieczeństwa, musimy mieć troszeczkę tła, czyli musimy wiedzieć, jak działa model dziedziczenia w Javascriptie. Więc tak, zacznę od tego, że utworzę sobie jakiś obiekt i ten obiekt może mieć jedną właściwość, na przykład właściwość, niech to będzie abc o wartości 123. Jak próbuję się odwołać do tego obiektu, to faktycznie ma taką jedną właściwość, ale w rzeczywistości ten obiekt ma więcej właściwości, to znaczy na przykład ma taką metodę jak to string, czy na przykład ma taką metodę jak hasOnProperty i jeszcze kilka innych takich budowanych metod. No i pytanie brzmi, skąd tak naprawdę bierze się ta metoda to string, skoro ona nie została zdefiniowana bezpośrednio w tym obiekcie, który ma tylko jedną właściwość, jaką jest abc. I odpowiedź jest taka, że każdy obiekt w Javascriptie ma zdefiniowany tak zwany prototyp. I jeżeli dana właściwość nie istnieje w obiekcie, czyli próbujemy się odwołać do właściwości to string, która nie istnieje bezpośrednio w tym obiekcie, to silnik javascriptowy szuka tej właściwości w prototypie. I jest właśnie taki, można by powiedzieć, globalny prototyp obiektów w Javascriptie, a do niego można się dostać przez Object Prototype. I właśnie ponieważ Object Prototype ma zdefiniowaną właściwość to string, no to de facto wszystkie obiekty w Javascriptie też mają tę właściwość zdefiniowaną, no chyba że nie dziedziczą po Object Prototype. Do tego sobie jeszcze zaraz przejdziemy. Czyli teraz tak, mogę sobie zdefiniować dwa obiekty, niech będzie obiekt pierwszy, który będzie miał właściwość abc o wartości 123 i obiekt drugi, który będzie miał właściwość xyz o wartości 456, ale dodatkowo sobie zdefiniuję, że jego prototypem będzie ten obiekt pierwszy. I teraz tak, jak widzimy, obiekt drugi ma tylko jedną właściwość, jak próbuje się do niego odwołać, tą właściwością jest xyz. Natomiast jeżeli spróbuje się odwołać do właściwości abc, to ta właściwość abc też istnieje. I dzieje się tak, ponieważ silnik javascriptowy patrzy, czy właściwość abc istnieje w tym obiekcie drugim, nie istnieje, więc patrzy sobie, czy ta właściwość istnieje w prototypie. W prototypie już istnieje, więc zwraca tę wartość z prototypu. Zobaczmy sobie, analogicznie się dzieje, jak spróbuje się dostać do toString, czyli silnik javascriptowy na początku sprawdza, czy toString istnieje w obiekcie drugim, nie istnieje, później sprawdza w prototypie, czyli w tym obiekcie pierwszym, tutaj też nie istnieje, więc sprawdza w prototypie obiektu pierwszego, czyli w tym takim globalnym obiekt prototyp. Innymi słowy widzimy, że możemy mieć swego rodzaju łańcuch prototypów, czyli jeden obiekt może mieć zdefiniowany prototyp, jego prototyp może mieć zdefiniowany kolejny prototyp itd., itd., aż dochodzimy do momentu, gdy ostatni obiekt w tym łańcuchu ma prototyp 0 i w tym momencie łańcuch prototypów się kończy. Natomiast w przeszłości bywały błędy w silnikach javascriptowych, że można było ustawić te prototypy cyklicznie i w ten sposób doprowadzać do nieskończonej pętli w silnikach javascriptowych, bo on po prostu, ten łańcuch prototypów nigdy się nie kończył. Ale tutaj akurat tak się nie dzieje. Czyli wiemy, że javascript jak szuka właściwości obiektu, to najpierw patrzy na obiekt, a później patrzy na łańcuch prototypów tego obiektu. Zobaczmy, że jeżeli ja zmienia sobie lub dodam nową właściwość do tego obiektu pierwszego, czyli np. niech to będzie właściwość, niech będzie może QQQ, o wartości niech będzie tym razem 789, to zobaczmy, obiekt 2 też przy okazji dostaje tę właściwość QQQ, pomimo że prototyp został zmieniony później niż obiekt drugi został zdefiniowany, ale silnik javascriptowy w tym momencie szuka tej właściwości QQQ w obiekcie drugim, ona znajduje się w prototypie tego obiektu, więc ta właściwość jest zwracana. Idąc dalej, ponieważ domyślnie każdy obiekt w javascriptzie dziedziczy potem object prototype, to teraz gdyby w object prototype powstała jakaś nowa właściwość, np. dodam sobie tę nową właściwość test, to w tym momencie każdy obiekt w javascriptzie będzie miał właściwość test, ponieważ gdy silnik javascriptowy będzie sobie przechodził po łańcuchu prototypów, to w końcu natrafi na ten object prototype i tutaj będzie ta wartość test. Jest to właśnie dość istotne, bo w ten sposób możemy zmieniać logikę aplikacji. To znaczy, jeżeli będziemy w stanie zmienić ten główny prototyp, to może się okazać, że pewne warunki logiczne przykładowo w aplikacji zachowają się inaczej niż wcześniej. Zobaczmy taki prosty przykład. Zrobię sobie zmienną, która będzie reprezentowała użytkownika i ta zmienna będzie zawierała np. id użytkownika, niech będzie 1337. I napiszę sobie tutaj takiego prostego ifa, że jeżeli użytkownik jest adminem, to ja wtedy wyświetlę log na konsoli, np. you are an admin. A ja oczywiście nie dostaję nic na konsoli, ponieważ ten użytkownik nie jest adminem, nie ma zdefiniowanej tej właściwości isadmin. Natomiast jeżeli w jakiś sposób, do tego w jaki sposób, to sobie przejdziemy jeszcze za chwilę. Natomiast jeżeli w jakiś sposób do głównego prototypu uda mi się dodać taką właściwość isadmin, to zobaczcie, że w tym momencie to sprawdzenie if user is admin jest zawsze prawdą, pomimo tego, że obiekt user tego konkretnego użytkownika nie ma tej właściwości isadmin, ale jest ona w prototypie. Więc ten warunek logiczny if user is admin zawraca w tym momencie prawdę. I to jest taki przykład, jak podmiana prototypu może wpłynąć na zmianę logiki w aplikacji. Co jeszcze jest istotne, to to, że mamy w JavaScript-cie taki operator in. Operator in sprawdza, czy właściwość o danej nazwie jest zdefiniowana w danym obiekcie. Natomiast właśnie tak naprawdę operator in nie sprawdza, czy ta właściwość jest zdefiniowana w obiekcie, tylko sprawdza, czy ta właściwość jest zdefiniowana w łańcuchu prototypów, czyli albo w obiekcie, albo w którymś z jego prototypów. Czyli jeżeli robiłbym sobie sprawdzenie isadmin in user, no to dostaję true, mimo że sam user nie ma tej właściwości, ale obiekt prototype ma tę właściwość. Czyli na razie wiemy tyle, że jeżeli w JavaScript-cie jesteśmy w stanie podmienić w jakiś sposób ten główny obiekt prototype, to możemy wpłynąć na logikę aplikacji. Pozwolę Wam jeszcze tylko ciekawostkę, że można w JavaScript-cie utworzyć obiekt, który będzie miał prototype null, czyli nie będzie dziedziczył po tym obiekt prototype. Czyli na przykład ten obiekt, który nazywa się ciekawostka, nie ma w tym momencie metody toString, ponieważ nie dziedziczy po obiekt prototype, więc nie ma skąd jej przejąć. Dobra, i teraz zobaczmy sobie praktyczny przykład, czym może skutkować właśnie nadpisanie prototypów w jakimś realnym przykładzie. I ponieważ tytuł tej prezentacji mówi o tym, że tutaj mi napis nie zniknął, żeby wyłączyć tryb pełnoekranowy. Coś tu nie do końca działa tak, jak powinno. Może spróbuję zmniejszyć i powiększyć jeszcze raz. I już jest OK. Dobra, ponieważ tytuł tej prelekcji czy opis tej prelekcji mówił o tym, że prototype pollution można wykorzystać do XSS-a, to zobaczmy sobie praktyczny przykład, jak to może się wydarzyć. Mam tutaj taką bardzo prostą aplikację, na której możemy wpisać jakiegoś HTML-a i ten HTML jest tutaj od razu renderowany. Możecie sobie wyobrazić, że w realnej aplikacji macie edytor WYSI-WYK, który pozwala na formatowanie tekstu. I teraz ja mogę sobie tutaj wrzucać fragmenty HTML-a, czyli na przykład jak wrzucę tak U, to mam podkreślenie, jak wrzucę tak B, to mam pogrubienie, jak bym wrzucił H1, to mam header i tak dalej. Widzimy, że ten HTML jest rzeczywiście renderowany. Natomiast pod spodem jest używana biblioteka, która nazywa się dompurify, która zapewnia mi, że w tym HTML-u, który tutaj wrzucę, czy może inaczej, przez ten HTML, który tutaj wrzucę, nie będzie się dało zrobić XSS-a. I pomimo, że ja tutaj wrzuciłem na przykład tak IMG z atrybutem onError, widzimy, że jakiś obrazek się tutaj pojawił, ale nie wyświetlił się żaden alert. No i to właśnie dompurify nam załatwia, że mamy tutaj ten obrazek w drzewie dom, ale atrybut onError został usunięty. Tak więc zadaniem dompurify jest właśnie to, że on sobie parsuje HTML-a, usuwa z niego to, co złośliwe i generuje nowego HTML-a. Teraz zobaczmy sobie dokładniej, jak dompurify działa. Ja tutaj założę sobie breakpointa na dompurify-u. Raczej jest tutaj taka metoda, która nazywa się, czy właściwie funkcja, która nazywa się sanitize. Spróbuję ją szybko znaleźć. Okej, założę sobie tutaj breakpointa i to zobaczymy sobie, co się będzie tutaj działo. Dobra, widzimy, że breakpoint założony i możemy sobie przechodzić po kolejnych liniach kodu. Na początku nie dzieje się tutaj zbyt wiele, natomiast widzimy, że do biblioteki dompurify można też podawać konfigurację, czyli na przykład zdefiniować, jakie tagi HTML-owe mają być dopuszczane, a jakie mają być odrzucane lub jakie atrybuty mają być dopuszczane, a jakie mają być odrzucane. No i jest tutaj metoda czy funkcja, której zadaniem jest parsowanie tej konfiguracji. Więc zobaczmy sobie, jak ona działa. Na początku sprawdza, czy jakaś konfiguracja została przekazana. Jeżeli nie, no to za konfigurację sobie ustawia po prostu pusty obiekt. Zobaczmy, co robi dalej dompurify. Wiele bibliotek javascriptowych robi coś podobnego. Ja pokazuję przykład na dompurify, bo to jest przykład, który swego czasu rzeczywiście badałem. Zobaczmy, że dompurify używa tutaj tego operatora in, czyli sprawdza, czy w konfiguracji znajduje się allowed tags. Jeżeli tak, to w jakiś sposób się wtedy zachowuje. Sprawdza, czy w konfiguracji istnieje allowed attr. I tak dalej, i tak dalej. Natomiast ze względu na fakt, że jest tutaj użyty operator in, to tak naprawdę tutaj nie jest sprawdzane, czy ta właściwość istnieje tylko w tym obiekcie konfiguracji, ale czy ta właściwość istnieje w łańcuchu prototypów. Więc jeżeli ja nadpiszę obiekt prototype w jakiś sposób, to to będzie właściwie zmodyfikować konfigurację dompurify. I jak tutaj byśmy sobie przejrzeli ten kod parsujący konfigurację dalej, to byśmy zobaczyli, że jest też taki parametr konfiguracyjny, który nazywa się attr. I on pozwala nam dodać dodatkowe atrybuty do listy dopuszczalnych atrybutów. Więc ja na początek to sobie zasymuluję. Czyli domyślnie dompurify blokował mi tutaj XSS, bo blokował ten atrybut onError. Natomiast ja zrobię teraz coś takiego, że do prototypu tego głównego obiektu, czyli do obiekt prototype, napiszę sobie nową właściwość, która nazywa się attr, i dodam tutaj onError. I w tym momencie, gdy dompurify jest uruchamiany jeszcze raz, bo zmodyfikowałem HTML-a, widzimy, że alert już się wyświetla. Czyli faktycznie poprzez napisanie prototypu udało mi się sprawić, że dompurify nie wykonuje do końca swojej pracy, a raczej jego konfiguracja została zmodyfikowana w taki sposób, żeby dopuszczał atrybut, który pozwala na zrobienie XSS-ów, czyli dopuszcza atrybut onError. No i teraz tak naprawdę zostaje nam ostatnie pytanie. Jak sprawić, co się realnie w aplikacji musiałoby stać, żebyśmy byli w stanie nadpisać ten obiekt prototype? No i tutaj właśnie jest taki research, który de facto zaczął się gdzieś w zeszłym roku, który pokazał, że zaskakująco dużo bibliotek ma błędy bezpieczeństwa, które pozwalają na nadpisywanie prototypów. Jest takie repozytorium na GitHub-ie, ClientSite PrototypePollution. Notabene mój tekst m.in. o tym obejściu dompurify za pomocą PrototypePollution jest tutaj zalinkowany. I mamy tutaj przykłady bibliotek, które w większości służą do parsowania parametrów podanych w adresie URL. Czyli przykładowo mamy jakąś tutaj bibliotekę, to już akurat jest naprawione, jak widać, ale mamy tutaj przykładowo bibliotekę Wistia Embedded Video i okazało się, że jeżeli sobie w adresie URL podamy taki parametr, podkreślenie proto i później w nawiasach kwadratowych test, to w tym momencie do Object Prototype zostanie dopisana właściwość test, która będzie miała wartość również test. I mogłoby się wydawać, że tego typu bibliotek, które będą miały taki błąd, nie powinno być dużo, ale okazuje się, że ich jest bardzo, bardzo dużo. I zobaczcie, że ten punkt wejściowy w prawie każdej bibliotece wygląda tak samo. Co prowadzi do takiego wniosku, że jak szukamy błędów bezpieczeństwa w aplikacji, to jest to taki nowy payload, którego warto używać, żeby po prostu sprawdzać, czy prototyp nie został podmieniony. I w przykładzie, który mam napisany, do którego za moment wrócę, używam biblioteki jQuery Deparam jako przykład, co tak naprawdę się tutaj dzieje. Zobaczmy, do czego służy biblioteka Deparam. Ona służy do parsowania właśnie parametrów, które możemy sobie podać na przykład w adresie URL. Czyli jak ja sobie sparsuję taki query string, że a równa się 123, b równa się 456, c równa się 789, to dostaję obiekt, który ma właśnie takie trzy właściwości. Czyli adres URL, czy raczej ten URL enkodowany query string jest parsowany do obiektu JavaScriptowego. To, co dodatkowo obsługuje Deparam, to to, że ja mogę tworzyć tutaj obiekty. Ja mogę sobie utworzyć właściwość test, która będzie miała podwłaściwość a o wartości 1 i będzie też druga właściwość b o wartości 2. I widać, że tutaj faktycznie powstał obiekt, który ma właściwość test i tutaj w środku jest a i b. I teraz tu może nie będę wchodził w kod źródłowy, żeby to dokładnie pokazać, natomiast spróbujmy sobie wyobrazić, jak działa logika takiej biblioteki jak Deparam w momencie, gdy my wpiszemy tego typu odnoszenie się do obiektów. To Deparam musi zrobić coś takiego. Jak widzi tą taką składnię z nawiasami kwadratowymi, czyli widzi tutaj obiekt, to musi sprawdzić, czy w tym obiekcie, które utworzyło, już istnieje taka właściwość test. Jeżeli nie, to tworzy pusty obiekt i do tego obiektu dodaje właściwość a. Następnie znowu sprawdza, czy w obiekcie, który Deparam utworzyło, jest już właściwość test. Okazuje się, że już jest, więc do tego obiektu dodaje nową właściwość b o wartości tutaj 2. Jest to taka, można by powiedzieć, naturalna logika, jak tego typu rozwiązanie można by napisać. I dlaczego tu jest problem? No bo zobaczmy, jak ja się odwołam do właściwości proto, to co robi teraz Deparam? Sprawdza, czy na obiekcie, które utworzyło, istnieje już właściwość proto. No i problem polega na tym, że na każdym obiekcie ta właściwość proto istnieje. I drugi problem polega na tym, że jak tworzymy sobie pusty obiekt, to ta właściwość proto tak naprawdę wskazuje na obiekt prototype. Dlatego biblioteka Deparam w tym momencie sprawdza, czy ta właściwość proto istnieje, stwierdza, że ok, istnieje, więc w proto otworzy sobie nową właściwość test. I zobaczcie, że faktycznie jak ja sobie tutaj dodam właściwość, może hak o wartości Deparam, to w tym momencie ja tutaj dostaję pusty obiekt, ale obiekt prototype ma w tym momencie nową właściwość. Czyli taka jest logika i praktycznie w każdej bibliotece ta logika jest dokładnie taka, że właśnie jest sprawdzane, czy właściwość o danej nazwie już istnieje i jeżeli tak, to w tym obiekcie są wówczas stworzone nowe właściwości. Natomiast problem jest taki, że właściwość proto istnieje domyślnie. Problemu by nie było, gdyby biblioteka Deparam i te wszystkie inne biblioteki tworzyły obiekty z prototypem 0, no to wówczas to proto faktycznie byłoby 0 i tutaj problemu by nie było. Ale tego nie robią, więc mamy ten problem bezpieczeństwa. Ok, więc teraz połączymy sobie jedno z drugim, to znaczy ta strona, którą tutaj napisałem, straciłem tutaj ten kod, który wpisałem, ale napiszę go jeszcze raz, czyli klasyczny kod na XSS-a. Ta strona, którą tutaj wpisałem, ona generuje takiego URL-a, gdzie ten tekst jest po prostu podany w adresie URL i pod spodem wykorzystuje bibliotekę Deparam, żeby ten adres URL tak naprawdę sparsować. I zobaczmy, jak ja sobie skopiuję ten adres i go wkleję, uruchomię stronę jeszcze raz, to automatycznie uzupełnia mi się ten kod z parametru. Więc skoro wiem, że mogę wykorzystać podatność Prototype Pollution, żeby zmienić działanie DOM Purify, żeby on dopuszczał dodatkowy atrybut, no to muszę to teraz tylko zrobić. Wkleję sobie ten URL tutaj do konsoli, żeby było go lepiej widać, żeby był duży i dopiszę sobie nowy parametr. I ten parametr będzie się nazywał Proto i w nawiasach kwadratowych będzie AtAt. On musi być arrejem, takie jest założenie w DOM Purify, więc tutaj jest taka standardowa spłatnia, że mam pusta nawiasy kwadratowe. Więc dodaję sobie do listy dopuszczalnych atrybutów nowy atrybut, który nazwę OnError. OK, teraz ten kod skopiuję sobie do schowka, wchodzę na takiego linka i widzimy, że natychmiastowo dostaję alert. Czyli w tym momencie zrobiłem XSS-a w tej aplikacji, pomimo faktu, że używa ona DOM Purify, czyli biblioteki, które stricte ma chronić przed XSS-ami. I to, że tego XSS-a tutaj zrobiłem, to de facto nie jest jakiś problem bezpieczeństwa samego DOM Purify, tylko raczej jest to problem bezpieczeństwa, można by powiedzieć, taki połączony, czyli fakt, że akurat używam tych dwóch bibliotek DEPA RAM plus DOM Purify pozwolił mi tutaj na wykorzystanie prototype solution do XSS-a. Zobaczmy sobie, jak to wygląda od strony kodu. Chcę Wam tylko pokazać, że tu nie ma żadnego magicznego kodu, który sztucznie dodaje podatność. To w momencie załadowania strony widzimy, że tutaj właśnie jest używany DEPA RAM na parametrach, które są w adresie URL. Jeżeli istnieje tam parametr tekst, to on jest przypisywany do tego elementu teksteria. Jeżeli on jest niezdefiniowany, to jest domyślna wartość, oto przykład wizytówki. I widzimy, że tutaj w update faktycznie jest używany DOM Purify Sanitize do tego, żeby wyczyścić HTML-a ze złośliwych elementów. Dobra, czyli wiemy z jednej strony, że mamy szereg bibliotek i niektóre z nich są naprawdę bardzo popularne, które pozwalają zatruwać prototypy. Pytanie, czy są inne biblioteki, poza na przykład DOM Purify, które pozwalają na wykorzystanie tego do XSS-a. I tutaj w tym samym repozytorium, clients.prototype.pollution, jak zjedziemy sobie trochę niżej, to mamy taki rozdział jak script.gadgets. I te tak zwane gadżety to są właśnie fragmenty kodu z wykorzystaniem różnych bibliotek, które pozwalają już zrobić XSS-a. Czyli to, co mamy u góry, pozwala nam zatruć prototyp, a to, co mamy tutaj w script.gadgets, pozwala nam wykorzystać zatrucie prototypu do zrobienia XSS-a. Innymi słowy, jeżeli chcielibyśmy tym sposobem znaleźć XSS-a w realnej aplikacji, to musimy mieć te obydwa kroki, czyli sposób na zatrucie prototypu plus jakiś fragment biblioteki, który pozwala nam na wykorzystanie zatrucia prototypu do XSS-a. Natomiast jak sobie zobaczymy te script.gadgets, to też okaże się, że tu jest masa popularnych bibliotek. Jest na przykład jQuery, więc zasadniczo każda aplikacja korzystająca z WordPressa, jeżeli miałaby prototype.pollution, to będzie się tam dało zrobić XSS-a właśnie z wykorzystaniem jQuery. Jak widać na jQuery jest masę sposobów. Tutaj ciekawostka, Google Rekapcza ma gadżet, który też pozwala zrobić na zrobienie XSS-a przez właśnie utworzenie w prototypie atrybutu src.doc. Tutaj są różnego rodzaju, może to odrobinę pomniejsze, tutaj są różnego rodzaju sanitizery HTML-a, czyli m.in. do Purify, ale nie tylko, bo w swoim researchu, który robiłem swego czasu, to znalazłem też tego typu wejścia w innych bibliotekach służących do czyszczenia HTML-a. Zobaczmy, że na przykład Lodasz, czyli też taka inna popularna biblioteka JavaScript-owa, swego czasu miała naprawdę sporo sposobów na wykorzystanie prototype.pollution. Z innych takich popularnych mamy tutaj Adobe DTM, czyli taka biblioteka, która zazwyczaj jest wykorzystywana do wrzucania jakichś skryptów marketingowych. Tutaj mamy Knockout.js, też taki framework JavaScript-owy można by powiedzieć. Mamy Vue.js, też framework JavaScript-owy, jak widać, na którym istnieje sporo sposobów. Na Reacta np. swoją drogą też istnieją sposoby na wykorzystanie prototype.pollution do XSS-a. Tu ich z jakiegoś powodu nie ma na tej stronie, ale istnieją. Więc zobaczmy, że jest tego naprawdę sporo. Jest tutaj też Google Tag Manager, więc był ten Adobe Tag Manager. Google Tag Manager też się tutaj pojawia. Google Analyticsy też się tutaj pojawiają. Dokładnie ten sam kod co w Tag Managerze. To wynika z tego, że część kodu jest współdzielona pomiędzy jednym i drugim. Gdybyście szukali inspiracji odnośnie tego, jak szukać tego typu błędów w żywych aplikacjach, to jest taki bardzo fajny research, dosyć świeży, chyba sprzed dwóch miesięcy, A Tale of Making Internet Pollution Free, autorstwa człowieka, który ma nick Sirius. I tutaj właśnie była ekipa bug hunterów, składająca się na oko gdzieś z około 10 osób, która po prostu masowo w programach bug bounty zaczęła szukać błędów typu prototype pollution. I summa summarum w trakcie tych badań znaleźli 18 podatnych bibliotek i zgłosili w okolicach, a myślę, że to było ponad 80 błędów do różnych programów bug bounty. Więc jest tego naprawdę bardzo dużo. Tutaj jest opisana metodyka, jakiej użyli do szukania i jak te błędy właśnie, jak podchodzili tak realnie do znajdowania tych błędów. Jednym z elementów tej metodyki, bo jednym z problemów, z jakimi musimy się zmierzyć, to jest właśnie znajdowanie tych gadżetów. Czyli ja Wam pokazałem gadżet w Dumpurify, natomiast pytanie, czy można to w jakiś sposób zrobić szybciej, niż tylko analizować kod źródłowy. I tutaj jest właśnie taki skrypt mojego autorstwa, który też w tym researchu był wykorzystany, który Wam jeszcze pokażę. Ten skrypt nazywa się Pollut.js. On jest udostępniony na GitHubie, jak dobrze pamiętam, to na GitHubie Securitum, a nie na moim prywatnym. Zobaczmy, czy dobrze pamiętam. Tak, na GitHubie Securitum, jak sobie poszukacie research prototype pollution, to tutaj właśnie jest ten skrypt Pollut.js. I ten skrypt służy właśnie do tego, żeby troszeczkę ułatwić znajdowanie gadżetów do prototype pollution. Pokażę Wam, jak ten skrypt działa. OK. I teraz tak. Będę musiał sobie pobrać plik javascriptowy z Dumpurify. Więc tutaj mogę wykorzystać na przykład cdm.js. No i tutaj pobiorę sobie ten skrypt, ale celowo pobiorę Dumpurify w nieco starszej wersji. W najnowszych pewne zabezpieczenia przed prototype pollution, przed tymi gadżetami zostały wprowadzone. Zobaczmy, czy plik się pobrał. Dobra. I teraz uruchomię ten swój skrypt, który nazywa się Pollut.js i właśnie uruchomię go na skrypcie katalogu Dumpurify. Tutaj dostaję informację, że nastąpi instrumentacja pliku javascriptowego. Co to dokładnie znaczy, za chwilę pokażę. Pliki zostaną zmienione, czy zostaną podmienione, więc jest tutaj tylko prośba o podpierdanie, czy na pewno chce to zrobić. Wpisałem, jest. Dobra. No i dodatkowo został mi wklejony taki szablonowy kod, który też za chwilę jeszcze sobie dodam. Okej. I teraz zobaczmy, co tak naprawdę się tutaj stało z tym skryptem w Purify.js. I tak naprawdę ten skrypt został podmieniony w taki sposób. Ja tutaj może pokażę jakiś przykład z tym parsowaniem konfiguracji. Zobaczcie, w tym miejscu, gdzie był operator in, tym razem jest użyta funkcja in. I tak naprawdę wszystkie wystąpienia operatora in zostały tutaj podmienione przez ten mój skrypt, a także wszystkie odwołania do właściwości javascripta też zostały podmienione przez getprop. I to, co mi to teraz daje, to to, że za każdym razem, jak jest wyzwalana ta funkcja in albo getprop, to ja sprawdzam, czy ta właściwość w tym obiekcie istnieje. Jeżeli nie istnieje, to ona będzie logowana na konsoli, dzięki czemu od razu dostanę informację o miejscu, które może być ciekawe pod kątem prototype pollution, bo będę wiedział, że w tym miejscu aplikacja odwołuje się do właściwości, która nie istnieje, więc jeżeli ta właściwość będzie istniała w object prototype, to będę w stanie wpłynąć na logikę aplikacji. I zobaczmy sobie teraz, jak to będzie działało. A tutaj teraz zgubiłem ten kod. Dobra, może ja to zrobię po prostu jeszcze raz. Czyli jeszcze raz uruchomię sobie curla, jeszcze raz wywołam polut.js. Dobra, i tutaj sobie po prostu zrobię HTMLa z tym takim szablonowym kodem. Ten szablonowy kod po prostu definiuje tę funkcję getprop i definiuje tę funkcję in. I na końcu załaduję sobie ten plik purify.js. I teraz wezmę sobie ścieżkę do pliku i uruchomię to w przeglądarce. I zobaczcie, że w tym momencie dostaję tutaj na konsoli takie logi, object trusted type i object document mode. Co oznacza, że DOM Purify próbował odwoływać się do właściwości document mode, która nie istniała na tym obiekcie, do którego próbował się odwoływać. Co oznacza, że gdyby document mode istniało w object prototype, to ja w tym momencie będę w stanie wpłynąć na logikę aplikacji. I tutaj w tym logu dostaję od razu stacktrace, gdzie jestem w stanie sobie łatwo przejść do linii, w której to odwołanie do document mode faktycznie zaistniało. Jak pamiętacie wcześniej, wykorzystaliśmy taką właściwość, która nazywała się add-add do dodania nowego atrybutu zapuszczonego przez DOM Purify. I zobaczmy, co się będzie działo, jak spróbuję użyć DOM Purify sanitize, żeby wyczyścić jakiegoś HTML-a. Zobaczcie, nie muszę teraz analizować kodu, tylko dostaję od razu w logu informację, że DOM Purify sprawdzał, czy istnieje taka właściwość allot-tags, czy istnieje allot-attr i tak dalej, i tak dalej. Na tej bazie od razu widzę, że są tutaj takie nazwy właściwości w obiekcie, którym warto było się przyjrzeć, jak właśnie pojawi się tutaj na przykład ten add-add, co wiedzieliśmy przy analizie kodu, ale tutaj dostajemy to niejako od razu. Przy okazji dostajemy inne ciekawe właściwości, być może ciekawe, to byłoby do dalszego zbadania. Widzimy, że jest tutaj właściwość body, czyli to jest nazwa tagu. Pojawia się tutaj też właściwość na przykład img, czyli to też jest nazwa tagu, więc to takie ciekawostki, że od razu wiem, w których miejscach aplikacja próbuje się odwoływać do nieistniejącej właściwości, od razu wiem, gdzie warto patrzeć. Notabene ten document mode faktycznie można też wykorzystać do obejścia do Purify. Ja o tym pisałem w takim swoim researchu na temat prototype pollution, który jest na blogu Securitum. Tutaj może od razu pokażę, że jeżeli w object prototype będziemy mieli taką właściwość document mode ustawioną na przykład na 9, to w tym momencie DOM Purify przestaje w ogóle dobrze działać. Zobaczmy to sobie. Tylko to będę musiał zmienić bezpośrednio tutaj w HTML-u, czyli przed załadowaniem DOM Purify ja napiszę sobie fragment skryptu, który mi zasymuluje prototype pollution i po prostu ustawię sobie tę właściwość document mode na 9. Widzimy, że teraz w blogu nie dostałem tego document mode. To wynika z tego, że ta właściwość istniała, a ja w blogu dostaję informacje tylko o tych właściwościach, które nie istniały. Teraz zobaczmy, że jak spróbuję uruchomić DOM Purify sanitize, to faktycznie dostaję tutaj niewyczyszczony HTML, dostaję dokładnie ten HTML, który dostałem na wejściu. To był taki inny gadżet do DOM Purify, który pozwalał na jego obejście za pomocą prototype pollution. Dla potwierdzenia pokażę, że jak zakomentuję ustawienie tego document mode na 9, to DOM Purify będzie działał dobrze, czyli DOM Purify sanitize wyczyściło mi HTML-a, i nie ma już tutaj tego złośliwego fragmentu JavaScripta. Czyli podsumowując, prototype pollution to jest podatność, która może nam pozwolić na zrobienie XSS-a, i będziemy do tego potrzebowali dwóch składników. Czyli po pierwsze, musimy mieć sposób na zatrucie prototypu, ale całkiem dużo bibliotek, które służą do parsowania adresów URL właśnie, jest podatna na zatrucie prototypu w taki sposób. Natomiast drugim elementem, jak już to mamy, jest wykorzystanie gadżetu w jakiejś innej bibliotece, albo nawet w tej samej, po to, żeby już faktycznie zatrucie prototypu wykorzystać do XSS-a. Pokazałem Wam, jak to zrobić w DOM Purify, natomiast na tym repozytorium na GitHub-ie jest sporo innych przypadków. To się nazywało client-side prototype pollution autorstwa BlackFam. OK. Dobra, jeżeli chodzi o tę prezentację, to wszystko, więc mam nadzieję, że zainspirowałem Was do nowego sposobu znajdowania XSS-u w aplikacjach. Na koniec pozwolę sobie jeszcze na taką małą reklamę. 20 i 21 grudnia prowadzę takie nowe szkolenie wprowadzające do OWAS Top 10. Będą tam omówione wszystkie punkty z OWAS Top 10, z tej najnowszej wersji 2021. Do wszystkich będą praktyczne przekłady, będzie dużo DEM. Nie będzie zadań w trakcie szkolenia, ale będzie taki CTF dookoła tego szkolenia, więc będzie okazja, żeby się zadaniami przećwiczyć. I tutaj jest kod rabatowy OWAS Pro Szkolenie, który zmniejsza cenę tego szkolenia o ponad 50%. Dobra, to wszystko z mojej strony. Mam otwartego w tle czata, więc pozwolę sobie w takiej formie odpowiedzieć na pytania. Jedno pytanie brzmi, dlaczego akurat document mode równa się 9, psuje działanie Don't Purify? Zajrzyjmy sobie na źródło Don't Purify. Ogólnie rzecz urmując, document mode to jest taka właściwość specyficzna dla Internet Explorera. Don't Purify na początku sprawdza, czy przeglądarka, na której działa, jest w stanie dobrze współdziałać z Don't Purify. I krótko mówiąc, jeżeli jest ustawiony document mode na 9, to znaczy, że przeglądarka, jakiej używamy, to jest IE9, ewentualnie starsza, tak to chyba było. I w tym momencie Don't Purify po prostu stwierdza, że nie wspiera tej przeglądarki. To wynika tak naprawdę z innych fragmentów kodu Don't Purify, że po prostu w tej wersji IE nie da się tej sanityzacji zrobić do końca dobrze, co wynika z pewnych błędów, które są w IE, no i które nigdy nie zostaną naprawione, bo przeglądarka nie jest już wspierana. I jeżeli ta zmienna is supported jest ustawiona na false, to Don't Purify próbuje wywołać taką funkcję to static HTML, która też istnieje w IE. Natomiast jeżeli ta funkcja też nie będzie istniała, to po prostu zwraca ten string, który dostała na wejściu, czyli w żaden sposób tego nie czyści. I widzę jeszcze jedno pytanie. Jak na razie wszystkie umawione podatności dotyczyły JS w przeglądarce, czy takie podatności zderzają się też w innych zastosowaniach, jak kod serwerowy, node desktopowy, elektron, czy mobilny React Native? Odpowiedź brzmi tak. Zderzają się tak naprawdę we wszystkich miejscach, gdzie występuje JavaScript. I tutaj mogę zalinkować do mojej starszej prezentacji. Ona jest publiczna. Jak sobie wejdziecie na slides.com, na moje konto SecurityMB, to tutaj w 2019 roku też miałem prezentację o PrototypePolution, ale to była prezentacja o serwerowym PrototypePolution. I tutaj w największym skrócie pokazałem, że w Kibanie dało się wykorzystać PrototypePolution do RCE, czyli do robienia shella. Czyli tak można by powiedzieć, po stronie mamy albo wykonywanie kodu po stronie klienta, czyli XSS, a tutaj mieliśmy wykonywanie kodu po stronie serwera, czyli właśnie RCE. I jeżeli a propos serwerowego PrototypePolution, jeżeli mielibyście ochotę się z tym troszeczkę przećwiczyć, to Twitter nie chce mi się załadować. Spróbuję na nowej zakładce, to czasami pomaga. Chyba, że Twitter ma jakąś awarię. W każdym razie na moim koncie twitterowym jakiś czas temu, całkiem niedawno, około miesiąc temu, linkowałem właśnie do takiego challenge'a, zadania CTF-owego na PrototypePolution. To zadanie wprawdzie się już skończyło i istnieje do niego write-up. Możecie sobie poczytać, jak to dokładnie działa. W każdym razie w tym zadaniu można też wykorzystać PrototypePolution do RCE, czyli do wykonania kodu po stronie serwera. Tutaj celem było wykonanie pliku flag, który znajduje się na serwerze. Ja może wrzucę na czat linka do tego challenge'a, natomiast on też jest na moim Twitterze, więc zachęcam do obserwowania. I tak jeszcze rzutem na taśmę na koniec przypominam o tym. W szkoleniu o Was powiem z kodem owasposzkolenia. Dziękuję bardzo za uwagę. Oddaję głos do studia.