Jak pewien komercyjny serwis aukcyjny przykłady OWASP Top 10 wdrożył

dodał 29 lutego 2016 o 06:58 w kategorii Błędy  z tagami:
Jak pewien komercyjny serwis aukcyjny przykłady OWASP Top 10 wdrożył

Gdy ktoś szuka w sieci przykładów błędów w aplikacjach WWW to często musi się trochę napracować. Problem ten postanowiła rozwiązać pewna polska firma, która w swoim serwisie aukcyjnym wdrożyła ich imponującą liczbę.

W zeszłym roku zostałem zaproszony by wygłosić prezentację dla osób zainteresowanych programowaniem. Zdecydowałem się wybrać temat rzadko poruszany podczas nauki programowania – błędy związane z bezpieczeństwem. Nie chciało mi się za bardzo wysilać, więc postanowiłem po prostu zaprezentować OWASP Top 10, czyli dziesięć najpopularniejszych błędów spotykanych w aplikacjach WWW. Zacząłem opowiadać o tych błędach i po opisaniu kilku otrzymałem pytanie z sali. Jeden ze słuchaczy chciał wiedzieć, kto dzisiaj, kiedy mamy dostęp do odpowiednich, często wbudowanych w dany język czy framework zabezpieczeń, popełnia tak podstawowe błędy.

Starałem się przekonać pytającego, że wbrew pozorom jest to wciąż problemem, ale, nieprzygotowany na takie pytanie, byłem pozbawiony odpowiednich przykładów. Słuchacze nie wydawali się usatysfakcjonowani moimi zapewnieniami i machaniem rękoma. Postanowiłem zatem spisać najciekawsze błędy które zaobserwowałem w ostatnim roku w Internecie. Błędy te zarówno były (czasami nawet wciąż są) obecne na zwykłych stronach internetowych jak i w panelach do zarządzania złośliwym oprogramowaniem. Podobnie jak w przypadku wspomnianej prezentacji, zebrałem je w formie odwołującej się do OWASP Top 10. Co więcej, niektóre z błędów są opisywane tutaj po raz pierwszy i dotyczą serwisów aukcyjnych tworzonych przez firmę JMLnet.

A1. Injection

Wstrzyknięcie (nie tylko SQL) pozwala użytkownikowi wykonać kod w sposób nieuprawniony. OWASP uznał to za najpopularniejszy błąd i widać to chociażby w kodzie poniższego panelu C&C, służącego do zarządzania złośliwym oprogramowaniem:

Przykład SQL Injection z panelu C&C

Przykład SQL Injection z panelu C&C

W pierwszych linijkach są wybierane zmienne select i search z przychodzącego żądania POST. Bez żadnej weryfikacji ich zawartości są wklejane do zapytania SQL. Stwarza to możliwość wywołania praktycznie dowolnego zapytania SQL w bazie danych. Oczywiście jest tu pewnego rodzaju przeszkoda – wywołanie prepare() wskazuje na to, że osoba pisząca aplikację słyszała o tym, że mechanizm SQL Prepared Statement może zapobiec atakom typu SQL Injection, ale użyła go niewłaściwie. Jest to jeden z najpopularniejszych przypadków błędnego użycia tego mechanizmu, spotykany także w komercyjnych aplikacjach.

Drugi przypadek jest troszkę bardziej skomplikowany. Część adresu zaprezentowana poniżej należy do pewnego polskiego serwisu aukcyjnego i użytkownik został do niego przekierowany po wpisaniu słowa „test” w pole wyszukiwania.

SQL Injection w serwisie firmy JMLnet

SQL Injection w serwisie firmy JMLnet

Bardziej wprawni informatycy zauważą, że w zmiennej „szukaj” znajdują się dane zakodowane za pomocą kodowania base64. Po ich rozkodowaniu otrzymujemy „ AND (p.tytul LIKE ‚%test%’)”. Ten ciąg znaków wyląda jak końcówka zapytania SQL. Nie mam niestety dostępu do kodu źródłowego, ani nie jestem upoważniony do przeprowadzania tego typu testów, ale można z dużym prawdopodobieństwem mogę stwierdzić, że kod ma podatność SQL Injection.

