Przegląd podatności na przykładzie aplikacji DVWA

27 czerwca, 2023

DVWA - hackowanie web aplikacji

DVWA (Damn Vulnerable Web Application) to celowo błędnie zabezpieczona aplikacja napisana w języku PHP. Umożliwia naukę wykorzystania popularnych podatności w praktyce, a dzięki dostępowi do kodu źródłowego pozwala na zrozumienie ich przyczyn, jak również metod ochrony. Składa się z trzech „poziomów trudności” (Low, Medium i High), w których stosowane są coraz mocniejsze zabezpieczenia do obejścia. Dodano także poziom Impossible, gdzie aplikacja powinna być odporna na ataki dostępne na wcześniejszych poziomach — tutaj celem jest już wyłącznie analiza odpowiednio bezpiecznego kodu.

Do uruchomienia DVWA wystarczy prosta konfiguracja serwera WWW (autorzy w wymaganiach podają Apache), PHP i bazy MySQL (MariaDB). W przypadku systemu Windows bardzo zbliżone środowisko zapewnia pakiet XAMPP. Można też wykorzystać Docker. Wszystkie podstawowe instrukcje uruchomienia zostały opisane w repozytorium projektu. Bardziej zaawansowana konfiguracja nie jest wymagana. Aplikacja nie korzysta przykładowo z reguł rewrite, nie potrzebuje też optymalizacji wydajności po stronie serwera.

Warto zauważyć, że DVWA nie powinna być dostępna bezpośrednio z zewnątrz, ponieważ możliwa jest jej szybka kompromitacja, a w efekcie dostęp do naszego systemu, jak również sieci (jeśli serwer nie został odpowiednio odizolowany od reszty hostów). W celu testowania różnych podatności musimy się co prawda zalogować, ale niektórzy mogą zapomnieć o możliwości zmiany domyślnego hasła. Dlatego najlepszym rozwiązaniem jest uruchomienie DVWA w lokalnym środowisku.

Wykorzystamy kilka ciekawych podatności wraz z ich wyjaśnieniem.

Konfiguracja DVWA

W pliku config/config.inc.php podajemy dostępy do bazy danych oraz (ewentualnie) klucze usługi reCAPTCHA (na ten moment działa wyłącznie v2). Możemy też od razu określić poziom trudności ustawiając wartość elementu default_security_level tablicy $_DVWA (pracujemy w trybie Low). Następnie przechodzimy pod adres <domena>/setup.php. Wszystkie testy powinny być w kolorze zielonym, co oznacza ich zaliczenie.

DVWA, setup check

Klikamy przycisk Create / Reset Database, po czym zostaniemy przeniesieni na stronę logowania. Domyślne poświadczenia to admin / password. Na początku spróbujemy złamać hasła pozostałych użytkowników aplikacji.

 

Brute Force

Jeśli sprawdzimy strukturę bazy danych aplikacji DVWA, to zobaczymy, że zakodowane hasła są przechowywane w kolumnie password tabeli users. Wyeksportujmy tę kolumnę do pliku poleceniem:

mysql -u dvwa -p dvwa -e "select password from users" > passwords

Odpowiednie nazwy logowania użytkowników zostały zapisane w kolumnie user, którą też możemy analogicznie przekierować do pliku. Ta metoda eksportu dodaje w pierwszej linii nazwę kolumny, która nie jest nam w tym momencie potrzebna, dlatego możemy ją usunąć, wykonując:

sed -i '1d' passwords

Autorzy aplikacji użyli powszechnie uznawanego za słaby algorytmu MD5. Aby to udowodnić, wystarczy prostym zapytaniem ustawić inne hasło dla użytkownika admin:

update users set password = md5("zaq1@WSX") where user like "admin";

Logowanie od teraz będzie możliwe hasłem zaq1@WSX. Do łamania pozostałych czterech ciągów można użyć narzędzia John The Ripper dostępnego w Kali Linux (można zainstalować na dowolnej innej dystrybucji). Kali ma też wbudowane słowniki, z których chyba najbardziej znanym jest rockyou.txt. Użyjemy go do ataku brute force. Znajdziemy go w lokalizacji /usr/share/wordlists/rockyou.txt (oryginalnie jest skompresowany w formacie gzip, należy więc użyć np. polecenia zcat i przekierować strumień). Pełne zastosowane polecenie john wygląda następująco:

john -w /usr/share/wordlists/rockyou.txt --format=raw-md5 passwords

