15-07-2019 / Od 0 do pentestera

Jak przejąć serwer usuwając plik

Błędy w aplikacjach to nie tylko XSSy czy też SQL Injection.

Oprócz tych standardowych rodzajów błędów jest jeszcze cała masa mniejszych, które połączone razem w jedną całość dają ciekawy rezultat.

Dzisiaj właśnie o takich błędach - a mówiąc dokładniej - o błędnie zaimplementowanej funkcjonalności pozwalającej na usuwanie lub przenoszenie plików z katalogu.

Może się wydawać, że taki rodzaj błędów pozwala jedynie na usunięcie jakichś danych z serwera.

Ja pokażę jednak jak można go wykorzystać do wykonania zdalnego kodu.

Jenkins1 umożliwia automatyzację procesu budowania aplikacji.

W skrócie: ustawiamy tam wszystkie parametry naszego oprogramowania, a serwer co jakiś czas ściąga wszystkie potrzebne dane z systemów kontroli wersji i tworzy gotowe paczki.

Jenkins

Może on obsługiwać praktycznie każdy język programowania.

Pozwala zatem na sporą dozę elastyczności.

Ale wiąże się z tym pewna cena: różne języki mają różne metody budowania kodu oraz różne narzędzia.

Każdy projekt konfiguruje się nieco inaczej. Aby zapewnić tak dużą elastyczność i różnorodność Jenkins pozwala na wykonywanie własnego kodu na danym serwerze.

Oczywiście twórcy zdają sobie sprawę z ryzyka jakie to ze sobą niesie, dlatego też dostęp do tej funkcjonalności jest ograniczony.

Ostrzeżenie przed skryptami groovy

Co nie zmienia faktu, że jeżeli posiadamy do niej dostęp możemy wykonać dowolny kod.

Popatrzmy na przykład. Loguję się jako administrator i przechodzę do odpowiedniej zakładki.

Tutaj mogę wykonać dowolny skrypt Groovy na maszynie, na której znajduje się Jenkins.

Używając więc odpowiedniej składni wykonuję polecenie whoami i otrzymuję jego wynik.

Prawda że proste?

Groovy

To sprawia, że podczas przeprowadzania testów penetracyjnych tej aplikacji wystarczy, że atakujący uzyska dostęp do konta z odpowiednimi uprawnieniami.

No dobrze, ale jak tego dokonać?

Przecież dzisiaj miało być o usuwaniu i przenoszeniu plików.

Nie tak dawno w Jenkinsie odnaleziono błąd, który pozwala na przeniesienie jednego, konkretnego pliku do innego katalogu.

I to wystarczyło do przejęcia tego serwera.

Ale jak to możliwe?

Cała główna konfiguracja aplikacji znajduje się w pliku config.xml, który to umieszczony jest w katalogu JENKINS_HOME.

Jedną z ważniejszych opcji, którą można tam znaleźć to <useSecurity>, która powinna być ustawiona na True.

Config.xml

Ten to przełącznik odpowiedzialny jest za bezpieczeństwo całego systemu.

Jeżeli wskazuje on na False, Jenkins dostępny jest bez żadnego logowania.

To oznacza, że każda osoba ma dostęp do wykonywania skryptów Groovy - czyli przejęcia tego serwera.

Bez tej opcji nie potrzebuje do tego żadnego loginu ani hasła.

Zadaniem atakującego jest zatem zmiana wartości tej opcji.

Jeżeli ten plik nie istnieje - system przyjmuje, że ta wartość jest ustawiona na fałsz.

Ten sam efekt można zatem uzyskać usuwając ten plik.

Wiemy już co należy zrobić. Gdzie zatem znajdował się błąd?

Jenkins pozwala na logowanie na dwa sposoby.

Pierwszy - normalny - przy pomocy loginu i hasła poprzez formularz.

Drugi - poprzez klucz API. Dzięki niemu z serwerem można się komunikować przy pomocy zewnętrznych skryptów.

Klucz API przesyłany jest przy pomocy standardowego nagłówka Authorization.

Najpierw podaje się login, potem dwukropek i hasło, a całość koduje algorytmem base64.

Nagłówek Authorization

Każdy użytkownik posiada swój własny katalog na dysku, a w nim plik config.xml.

Tam to znajdują się wszystkie dane na temat konkretnej osoby.

W kodzie źródłowym znajdowała się metoda nazwana getUnsanitizedLegacyConfigFileFor.

Już sama nazwa Legacy, czyli spuścizna, świadczy o tym, że ten kawałek kodu obsługuje pewien archaiczny przypadek, który stoi w sprzeczności z obecnymi funkcjami.