Innym rodzajem błędu typu Injection jest wstrzyknięcie np. skryptu języka PHP na stronę. Wydaje się to mało prawdopodobne, ale jest dosyć popularne, zwłaszcza jeśli umożliwiamy użytkownikowi wgrywanie własnych plików. Ten sam serwis aukcyjny napisany przez firmę JMLnet umożliwiał wgranie zdjęcia użytkownika do profilu. Następnie zdjęcie jest zapisywane jako:

Dodatkowo rozszerzanie nie jest porównywane z typowymi rozszerzeniami plików graficznych. Gdy użytkownik wgrywa plik z rozszerzeniem „.php” serwis wykonuje ten plik za każdym razem, kiedy użytkownik wchodzi na link prowadzący do zdjęcia. Zauważmy, że wykonanie to odbywa się na serwerze WWW serwisu. Powoduje to, że mamy dostęp do serwera na takim samym poziomie jak serwer WWW. Oczywiście sytuacja taka jest bardzo niebezpieczna.

A2. Broken Authentication and Session Management

Niekiedy sposób w jaki weryfikowane są uprawnienia czy tożsamość użytkownika potrafi być zepsuty do tego stopnia, że przypadkiem można się zalogować nie znając hasła. Tak się właśnie dzieje w przypadku panelu do zarządzania C&C, którego fragment kodu został zaprezentowany poniżej.

Błędna weryfikacja stanu logowania w C&C

Błędna weryfikacja stanu logowania w C&C

Jak widać, jeśli użytkownik nie jest zalogowany to do kodu HTML strony jest dodawany fragment odpowiedzialny za jego przekierowanie. Przekierowanie to odbywa się za pomocą języka JavaScript. Ponieważ później reszta strony jest tworzona normalnie to użytkownik, który ma wyłączone skrypty JavaScript w przeglądarce (np. za pomocą rozszerzenia NoScript) nawet nie zauważy przekierowania i zobaczy taką samą stronę jakby był zalogowany. Oczywiście w takiej sytuacji przekierowanie powinno być wykonane za pomocą kodu odpowiedzi HTTP 3xx z odpowiednio ustawionym nagłówkiem “Location”.

Kolejny przykład pochodzi z serwisu aukcyjnego firmy JMLnet. Po zalogowaniu się do serwisu dostajemy ciasteczko z następującymi informacjami:

To ciasteczko jest następnie wykorzystywane do naszej identyfikacji w całym serwisie. Takie podejście wiąże się z kilkoma, znacznie zmniejszającymi bezpieczeństwo, skutkami. Po pierwsze do zalogowania się użytkownika wystarczy sam skrót MD5 hasła, a nie hasło. Po drugie raz przechwycona sesja użytkownika jest ważna aż do czasu zmiany hasła (a nie do czasu wylogowania). W końcu po trzecie hasła w bazie są przechowywanie w postaci jawnej, bądź jako niesolone skróty MD5, co znacznie ułatwia ich odwrócenie.

Odwrócenie niesolonego skrótu prostego hasła jest możliwe nawet za pomocą wyszukiwania w Internecie. Istnieją strony, które mają zapisane proste hasła i ich skróty MD5. Dzięki temu, w przypadku wycieku bazy danych, jesteśmy w stanie odtworzyć prawdziwe hasło i użyć go w innych serwisach, w których ten sam użytkownik ma konta, mając nadzieję, że użył tego samego hasła. Solenie haseł pomaga, ponieważ tworzy dodatkowy, losowy ciąg znaków i dodaje go do hasła, aby uczynić je bardziej „niestandardowym” i trudniejszym do złamania. Należy też zwrócić uwagę, że wykorzystano skrót MD5, który jest już od dawna uznawany za niebezpieczny. Jednak ten typ błędu podlega pod kategorię A9, o której w dalszej części artykułu.

A3. Cross-site scripting (XSS)

Jeden z ulubionych błędów pracodawców rekrutujących na stanowiska pentesterów. Powstaje on w momencie gdy strona bezkrytycznie przyjmuje dane od użytkownika i wysyła je do przeglądarki internetowej bez odpowiedniego przetworzenia. Istnieją dwie główne rodziny błędów tego typu. Jedna polega na stworzeniu takiego adresu URL, aby część adresu została zinterpretowana jako HTML bądź JavaScript i została wykonana w kontekście strony. Następnie taki link wysyła się użytkownikowi. Można w ten właśnie sposób np. wykraść ciasteczka bądź inne dane wyświetlane użytkownikowi i przesłać je na zdalny serwer. Poniżej przykład takiego zachowania w serwisie aukcyjnym firmy JMLnet. Tekst z adresu URL jest bezkrytycznie prezentowany użytkownikowi. Zmiana tego tekstu na kod JavaScript pozwala na zewnętrzny dostęp do zawartości wyświetlanej strony internetowej.

