Sql injection jest jedną z najpopularniejszych i najczęściej wykorzystywanych technik mających na celu nieautoryzowane uzyskanie dostępu do bazy danych. Polega ona na takim manipulowaniu przekazywanymi danymi by ominąć filtrowanie i uzyskać dostęp do większej ilości danych niż było to pierwotnie zamierzone.

OK, tyle teorii – przejdźmy do praktyki.

Jako środowisko testowe wykorzystamy DVWA – w moim przypadku to kontener dockera postawiony poleceniem:

Aby bezpłatnie odblokować dalszą treść kliknij "Zapisz się" - dostaniesz nielimitowany dostęp do wszystkich treści i wyślę Ci na maila kilka wyjątkowych bonusów !!
Jeśli jesteś już zapisany - Kliknij "Zaloguj się" i podaj swojego maila - treść zostanie odblokowana:
Zaloguj się lub Zapisz się
Sprawdź Przejdź do zapisu
Anuluj
Dalsza część artykułu jest dostępna tylko dla zapisanych do newslettera. Aby zapisać się do newslettera Kliknij tutaj , lub wypełnij pola w bocznym panelu.
Jeśli jesteś już zapisany - podaj poniżej swój adres e-mail: Odblokuj
adotpay
Anuluj
Po uruchomieniu środowiska pod adresem http://localhost:8088 i instalacji bazy danych przechodzimy do zakładki sql incjection gdzie mamy pole do podania id użytkownika:

Podając dowolny id – np 1 pokażą nam się przykładowe dane:

sql injection

Ok, zostawmy na chwilę formularz i spójrzmy w w źródło (“View Source”) – widzimy tam taki sposób wykonania zapytania:

Jak widać pobierana jest zmienna id i umieszczana bezpośrednio w zapytaniu. Oznacza to tyle, że cokolwiek podamy jako id będzie dodane bezpośrednio do zapytania.
Wystarczy więc wykorzystać operator łączenia zbiorów, czyli “union” by wydobyć potrzebne nam dane.

Podajemy jako id:

i otrzymujemy informację o użytkowniku i wersji bazy danych:

sql injection

SQL Injection – poziom medium

W poziomie medium mamy trzy zmiany:

  1. Inaczej zapisane zapytanie
  2. Jako zabezpieczenie została dodana  funkcja mysqli_real_escape_string
  3. Zamiast pola do wpisania wartości – rozwijana lista

Jak można je obejść

Punkt 1:

Nasze zapytanie ma teraz postać:

W porównaniu z poprzednim przykładem zmienna id nie jest w pojedyńczych cudzysłowiach – więc podstawienie poprzedniej wartości pod id da nam błąd syntax error.
Obejście? Nie podajemy w wartości pojedyńczego cudzysłowia.

Punkt 2:

Wskazana  funkcja w naszym przypadku nic nie przeszkadza gdyż nie jest poprawnie zaimplementowana

Punkt 3:

Musimy tutaj posłużyć się narzędziem które pozwala na przechwytywanie i zmianę parametrów dla wykonanego zapytania przed wysłaniem na serwer ( tj robi za proxy).
Narzędzie jest kilka, ja  do tego celu wykorzystuje Burp Suite + przeglądarkę firefox.

Aby skonfigurować Burp Suite należy go pobrać, zainstalować, uruchomić ( domyślnie nasłuchuje na porcie 8080) i w przeglądarce uruchomić przesyłanie żądań przez Proxy.

W przypadku firefox-a ustawienie to znajduje się w Menu–.Preferences –>General i w dziale “Network Proxy” klikamy na “Settings…” i ustawiamy naszego burpa jako proxy:

W Burp Suite żądania możemy przechwytywać w ( kolejno od góry) Proxy–>Intercept – aby proxy przechwytywało żądania musi być uruchomiona opcja “Intercept is on”

Wracamy na chwilę do firefoxa, wybieramy dowolną wartość z listy i klikamy “Submit”

W burp nasze żądanie zostało przechwycone:

Przechodzimy do zakładki Params, gdzie mamy podane parametry, czyli nasze id, id sesji, poziom zabezpieczeń i wywołanie wysłania przyciskiem submit.

Modyfikujemy parametr id pamiętając o punkcie 1 i 2 – czyli  wystarczy nie podawać pojedyńczego cudzysłowia na początku:

Klikamy na “Forward” i gotowe – przeglądarka zwróciła nam użytkownika i wersję serwera mysql:

