W bibliotece Guzzle (PHP HTTP client) w wersjach przed 7.15.2 i 8.0.1 istnieje podatność SSRF wynikająca z rozbieżności między tym, jak Guzzle waliduje hosta w URI, a tym, jak libcurl dekoduje i interpretuje ten host. Atakujący mogący kontrolować URI żądania może ominąć mechanizmy kontroli dostępu i skłonić aplikację do połączenia się z hostem, który ta miała wykluczyć.
▸ Pokaż oryginał (EN)
Guzzle is an extensible PHP HTTP client. Prior to 7.15.2 and 8.0.1, Guzzle gives a transport the request URI as text and supplies the Host header separately. The cURL handlers set CURLOPT_URL to the URI exactly as written and push that Host into CURLOPT_HTTPHEADER; StreamHandler does the same through fopen(). libcurl then parses the authority itself, percent-decoding it and, on an IDN-capable build, applying IDNA mapping, and uses the result to resolve, connect, name the TLS peer and address a proxy CONNECT, while the supplied Host suppresses the aligned one libcurl would have generated. For a URI host written as 127.0.0.%31, filter_var() rejects the host as an IP literal, yet libcurl decodes it to 127.0.0.1 and reaches loopback with no DNS lookup while the server receives Host: 127.0.0.%31. An attacker who influences a fetched URI can therefore reach a host the application's checks excluded and read whatever the host exposes of the response. The same divergence moves Guzzle's own decisions onto a spelling the transport does not use: no_proxy selects proxy routing from the literal host, and RedirectMiddleware decides from it whether to strip Authorization and Cookie. Exploitation requires the application to build a request URI from untrusted input and to make a host decision before handing it to Guzzle. This issue is fixed in versions 7.15.2 and 8.0.1.
Guzzle przekazuje URI żądania do warstwy transportowej (cURL lub StreamHandler) jako tekst, ustawiając CURLOPT_URL na URI dosłownie tak, jak zostało zapisane. libcurl następnie samodzielnie parsuje sekcję authority URI, wykonując percent-decoding oraz — na buildach z obsługą IDN — mapowanie IDNA, i używa wyniku do rozwiązywania nazwy, nawiązania połączenia, weryfikacji peera TLS oraz obsługi proxy CONNECT. Tymczasem Guzzle waliduje hosta na podstawie nieprzetworzonego, dosłownego zapisu URI: np. dla hosta zapisanego jako `127.0.0.%31` funkcja `filter_var()` odrzuci go jako nieprawidłowy literał IP, jednak libcurl zdekoduje go do `127.0.0.1` i nawiąże połączenie z loopbackiem bez żadnego zapytania DNS, przy czym serwer docelowy otrzyma nagłówek `Host: 127.0.0.%31`. Taka sama rozbieżność dotyczy logiki `no_proxy` (wybór trasowania przez proxy) oraz `RedirectMiddleware` (decyzja o usunięciu nagłówków `Authorization` i `Cookie` przy przekierowaniach) — obie operują na dosłownym zapisie hosta, który nie odpowiada hostowi faktycznie używanemu przez transport.
Atakujący mogący wpływać na URI budowane przez aplikację może ominąć listy blokowanych hostów i dotrzeć do zasobów wewnętrznych (np. loopback, sieci prywatne), odczytując odpowiedzi serwera, do których dostęp miał być zablokowany. Dodatkowo może dojść do nieumyślnego przesyłania nagłówków uwierzytelniających (`Authorization`, `Cookie`) do niezaufanych hostów przy obsłudze przekierowań lub nieprawidłowego trasowania ruchu przez proxy.
Należy zaktualizować Guzzle do wersji 7.15.2 (gałąź 7.x) lub 8.0.1 (gałąź 8.x). Patche dostępne są w oficjalnym repozytorium GitHub projektu (commity wskazane w referencjach). Jako obejście należy unikać budowania URI żądań na podstawie niezaufanych danych wejściowych bez uprzedniej normalizacji i dekodowania percent-encodingu po stronie aplikacji przed przekazaniem do Guzzle.
Guzzle w wersjach przed 7.15.2 (gałąź 7.x) oraz przed 8.0.1 (gałąź 8.x), gdy aplikacja buduje URI żądania na podstawie niezaufanych danych wejściowych i wykonuje walidację hosta przed przekazaniem żądania do Guzzle.
Podatność wymaga spełnienia dwóch warunków po stronie aplikacji: (1) budowania URI żądania z niezaufanych danych wejściowych oraz (2) podejmowania decyzji dotyczącej hosta przed przekazaniem żądania do Guzzle. Poprawki zostały wydane w pull requestach #3907 i #3908 w repozytorium guzzle/guzzle.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N