Parametrem -w wskazujemy ścieżkę do pliku słownika, –format służy do jawnego określenia użytego kodowania (co przyspiesza całą operację), a passwords to nazwa pliku z zakodowanymi hasłami. Łamanie nie będzie długo trwało z uwagi na słabość algorytmu MD5 oraz niewielką ilość ciągów do złamania. Wyniki zostaną wypisane na ekranie:

john

Otrzymaliśmy hasła dla użytkowników, zgodnie z ich kolejnością w kolumnie user (wyłączając rekord 1337). Przykładowo dla użytkownika pablo hasłem jest letmein. Udało się nam się skompromitować hasła, a tym samym przejść pierwsze z zadań.

brute force

Na dole każdej podstrony z daną podatnością znajdziemy odnośniki do listingu kodu źródłowego oraz pewne wskazówki dla każdego z poziomów trudności. W tym konkretnym zadaniu poważnym błędem jest także przesyłanie poświadczeń metodą GET (zamiast POST). Skutkiem jest możliwość podglądu loginu i hasła nie tylko w pasku adresu przeglądarki, ale też w logach serwera WWW czy proxy (gdy z niego korzystamy). Szyfrowane połączenie niczego tutaj nie zmienia. Więc nawet jeśli solidnie zabezpieczyliśmy hasła w bazie, to w sytuacji, gdy atakujący uzyska dowolnym sposobem dostęp do serwera, będzie teoretyczne w stanie przejąć nasze poświadczenia, analizując logi.

url get

Fragment logów serwera Apache:

				
					192.168.1.103 - - [24/Jun/2023:17:08:58 +0200] "GET /vulnerabilities/brute/?username=pablo&password=letmein&Login=Login HTTP/1.0" 200 1734 "http://dvwa.test.local/vulnerabilities/brute/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
				
			

Command Injection

Ta strona pozwala na przeprowadzenie testu ping. Podobne rozwiązania spotyka się często w panelach zarządzania różnych urządzeń sieciowych. W tej aplikacji wpisanie adresu IP lub domeny zwraca output polecenia ping -c 4. Widać to w kodzie źródłowym:

				
					// Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }


				
			

Skrypt sprawdza, w jakim środowisku jest wykonywany (ma to znaczenie, ponieważ ping w Linuxie domyślnie jest sprawdzany nieskończenie, przed czym zabezpiecza właśnie użycie parametru -c) i po prostu wykonuje polecenie w systemie. Nie ma żadnej weryfikacji zawartości zmiennej $target, co jest tym gorsze, że funkcja shell_exec operuje bezpośrednio w powłoce.

Wystarczy więc wpisanie | (pipe), co w systemach unixowych pozwala na przekazanie wyniku jednego polecenia do następnego. Jako ciekawostkę dodam, że obsługa funkcjonalności potoków została zaimplementowana po raz pierwszy przez Kena Thompsona w roku 1973, a zajęło mu to jedną godzinę.

Korzystając z tej możliwości, możemy sprawdzić np. na jakim użytkowniku działa aplikacja (|whoami) czy wylistować (|cat) plik /etc/passwd zawierający listę wszystkich użytkowników w systemie.

command whoami
command passwd

File Upload

Analizując kod tej funkcjonalności, zauważamy, że możliwe jest przesłanie dowolnego pliku (brak ograniczenia rozszerzenia), jak również brak zmiany jego nazwy na losową. W połączeniu ze stałą ścieżką powoduje to zagrożenie bezpieczeństwa (a przy okazji nadpisanie naszego pliku, gdy inny użytkownik prześle swój plik o takiej samej nazwie). Nie musimy zresztą analizować kodu, ponieważ ścieżka do pliku jest wyświetlana po poprawnym jego przesłaniu. Pliki są zapisywane w katalogu hackable/uploads.

ssh ls

Przykładem szkodliwego pliku będzie skrypt PHP, który utworzy plik o rozmiarze 10 GB. Jeśli użytkownik ma ustawione limity quota lub po prostu kończy się miejsce na dysku, takie działanie może zaburzyć działanie aplikacji (w pewnych sytuacjach także samego systemu). Zawartość skryptu:

<?php exec(“fallocate -l 10G test”);

Wykonanie go z poziomu przeglądarki utworzy w katalogu hackable/uploads plik test. Jeśli limit quota zostanie przekroczony, plik będzie miał odpowiednio mniejszy rozmiar (limit i tak zostanie wyczerpany).