SQL Injection – Poziom Hard

Na poziomie hard nie mamy ani pola ani rozwijanej listy, a link do kliknięcia gdzie pojawia nam się nowe okno do wpisania id.
Różnica pomiędzy poprzednim poziomem jest taka, iż zmienna ta nie jest wysyłana jako parametr, a zmienna sesji ($_SESSION[‘id’]):

Format zapytania został przywrocóny z wersji low, czyli mamy zmienną w pojedyńczym apostrofie.

Wykorzystując zapytanie z poziomu low, oraz wiedzę na temat przechwycenia pakietu z poziomu medium przechwytujemy pakiet, pozmieniamy zmienną id na:

I otrzymujemy wynik:

SQL Injection na przykładzie galerii zdjęć

Firma Acunetix udostępnia specjalnie przygotowaną stronę zawierającą wiele podatności.
Jej główne zadanie to pokazanie skuteczności ich produktu – ale skoro sami informują że jest otwarta dla wszystkich do testów to grzechem było by nie skorzystać.

Zajmijmy się więc galerią znajdującą się pod tym adresem:

Nas najbardziej interesuje to co w pasku adresu, a dokładniej “cat=1”.

Zmodyfikujmy ten parametr używając łączenia za pomocą znanego nam już union:

Otrzymaliśmy bardzo wyraźny komunikat:

Oznacza on nic innego jak to że ilość kolumn po lewej stronie zapytania ( czyli w tabeli z opisami obrazków) jest inna niż po prawej ( nasz “union select 1,2,3”).  Aby przekonać się ile kolumn jest w danej tabeli należy dodawać kolejne liczby, tj “union select 1,2,4,5”, “union select 1,2,3,4,5” itp.

Jeżeli w końcu “trafimy” z liczbą kolumn ( tutaj jest akurat 11) pojawi nam się spowrotem galeria:

 

Została ona jednak powiększona o jeden rekord – ten z naszego łączenia zbiorów, który widać na samym końcu:

Jak widać wyświetlają nam się liczby 7, 2 i 9 – zmodyfikujmy więc nasze zapytanie podstawiając pod nie użytkownika, nazwę bazy i wersję serwera mysql:

Więcej SQLi, czyli dodajemy and

Szukając podatnych aplikacji udało mi się trafić na taki oto projekt:

Zixem SQLi

Jest tam 8 poziomów – od bardzo łatwego do experta. Zasady są proste – mamy wyświetlić jedynie wersję oraz użytkownika, oraz używać jedynie union.
Rozwiniemy też nasze zapytania o instrukcje warunkowe – do dzieła!

Level 1:

Podstawowy url to:

Kilka strzałów i wiemy że kolumna ma 3 wartości – jednak niestety nie widzimy tego co chcemy:

Autor ograniczył wyświetlanie wyników tylko do pierwszej wartości – w tym przypadku możemy ominąć to na dwa sposoby.
Pierwszym z nich jest użycie instrukcji warunkowej. Wyobraźmy sobie że zapytanie ma postać:

Jeżeli mamy ograniczenie wyników do jednego to musimy je tak zmodyfikować by prawdą była tylko “nasza część”. Jeżeli więc zapytanie będzie miało postać:

wtedy jego wynik będzie wynosił false, czyli nic się nie pojawi:

Jeżeli zaś do tego momentu zapytanie nie zwraca wartości dodajmy do niego drugą stronę która ma wartości i nam je zwróci:

Pisałem jeszcze o drugim sposobie – zasada jest ta sama co w pierwszym, tj lewa część musi nic nie zwracać.

Można to prosto osiągnąć podstawiając za id bardzo wysoką wartość której nie będzie w bazie.
W tym przypadku nie trzeba się wysilać i wystarczy jako id podać 2:

Level 2:

Sprawdzamy ilość kolumn plus umieszczamy zapytanie w pojedyńczym cudzysłowiu:

Level 3:

Tutaj zaczeły się schody – standardowe zapytania nie pokazywały nic , kombinacje pojedyńczych i podwójnych cudzysłowiów również.
Pomocny okazał się błąd składni – pokazał on ciekawy komunikat błędu:



Możemy z niego wywnioskować, że w kodzie jest funkcja która naszemu union urywa końcówkę 🙂

Wystarczy więc zamiast union wpisać unionon , znaleść liczbę kolumn i gotowe:

Level 4:


Level 7:

Nad tym zadaniem spędziłem 3 godziny, i przyznam że gdyby nie to że wiedziałem że jest tam błąd nigdy bym go tam nie znalazł.
Większą część czasu robiłem ślepe próby myśląc że chodzi tutaj o konkretne id.
Po ponad godzinie zajrzałem do kodu źródłowego – a tam na początku ukryty obrazek:

Podstawianie  daty nic nie dało, różne kombinacje z wynikami również – odpuściłem.
Spojrzałem jeszcze raz – a tam coś ciekawego:

Kolejna godzina z próbami przesyłania różnych wartości z polem status nie przyniosły efektu – cały czas statusem było error.
Wróciłem do podstawowej wersji adresu – domyślna wartość to ok1!
Zacząłem od drugiej strony – podstawiałem zapytania union, ale nic nie zwracało. Zauważyłem jednak jedną prawidłowość – jeśli po prawej stronie podstawimy odpowiednią ilość wartości ukryte polez wróci “ok1” , jeśli złą – zwraca “error”
Skoro już wiedziałem że tabela ma 3 kolumny, podstawianie różnych wartości pod id nic nie daje to próbowałem kombinacji z “and” – okazało się to strzałem w dziesiątke!

Wysłanie takiego zapytania:

pokazało w źródle strony w ukrytym polu wersję serwera mysql:

a odwrócenie user() z version() pokazuje użytkownika:

Level 8:

Pierwsze co zrobiłem to spojrzałem w źródło strony – nic ukrytego nie ma 🙂
Kolejna obserwacja – pod wartością 2 nic nie ma, więc nie trzeba instrukcji and.
Ale cokolwiek nie wpisałem to zawsze miałem komunikat “hacking attempt” – wygląda że wycina wszystko poza wartością.

Zacząłem kombinować więc z łączeniem zapytań plusami , spacjami czy innymi znakami opisanymi w kodowaniu znaków w adresach url – zadziałały dopiero tabulatory, ale nie do końca:

Mówi Wam to coś? Mi przypomniało “unionon”,więc odrazu go sprawdziłem:

Tutaj się trochę pobawiłem – koniec końców okazało się że jedno select musi być w drugim, tzn ciąg powinien wyglądać tak:

Level 9:

Na pierwszy ogien standardowe union:

zwróciło informację o błędnej liczbie kolumn oraz błąd fatal error:

Po zmniejszeniu liczb po prawej stronie  było jeszcze ciekawiej:

Błąd fatal error czyta nam zmienną 1 z zapytania union!
Wystarczy teraz podmienić zmienną 1 na /etc/passwd ( dodając ją w podwójny cudzysłów) i cofać się do poziomu głównego systemu plików.

Zapytanie wygląda więc tak:

a wynik:

Level 10:

Na poziomie 10 mamy taki adres:

Pierwsze co się rzuca w oczy to ciąg znaków przypisany do zmiennej x:

%3D to zakodowany znak równości, więc nasz ciąg wygląda tak:

Jest to nic innego jak zakodowany base64 – używając dekodera wynik to:

Ten wynik nic mi nie mówił – wrzuciłem go w rozmaite dekodery i w końcu trafiłem ( o zgrozo a tej samej stronie) na uudecoder, który jako wynik zwrócił mi 1 !

Dalej już z górki – jako zapytanie do uencoder podałem:

które po zakodowaniu dałem do base64encoder i udało się:

Po podmianie kolejności user() z version() otrzymałem nazwę użytkownika:

Chcemy więcej danych!

Do tej pory naszym celem było wyświetlenie wersji bazy danych i użytkownika. O ile takie dane pokazują podatność na sql injection, o tyle klient zlecający nam audyt chciałby widzieć coś więcej.
Wróćmy  więc do naszego dvwa – jak pamiętamy atak się udał gdy w polu podaliśmy:

W każdym serwerze mysql znajduje się baza danych o nazwie information_schema – jest to specjalny typ bazy danych która zawiera wszystkie informację o bazach danych, użytkownikach itp.
Po więcej informacji odsyłam Cię do dokumentacji – my natomiast wykorzystamy wskazaną bazę do uzyskania danych o bazie obsługującej nasz skrypt.

Zmodyfikujmy nasze zapytanie – wiemy z powyższego że baza danych obsługująca serwis nazywa się dvwa. Pobierzmy więc nazwy tabeli w naszej bazie z tabeli information_schema.tables:

Zostaje nam zwrócona lista tabel w naszej bazie dvwa:

Jak widać baza zawiera 2 tabele – guestbook i users. Sprawdźmy więc co znajduje się w tabeli users:

Mamy listę tabel – najbardziej interesujące wydają się tabele user i password – zobaczmy co w nich jest:

Bingo! Ponieważ widać że hasła są kodowane w MD5 możemy zdekodować je narzędziem online, lub w systemie kali linux np poprzez findmyhash:

Blind SQL Injection

Poza zwykłym sql injection istnieje jeszcze blind sql injection.
Jak sama nazwa mówi podatność typu blind charakteryzuje się tym iż nie widzimy wyniku zwrotnego a jedynie “dowód” na istnienie podatności.

Omówimy to na przykładzie dvwa:

Formularz przyjmuje id użytkownika – po wpisaniu otrzymujemy informację zwrotną na temat tego czy id znajduje sie w bazie czy nie, jednak bez szczegółowych informacji na temat podanego id.
Po wpisaniu np 1 otrzymamy komunikat:

Jak sprawdzić podatność typu blind?
Wystarczy zamiast id wprowadzić odpowiednie zapytanie ( użyjmy tego ze zwykłej podatności sql):

Otrzymamy informację zwrotną identyczną ja na obrazku powyżej – czyli mimo dodania własnego ciągu znaków zapytanie wykonało się poprawnie.
Nie widzimy co prawda wyniku zapytania, ale wiedza o tym iż dany formularz jest podatny jest cenną informacją dla dalszego rekonesansu.

 

Automatyzacja sprawdzania podatności, czyli sqlmap

Skoro mamy już wiedzę jak sprawdzać czy dany serwis jest podatny na atak sql injection czas wykorzystać narzędzie napisane do pomocy w rekonesansie, czyli sqlmap.
Jest to zaawansowany kombajn który automatycznie zweryfikuje podatność danej zmiennej, pod warunkiem że podamy mu odpowiednie parametry.

Do wykonania ataku na nasz serwis potrzebujemy adresu url oraz danych używanych w przesłanym żądaniu (parametrów). Ponieważ adres url działa tylko dla zalogowanych potrzebujemy też nasze aktualne ciasteczko sesyjne.
Pełne polecenie do weryfikacji podatności w przypadku mojego serwisu wygląda tak:

Jak widać nasz skrypt jest podatny – otrzymaliśmy gotowe zapytanie które potwierdziło lukę.
Możemy teraz dodać do naszego polecenia różne przełączniki jak np –dbs do wylistowania baz danych, –dump do wykonania kopii czy –os-shell do uzyskania powłoki na podatnym serwerze.

Używając sqlmap należy jednak pamiętać o kilku istotnych rzeczach ( nie nazwałbym ich wadami, a raczej skutkiem używania):

  • Sprawdzenie podatności to wykonanie conajmniej kilkudziesięciu żądań  które są zapisywane w logach, a więc możliwe do wykrycia po stronie systemu detekcji
  • Skaner to nie człowiek – jak widać w powyższych przykładach często trzeba się sporo natrudzić by osiągnąć zamierzony efekt
  • warto przejrzeć i zmienić niektóre ustawienia, takie jak user-agent który domyślnie widnieje jako “sqlmap/1.2.10#stable (http://sqlmap.org)”

Podsumowanie

Wystarczy jedno słabo zabezpieczone pole by pozyskać wszystkie informację z bazy danych. – należy o tym pamiętać tworząc rozwiązania, gdyż jest to jedna z najczęściej wykorzystywanych podatności.
Mimo iż pokazałem kilkanaście przykładów na uzyskanie dostępu do niepożądanych danych, istnieje jeszcze conajmniej kilka technik (celowo) nieopisanych  w tym artykule na ominięcie filtrowania.
Każdemu zainteresowanemu polecam samodzielne wykonanie testów – jeżeli chciałbyś sprawdzić się w innym narzędziu niż opisane w tym wpisie polecam pobrać obraz  z mutillidae, który również ma sporo podatności i kilka poziomów, lub sqli-labs:

I pamiętaj – przykłady podane tutaj są w celach edukacyjnych a nieautoryzowane testy( nawet w dobrej wierze) mogą skutkować sporymi nieprzyjemnościami.
Polecam wykonywac je albo we własnym środowisku, albo na w/w platformach testowych.

Boyabat escort