XSS w serwisie aukcyjnym JMLnet

XSS w serwisie aukcyjnym JMLnet

Druga rodzina to błędy XSS, które polegają na tym, że kod JavaScript czy HTML jest zapisany do bazy danych, a następnie wyświetlany użytkownikom. Często nie trzeba wtedy stworzyć specjalnego adresu URL, a kod znajduje się pod publicznie dostępnym adresem. Jeden z bardziej popularnych zeszłorocznych błędów tego typu  został odkryty na blogu Oracle przez Andreasa Lindh (@addelindh) po opublikowaniu przez Mary Ann Davidson, szefową działu bezpieczeństwa, artykułu zabraniającego klientom wyszukiwania podatności w aplikacjach tej firmy. Poniżej zrzut ekranu prezentujący podatność. Jak widać, pole “URL” pozwalało na wpisanie kodu Javascript, który wykonywał się w kontekście strony blogs.oracle.com na komputerach użytkowników, którzy kliknęli w link.

Błąd w serwisie internetowym firmy Oracle

Błąd w serwisie internetowym firmy Oracle

A4. Insecure Direct Object References

To jeden z ciekawszych błędów, wynikających z przyjętej logiki działania aplikacji. Weźmy, na przykład, następujący sposób dodawania zaufanego odbiorcy w serwisie bankowości elektronicznej:

  1. Użytkownik tworzy nowego zaufanego odbiorcę podając numer rachunku, dane adresowe etc.
  2. System nadaje takiemu odbiorcy nowy, globalny numer identyfikacyjny np. 123456.
  3. Użytkownik chcąc wykonać przelew wybiera zdefiniowanego odbiorcę i do serwera systemu bankowości elektronicznej wysyłana jest informacja “Wyślij przelew od zalogowanego użytkownika do zaufanego odbiorcy numer 123456”.

Jeśli w takiej sytuacji serwis nie sprawdza czy odbiorca zaufany został dodany przez tego samego użytkownika który wykonuje przelew to pojawia się podatność. Pozwala ona na wykonanie z konta dowolnego użytkownika serwisu bankowości elektronicznej przelewu na konto odbiorcy zdefiniowanego przez innego użytkownika bez konieczności potwierdzania go kodem jednorazowym (bądź innym drugim składnikiem uwierzytelniania). Podatność pojawiła się, gdyż użyto globalnych identyfikatorów bez odpowiedniej autoryzacji. Jest to tylko jeden z czysto teoretycznych przykładów tego typu błędów, który z pewnością nigdy się nie wydarzył.

A5. Security Misconfiguration

Jednym z najpopularniejszych zeszłorocznych błędów tego typu były ataki na serwery wspierające przestarzałe ograniczenia eksportowe nałożone na niektóre szyfry. Najogólniej rzecz ujmując atakujący, jeśli znajdował się pomiędzy ofiarą a podatnym serwerem, mógł wymusić osłabienie algorytmów używanych przy szyfrowanych połączeniach. Wynikało to z zaszłości związanych z ograniczeniami dotyczącymi eksportu algorytmów kryptograficznych które były w przeszłości nałożone przez rząd USA. Ograniczenia te wymuszały stosowanie łatwiejszej do złamania kryptografii, która dzisiaj jest zupełnie niewystarczająca. Oczywiście po zniesieniu tych ograniczeń wiele serwerów (w szczególności te obsługujące nsa.gov) wciąż je wspierało, umożliwiając używane słabszych algorytmów. Wynikało to zapewne z nieuwagi bądź opieszałości administratorów tych serwerów. Na przykład, błąd o nazwie Logjam (CVE-2015-4000) pozwalał na podsłuchiwanie (i wpływanie na treść) połączenia korzystając tylko z jednego serwera i obliczeń trwających minutę.

A6. Sensitive Data Exposure