Bardziej zaawansowanym sposobem kompromitacji usługi będzie przesłanie pliku, który po wykonaniu nawiąże połączenie z hostem kontrolowanym przez atakującego, dając mu dostęp do powłoki (reverse shell) na prawach użytkownika aplikacji. Opis tworzenia takiego pliku za pomocą msfpc został opisany w innym z naszych tekstów. Dzięki temu uzyskaliśmy dostęp do powłoki serwera, poprzez którą możemy wykonywać dalsze polecenia.

reverse shell

Insecure CAPTCHA

Jedno z zadań polega na obejściu zabezpieczenia CAPTCHA i zmianie hasła zalogowanego użytkownika. Użyjemy w tym celu pakietu Burp Suite, w którym przechwycimy, a następnie odpowiednio zmodyfikujemy żądanie wysyłane do serwera.

captcha

Burp Suite jest dostępny w Kali Linux, natomiast nic nie stoi na przeszkodzie, aby dokonać instalacji na innych systemach. Po uruchomieniu przechodzimy do zakładki Proxy i aktywujemy tę opcję poprzez wybór przycisku Intercept is off. Na karcie Proxy settings w sekcji Proxy listeners zobaczymy, że serwer proxy działa lokalnie na porcie 8080. W wybranej przeglądarce musimy ustawić ten adres jako proxy dla protokołu HTTP.

Jeśli proxy działa poprawnie i jest osiągalne przez przeglądarkę, to po wejściu na adres serwera DVWA strona będzie ładować się w „nieskończoność” dopóty, dopóki nie klikniemy Forward w Burp Suite. Pozwoli to na łatwe modyfikowanie żądań HTTP. Wygodnie jest ustawić okna przeglądarki i Burp Suite obok siebie.

Jeśli spróbujemy wpisać nowe hasło w oba pola formularza i bez rozwiązywania CAPTCHA wybierzemy CHANGE, w Burp Suite powinniśmy zobaczyć:

burp url

Po zezwoleniu na to żądanie bez żadnych modyfikacji aplikacja zwróci błąd The CAPTCHA was incorrect. Please try again. Jeśli jednak przeanalizujemy kod źródłowy, łatwo dostrzeżemy, że obejście zabezpieczenia jest możliwe.

				
					if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) {
    // Hide the CAPTCHA form
    $hide_form = true;

    // Get input
    $pass_new  = $_POST[ 'password_new' ];
    $pass_conf = $_POST[ 'password_conf' ];

    // Check to see if both password match
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $pass_new = md5( $pass_new );

        // Update database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // Feedback for the end user
        echo "<pre>Password Changed.</pre>";
    }

				
			

Wartość zmiennej $step powinna być ustawiona na 2, a zawartość $pass_new i $pass_conf musi być taka sama (walidacja jest po stronie serwera — tego akurat nie obejdziemy). Jeśli te warunki są spełnione, do serwera MySQL wysyłana jest kwerenda aktualizująca hasło użytkownika. Dodatkowo okazuje się, że wartość widocznego w podglądzie zapytania parametru g-recaptcha-response musi mieć jakąkolwiek wartość, aby skrypt wykonywał się dalej, co dla nas oznacza skuteczne przeprowadzenie ataku.

				
					$resp = recaptcha_check_answer(
        $_DVWA[ 'recaptcha_private_key'],
        $_POST['g-recaptcha-response']
    );

    // Did the CAPTCHA fail?
    if( !$resp ) {
        // What happens when the CAPTCHA was entered incorrectly
        $html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
        $hide_form = false;
        return;
    }

				
			

Warto zauważyć, że analiza kodu nie była potrzebna do udanego obejścia CAPTCHA, ponieważ można było się domyślić odpowiednich wartości parametrów.

Modyfikujemy więc odpowiednio żądanie w Burp Suite i je przekazujemy. Ostatecznie hasło zostanie zmienione, co zobaczymy w bazie danych:

MariaDB [dvwa]> select password from users where user like "admin";
+----------------------------------+
| password                         |
+----------------------------------+
| 676eff79d9a5c0cdfb5a861bcf47a525 |
+----------------------------------+
1 row in set (0.001 sec)

 

Stored Cross Site Scripting (XSS)

Ta konkretna odmiana ataku XSS oznacza, że dodany kod jest przechowywany po stronie serwera, a nie tylko klienta. W związku z tym potencjalnie każdy odwiedzający może być „ofiarą” złośliwego działania.

W testowanej aplikacji dodane komentarze są wyświetlane każdemu zalogowanemu użytkownikowi.

Spróbujemy wykorzystać JavaScript do wyświetlenia komunikatu po wejściu i odświeżeniu strony oraz do zmiany koloru tła na niebieski. Niezbędne będzie dodanie poniższych kodów (odpowiednio pola Name i Message):