Często to właśnie w tych starych funkcjonalnościach można odnaleźć błędy.

getUnsanitizedLegacyConfigFileFor

Kod ten sprawdzał, czy w katalogu danego użytkownika znajdował się plik config.xml.

Tylko że nazwa katalogu użytkownika pobierana jest bezpośrednio z nagłówka authorization.

A co najważniejsze, brakuje tutaj odpowiedniej walidacji i weryfikacji.

Nazwa ta może zatem zawierać w sobie dwie kropki.

Łącząc dwie kropki z nazwą config.xml otrzymujemy standardowy atak path traversal, kiedy to aplikacja podczas odczytywania pliku z dysku nie sprawdza danych z katalogu przewidzianego przez programistę a z katalogu, który znajduje się wyżej w hierarchii.

../ oznacza bowiem przejdź katalog wyżej.

A tak się akurat składa, że w katalogu wyżej również znajduje się plik config.xml.

Ten jednak nie przechowuje informacji na temat użytkownika, a na temat konfiguracji aplikacji.

Jeżeli funkcja stwierdziła, że taki plik istnieje - przenosiła go w inne, nowe miejsce.

Był to więc pewnego rodzaju automatyczny system upgrade'u ze starej funkcjonalności na nową.

Wykorzystanie błędu

Podsumowując: podając dwie kropki jako nazwę użytkownika w headerze authorization funkcja przenosiła plik config.xml z ustawieniami aplikacji do nowego katalogu.

Można to więc porównać do usunięcia tego pliku z serwera.

Pozostał jeszcze jeden drobny szczegół.

Uruchomiona aplikacja nie sprawdza swojej konfiguracji na bieżąco, a przechowuje ją w swojej pamięci.

Aby więc atakujący mógł uzyskać dostęp do serwera - musi poczekać aż zostanie on zresetowany.

Wbrew pozorom nie jest to taka rzadka czynność - Jenkins jest na bieżąco aktualizowany, a każda aktualizacja wymaga resetu aplikacji.

Po restarcie, aplikacja zauważy brak konfiguracji i uruchomi się bez odpowiednich zabezpieczeń.

Teraz wystarczy już tylko uruchomić skrypt grooviego wykonujący złośliwą akcję i tyle.

Pora na drugi błąd.

Tym razem WordPress, czyli najpopularniejszy system blogowy na świecie.

Tutaj również dane konfiguracyjne przechowywane są w specjalnym pliku wp-config.php.

 wp-config.php

Tam to skrypt przechowuje informacje o bazie danych, zazwyczaj do serwera MySQL.

CMS wszystkie dane pobiera właśnie z bazy danych - jeżeli więc nie może się z nią połączyć nie działa prawidłowo.

Dlatego też programiści założyli, że jeżeli taki plik nie istnieje na dysku - oznacza to, że aplikacja nie została jeszcze zainstalowana.

Uruchamia wtedy instalator - aby administrator mógł odpowiednio skonfigurować swoją stronę.

Dlaczego takie zachowanie jest niebezpieczne?

Ponieważ podczas instalacji tworzy się nowego administratora - który to posiada pełną kontrolę nad serwerem.

Mogłoby się wydawać, że atakujący nie jest w stanie odpowiednio skonfigurować aplikacji - wszak musi posiadać dane do logowania do serwera MySQL - których nie posiada.

Tylko, że serwer bazodanowy nie musi znajdować się na tym samym komputerze co skrypt.

Może się znajdować w zupełnie innej lokalizacji - kontrolowanej przez atakującego.

 Instalator WordPress.php

Jeżeli zatem atakujący może usunąć plik wp-config.php z serwera, może to wykorzystać do wykonania zdalnego kodu.

Schemat działania wygląda następująco:

Atakujący usuwa plik wp-config.php przy pomocy odnalezionej przez siebie podatności.

Ja ją tutaj zasymuluję - po prostu usuwając ten plik ręcznie.

WordPress zauważa, że nie jest zainstalowany i wyświetla instalator.

Haker podaje swoje dane oraz dane do zewnętrznego serwera MySQL.

Teraz może już zalogować się do panelu administracyjnego.

 RCE przy użyciu szablonu w WordPress

Następnie wystarczy tylko zmodyfikować treść szablonu.

One to są zwykłymi plikami PHP - mogą więc wykonywać dowolny, przekazany do nich kod.

Właśnie uzyskaliśmy dostęp do serwera i możemy przeglądać na nim wszystkie pliki.

Teraz już wiesz, że nawet drobne błędy mogą w specyficznych warunkach mieć duże konsekwencje.

Icon made by Freepik, Smashicons, srip from www.flaticon.com