7. Server Side Request Forgery

Server Side Request Forgery to technika wykorzystania podatności strony backendowej powstającej wtedy, gdy wykonuje ona zapytania zewnętrzne z wykorzystaniem danych przekazanych przez użytkownika tj. gdy URL wykorzystany do zapytania jest w jakiś sposób kontrolowany przez atakującego. Umożliwia to wykonywanie zapytań z serwera, co w wielu wypadkach może pozwolić na:

  • rekonesans w wewnętrznych sieciach intranetowych, normalnie niedostępnych z zewnątrz,
  • dostęp do usług chronionych, bardzo często nieodpowiednio zabezpieczonych, gdyż założono, że nie są publicznie dostępne.

Aby tego typu atak się powiódł aplikacja backendowa musi wykonywać zapytania na podstawie danych przychodzących z zewnątrz oraz musi posiadać podatny parser lub walidator URL, który umożliwi obejście filtrów dozwolonych/niedozwolonych adresów URL.

Aby zrozumieć w jakim kontekście może pojawić się potencjalne zagrożenie wyobraźmy sobie zakończenie API, które pozwala na pobranie dokumentu (niech to będzie własna faktura z systemu), w którym parametrem jest url dokumentu. Załóżmy dodatkowo, że url ten wskazuje na poddomenę aplikacji generującej dokument w formacie PDF. Wszystko wygląda poprawnie, ale pozostaje pytanie – w jaki sposób końcówka API sprawdza czy podany url jest właściwy?

Otóż od jakości walidacji przekazanych URLi zależy, czy wewnętrznie system wykona zapytanie do właściwego serwera czy też nie. Problem walidacji URL jest dość skomplikowany. Jeżeli nasz system zawsze korzysta z jednego URL-a sytuacja może być rozwiązana dokładnym porównaniem URLi. Jeżeli jednak URL-e mogą się zmieniać zaczynamy polegać na narzędziach dostarczanych przez biblioteki (np. parse_url w PHP) lub własnych dopasowaniach wyrażeniami regularnymi. Tutaj warto przypomnieć problem w PHP (https://bugs.php.net/bug.php?id=73192) gdzie parse_url zwracał błędną nazwę hosta w przypadku, gdy podano zmanipulowany URL:

php > echo parse_url("http://example.com:80#@google.com/")["host"];
google.com
php > echo parse_url("http://example.com:80?@google.com/")["host"];
google.com

a zgodnie z RFC3986 (https://tools.ietf.org/html/rfc3986#section-3.2) powinien to być example.com.

Błędy walidacji dotyczą nie tylko sekcji hosta, ale także parametrów. Jednym z takich potencjalnie podatnych przypadków jest sytuacja, gdy użytkownik jest w stanie wstrzyknąć w parametr znaki końca linii (pamiętajmy, w kodowaniu URL to po prostu %0D%0A). Takie sytuacje niejednokrotnie umożliwiają wykonanie ataku ,,Protocol smuggling’’ gdzie można wykonać operację innym protokołem niż występujący w URL-u. Klasyczny przykład to atak opisany w [33] polegający na wykonaniu połączenia SMTP poprzez nadużycie protokołu gopher.

Ostatnim przykładem jest próba rekonesansu sieci wewnętrznej z wykorzystaniem mechanizmów DNS (atak DNS pinning). Wyobraźmy sobie, że zezwalamy w naszej aplikacji na żądania serwer side, ale blacklistujemy adresy IP lokalne naszej sieci aby atakujący nie mógł wykonać rekonesansu wewnątrz naszej sieci. Załóżmy że przykładowy pseudokod jest następujący

blacklist = [127.0.0.1/8, 172.16.0.0/24]
host = getIP(url)
if not host in blacklist {
     request(url)
}

Co jest tutaj złego? Otóż przyjęliśmy założenie, że zarówno w wywołaniu getIP() jak i request() efekt rozwiązania adresu URL będzie taki sam. Tak wcale nie musi być i atakujący może spreparować zapis w systemie DNS tak, by zwracał różne adresy IP (to wbudowana funkcja systemu DNS dająca możliwość rozłożenia połączeń na różne serwery). Jednym z nich będzie adres spoza listy, drugim z listy. Podatność tutaj omawiana jest klasycznym przykładem podatności TOCTOU (Time of check time of use), gdzie pomiędzy sprawdzeniem a wykorzystaniem występuje możliwość zmiany sytuacji – tym razem zmiany adresu IP. Praktyczny przykład to CVE-2022-4096 opisane w [34].

Ochrona w tym przypadku przede wszystkim:

  • unikanie możliwości manipulacji całym bądź częścią URL przez użytkownika,
  • poprawne filtrowanie, w tym usuwanie znaków nowej linii z zapytań GET,
  • aby uchronić przed atakiem DNS pinning należy pobrać wszystkie adresy IP powiązane z domeną i wszystkie zweryfikować.