<script>alert("Przykład XSS")</script>
<script>document.body.style.backgroundColor = "blue"</script>

Niestety nie będzie od razu takiej możliwości. Jeśli w DevTools podejrzymy kod tego formularza, zobaczymy dwa atrybuty maxlength.

devtools

Należy je po prostu usunąć, aby przeglądarka przyjęła większą ilość znaków. Po zapisaniu formularza natychmiast zobaczymy efekt naszego działania.

xss bg

Dodanie wyskakującego komunikatu czy zmiana tła to jednak stosunkowo niewinne przykłady ataku XSS. Zdecydowanie gorszym przypadkiem jest przekierowanie użytkownika na inną stronę, do czego służy właściwość window.location.href:

<script>window.location.href = "https://avlab.pl";</script>

 

SQL Injection

Podanie ID użytkownika zwraca nam wartości pól First name i Surname. Jeśli sprawdzimy kod tej podstrony, zauważymy, że zapytanie SQL z ID użytkownika ma postać:

$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

Nieautoryzowane zwrócenie wszystkich rekordów (z danymi wszystkich użytkowników) może ograniczyć się przykładowo do:

5' or 5='5

Zadziałają dowolne wartości, ale dwie ostatnie muszą być oczywiście takie same. Silnik bazy danych zwróci nam wszystkie dane, ponieważ warunek 5=’5 zawsze jest prawdziwy. Nie jest to wada języka SQL, a problem w kodzie, bo wszelkie dane pochodzące od użytkownika powinno się walidować.

Użycie apostrofów jest związane wyłącznie z zapytaniem — przekazywany ID jest w apostrofach (pomimo że w przypadku liczb nie jest to niezbędne), dlatego należało go sztucznie domknąć. Nasze zapytanie wygląda więc tak:

SELECT first_name, last_name FROM users WHERE user_id = '5' or 5='5';

Jeśli interesują nas konkretne rekordy z tej tabeli, możemy też podać:

1' or user_id = '5

W efekcie otrzymamy:

ID: 1' or user_id = '5
First name: admin
Surname: admin
ID: 1' or user_id = '5
First name: Bob
Surname: Smith

____________

Zapoznanie się z DVWA na pewno jest pouczającym doświadczeniem. Pozwala zrozumieć różne błędy w aplikacjach webowych oraz wykorzystać niektóre narzędzia do ich testowania. Widać też, że testy zabezpieczeń wymagają dość kompleksowej wiedzy z wielu dziedzin, jak programowanie, administracja systemami czy też sieci komputerowe.

Picture of Michał Giza

Michał Giza

Autor tekstów na portalu Fundacji AVLab dla Cyberbezpieczeństwa. Administrator systemów Linux i Windows Server. Zajmuje się także bezpieczeństwem sieci.
Picture of Michał Giza

Michał Giza

Autor tekstów na portalu Fundacji AVLab dla Cyberbezpieczeństwa. Administrator systemów Linux i Windows Server. Zajmuje się także bezpieczeństwem sieci.

PODZIEL SIĘ:

guest
0 komentarzy
Inline Feedbacks
View all comments

Wyrażam zgodę na przesłanie oferty drogą telefoniczną przez IT Partners security sp. z o.o. z siedzibą Katowicach ul.Padereskiego 35 na podany przeze mnie adres e-mail zgodnie z ustawą z dnia 10 maja 2018 roku o ochronie danych osobowych (Dz. Ustaw z 2018, poz. 1000) oraz zgodnie z Rozporządzeniem Parlamentu Europejskiego i Rady (UE) 2016/679 z dnia 27 kwietnia 2016 r. w sprawie ochrony osób fizycznych w związku z przetwarzaniem danych osobowych i w sprawie swobodnego przepływu takich danych oraz uchylenia dyrektywy 95/46/WE (RODO).

Wyrażam zgodę na przesłanie oferty drogą mailową przez IT Partners security sp. z o.o. z siedzibą Katowicach ul.Padereskiego 35 na podany przeze mnie adres e-mail zgodnie z ustawą z dnia 10 maja 2018 roku o ochronie danych osobowych (Dz. Ustaw z 2018, poz. 1000) oraz zgodnie z Rozporządzeniem Parlamentu Europejskiego i Rady (UE) 2016/679 z dnia 27 kwietnia 2016 r. w sprawie ochrony osób fizycznych w związku z przetwarzaniem danych osobowych i w sprawie swobodnego przepływu takich danych oraz uchylenia dyrektywy 95/46/WE (RODO).

[ninja_tables id=”27481″]