Tę kategorię można zilustrować na dwa sposoby. Jednym rodzajem danych wrażliwych są dane, które dotyczą użytkowników serwisu: ich numery kart kredytowych czy hasła. Jednym z najbardziej znanych, wciąż obecnych przykładów tego błędu jest przesyłanie strony logowania do poczty elektronicznej za pomocą protokołu HTTP i ograniczenie szyfrowania tylko do przesłania loginu i hasła. Wynika to z chęci ograniczenia zasobów wymaganych do obsługi serwisu. Jednak wtedy atakujący znajdujący się między użytkownikiem a serwerem jest w stanie zmusić użytkownika do wysłania danych najpierw do niego, a dopiero później do serwisu. De facto takie podejście zapewnia bezpieczeństwo tylko przed pasywnym atakującym. W przypadku aktywnego atakującego ofiara nie jest w stanie zauważyć ataku jeśli nie spojrzy za każdym razem w kod otrzymanej strony. Jednym z przykładów wciąż pozwalających na to serwisów jest poczta.o2.pl. Serwis aukcyjny firmy JMLnet z kolei zupełnie nie używał protokołu HTTPS na stronie logowania (!), co prowadzi do jeszcze większych problemów. Wszak w ten sposób można podsłuchać hasło i login użytkownika np. w otwartej sieci WiFi.

Drugą kategorią są dane wrażliwe z punktu widzenia administratora strony. Jak na przykład poniższy kod błędu z publicznie dostępnej testowej wersji serwisu transakcyjnego pewnego polskiego banku, gdzie widać login, hasło oraz adres maszyny na której stoi serwer bazy danych.

owasp_a6_1

A7. Missing Function Level Access Control

Przykładem błędu polegającego na nieodpowiedniej weryfikacji uprawnień po stronie serwera jest poniższy fragment kodu panelu C&C.

owasp_a7_1

Błędna kontrola dostępu do funkcji „delete” w kodzie C&C

Pierwszy warunek jest wykonywany jeśli użytkownik ustawi zmienną POST o nazwie “delete”. Następnie dla każdego identyfikatora jest usuwany odpowiedni wpis w logach. Autor kodu nawet odpowiednio sprawdza czy użytkownik podał rzeczywiście tylko numeryczne identyfikatory. Mimo to sprawdzenie czy użytkownik jest zalogowany znajduje się dopiero po kodzie kasującym wpisy. Oznacza to że niezalogowany użytkownik może usunąć wszystkie logi z serwera. Oczywiście niezalogowany użytkownik nie zobaczy takiej opcji w interfejsie C&C – musi sam wykonać odpowiednie żądanie.

A8. Cross-site request forgery (CSRF)

Kolejny z ulubionych błędów rekruterów. Polega on na tym, że jedna strona może wykonać żądanie do zupełnie innej strony, wykonując akcję za zalogowanego użytkownika. Wyobraźmy sobie, że jesteśmy zalogowani do bankowości elektronicznej i w tym samym czasie przeglądamy zupełnie niezwiązane strony. Jedna ze stron wykonuje żądanie GET lub POST do adresu nasz-bank.xyz/wykonaj/przelew z odpowiednimi parametrami. Ponieważ jesteśmy zalogowani to bank przyjmuje polecenie wykonania przelewu i go wykonuje. W rzeczywistości coś takiego nie mogłoby mieć miejsca, gdyż bank do każdego formularza wykonania przelewu powinien dołączać losowy ciąg znaków tzw. „CSRF token”. Powoduje to, że strona trzecia nie może odgadnąć tego losowego ciągu znaków i nie może wykonać polecenia przelewu. Innym mechanizmem zabezpieczającym jest Same-Origin Policy (SOP). Powoduje on, że strona spod jednej domeny nie może odczytać zawartości żądania GET lub POST, które wysyła do innej domeny. Bardzo ważne jest zauważenie, że dotyczy to tylko niemożliwości odczytania odpowiedzi, a nie samego wysłania żądania. Dlatego też tokeny CSRF są również istotne.

Niestety, żaden z prezentowanych w tym artykule paneli C&C nie implementował tokenów CSRF. Oznacza to, że, jeśli przeglądarka przestrzega podanej wyżej wersji SOP oraz atakujący jest zalogowany do panelu C&C i wejdzie na naszą stronę, jesteśmy w stanie po prostu przejąć botnet.

A9. Using Components with Known Vulnerabilities

Używanie podatnych komponentów przeważnie wynika z niewiedzy lub zwykłego zaniedbania administratora serwisu. Pomimo tego, że Shellshock – rodzina błędów dotyczących obsługi zmiennych w Bash – odkryty został we wrześniu 2014 roku, do dzisiaj obserwujemy ataki wykorzystujące tę podatność. Pewnie będzie to trwało jeszcze przez następne kilka lat. Poniżej znajduje się jeden z przykładowych ataków zaobserwowany w październiku 2015 przez honeypot należący do fundacji The Honeynet Project.

Przykład ataku na sewer honeypot

Przykład ataku na sewer honeypot

Jak widać atak ma za zadanie pobranie skryptu języka perl i nieuprawnione wykonanie go po stronie serwera. Skrypt ten był IRCBotem (analiza na VirusTotal), którego celem była kontrola zainfekowanego serwera. Obecność takich ataków potwierdza, że wciąż są osiągalne niezałatane serwery.

A10. Unvalidated Redirects and Forwards

Jeden z najtrudniejszych do wykorzystania błędów, który wymaga odrobiny socjotechniki. Najczęściej, dla wygody użytkownika, gdy klikniemy w link, który wymaga bycia zalogowanym serwis przenosi nas do strony logowania. Po zalogowaniu wracamy wtedy do tej strony, na której byliśmy. Jest to możliwe dzięki przekazaniu odpowiedniego parametru w żądaniu (najczęściej) GET. Czasami jednak serwer nie sprawdza czy ten parametr prowadzi rzeczywiście do strony w serwisie. Może się zdarzyć, że atakujący wykorzysta tę podatność i wyśle nam link do logowania prowadzący do oryginalnej strony, który, po zalogowaniu, przeniesie nas na stronę atakującego. Strona ta wygląda jak strona logowania do serwisu wzbogacona o znany nam komunikat “Nieprawidłowy login bądź hasło. Proszę spróbuj ponownie.” Powoduje to, że automatycznie (bez sprawdzania paska adresu) wpisujemy dane logowania. Tylko, że tym razem trafiają one do atakującego.

Dokładnie z takim przypadkiem mamy do czynienia poniżej, w zaprezentowanym kawałku adresu URL z serwisu aukcyjnego stworzonego przez firmę JMLnet. Jak widać adres, na który zostaniemy przekierowani po zalogowaniu jest znowu zakodowany za pomocą base64. Wystarczy zatem zakodować inny adres, aby po udanym zalogowaniu być przekierowanym na stronę wyłudzającą nasz login i hasło.

Open redirect w serwisie aukcyjnym JMLnet

Open redirect w serwisie aukcyjnym JMLnet

Podsumowanie

Jak widać nawet w zeszłym roku można było w popularnych, komercyjnych serwisach znaleźć przykłady na każdy z błędów OWASP Top 10. Przypomina to, jak ważne jest wykonanie audytu kodu zanim zostanie on umieszczony na serwerze produkcyjnym i przestępcy wykonają ten audyt za nas. Błędy w serwisie aukcyjnym firmy JMLnet są jednymi z najbardziej podstawowych i widać, że można stworzyć i sprzedawać serwis aukcyjny bez znajomości OWASP Top 10.

Oczywiście błędy znalezione w serwisie firmy JMLnet od razu zgłosiłem do autorów oprogramowania. Komunikacja z firmą przebiegała następująco:

  • 27.12.2015 – pierwszy kontakt, informacja o znalezieniu podatności
  • 28.12.2015 – prośba o szczegóły, nieudana próba wymiany kluczy PGP
  • 28.12.2015 – przekazanie całości wiedzy na temat podatności, rozmowa
  • 29.12.2015 – JMLnet proponuje termin publikacji informacji o podatnościach na „koniec stycznia 2016”
  • 05.02.2016 – pierwsze przypomnienie o terminie z zapytaniem o możliwość publikacji, brak odpowiedzi
  • 11.02.2016 – drugie przypomnienie o terminie z zapytaniem o możliwość publikacji, brak odpowiedzi
  • 29.02.2016 – publikacja tego artykułu

Pierwszy kontakt był naprawdę profesjonalny i uprzejmy, dlatego zdziwiłem się, gdy później się tak nagle urwał. Szkoda.