NFsec Logo

NordVPN – zabawy z API i Cloudflare X-Forwarder-For

07/01/2021 w Pen Test Brak komentarzy.  (artykuł nr 765, ilość słów: 802)

S

erwery API serwisu NordVPN stoją za Cloudflare. Jeśli wejdziemy na jeden z adresów API otrzymamy w formacie JSON geo informację o swoim adresie IP z zaznaczeniem czy jest on chroniony przez wspomnianą usługę, czy nie: "protected": false. Jeśli z ciekawości do metody insights dodamy parametr ?ip=$IP okazuje się, że możemy taką informację otrzymać o dowolnym (poprawnym) adresie IP w internecie. Na przykład: 1.1.1.1. W praktyce oznacza to, że oprócz VPN możemy gratis dostać usługę GeoIP znaną z Maxmind. W celach testowych zebrałem sobie 1000 adresów IP, dla których chciałbym sprawdzić informacje geo. Skoro północny serwis mi to oferuje za darmo to wystarczy uruchomić prosty skrypt:

for i in `cat ips`
do 
  curl https://api.nordvpn.com/v1/helpers/ips/insights?ip="$i" -w "\n"
done

Niestety po około ~500 żądaniach nasz skrypt wpadł w mechanizm limitowania żądań i zamiast wyników zaczęła ukazywać się odpowiedź:

<html>
<head><title>429 Too Many Requests</title></head>
<body bgcolor="white">
<center><h1>429 Too Many Requests</h1></center>
<hr><center>nginx</center>
</body>
</html>

Mimo, że setki żądań to i tak duże okno, jak na taki endpoint to czekanie aż limit z naszego źródłowego adresu IP zostanie zdjęty jest średnim czasowo rozwiązaniem. Cloudflare jako WAF (ang. Web Application Firewall) pełni również rolę serwerów proxy. Serwery te zazwyczaj dla ruchu HTTP* informację o źródłowych adresach IP przekazują za pomocą nagłówka X-Forwarded-For (któremu nie można do końca ufać jeśli się go odpowiednio nie traktuje przez serwery proxy). Jeśli odwiedzam adres serwisu, który jest chroniony przez Cloudflare i nagłówek X-Forwarded-For nie jest ustawiony w żądaniu to jest on skonstruowany na podstawie mojego źródłowego adresu IP i przesyłany dalej serwerowi końcowemu. Jeśli wysyłam żądanie z już ustawionym nagłówkiem X-Forwarded-For to mój źródłowy adres IP zostanie ustawiony jako pierwszy, a wartości z istniejącego nagłówka jako kolejne po przecinku (przynajmniej według dokumentacji). Załóżmy, że mój adres IP to: 11.22.33.44. Wysyłam żądanie HTTP bez ustawionego nagłówka X-Forwarded-For. Serwer za Cloudflare otrzymuje wartość nagłówka:

X-Forwarded-For: 11.22.33.44

Jeśli wysyłam żądanie HTTP z ustawionym nagłówkiem X-Forwarder-For na wartość: 55.66.77.88 to serwer według dokumentacji usługi Cloudflare powinien otrzymać na swoim poziomie nagłówek:

X-Forwarded-For: 11.22.33.44,55.66.77.88

Czyli na pierwszym miejscu zawsze powinien być “rzeczywisty” adres źródłowy. Niestety rzeczywistość nie jest już taka piękna. To, co dociera do serwera wygląda tak:

X-Forwarded-For: 55.66.77.88,11.22.33.44

Co to zmienia w naszej sytuacji? Otóż każdy klient Cloudflare, który oparł się na ustalaniu adresu IP klienta na podstawie nagłówka X-Forwarded-For zgodnie z dokumentacją – robi to dobrze tylko w przypadku, gdy nagłówek taki nie istnieje. W innym przypadku istnieje możliwość sfałszowania adresu źródłowego, który dociera do serwera za usługą Cloudflare (możemy zatruć wpisy w logach lub oszukać mechanizmy dostępu opierające się na IP). Sprawdźmy to na przykładzie API NordVPN:

curl -s https://api.nordvpn.com/user/address
XX.230.161.31
curl -s -H "X-Forwarded-For: 1.1.1.1" https://api.nordvpn.com/user/address
1.1.1.1

Dla pewności sprawdźmy jeszcze, czy nabierzemy NordVPN mówiąc mu, że korzystamy z jego serwera VPN w Polsce:

curl -H "X-Forwarded-For: X.XXX.206.59" https://api.nordvpn.com/v1/helpers/ips/insights

{"ip":"X.XXX.206.59","country":"Poland","country_code":"PL","city":"Warsaw",
"isp":"M247 Ltd","protected":true,"longitude":20.9999,"latitude":52.1532,
"state_code":"14","zip_code":"02-822"}

Wróćmy jeszcze do pierwszej wersji skryptu, który wpadł w limitowanie żądań z jednego adresu IP. Skoro mogę dowolnie manipulować adresem źródłowym, który widzi serwis za Cloudflare robiąc lekką modyfikację mogę ominąć mechanizm typu rate limit przekazując za każdym razem inną wartość nagłówka X-Forwarded-For. Od strony serwera końcowego będzie to miało postać, że unikalny adres IP robi jedno żądanie – w rzeczywistości to jeden i ten sam adres robi wiele żądań:

for i in `cat ips`
do 
  curl -H "X-Forwarded-For: $i" https://api.nordvpn.com/v1/helpers/ips/insights" \
  -w "\n"
done
{"ip":"XX.230.0.0","country":"Poland","country_code":"PL"...
{"ip":"XX.230.0.1","country":"Poland","country_code":"PL"...
{"ip":"XX.230.0.2","country":"Poland","country_code":"PL"...
{"ip":"XX.230.0.3","country":"Poland","country_code":"PL"...
{"ip":"XX.230.0.4","country":"Poland","country_code":"PL"...
{"ip":"XX.230.0.5","country":"Poland","country_code":"PL"...
{"ip":"XX.230.0.6","country":"Poland","country_code":"PL"...

Bez problemu skrypt sprawdził 1000 różnych adresów IP bez wyzwalania mechanizmu limitującego.

Cloudflare Bug Bounty rewarded you with a bounty of $XXX for Incorrect handling of an existing X-Forwarded-For header

Rozjazd pomiędzy dokumentacją, a rzeczywistą sytuacją, która mogła prowadzić do niepoprawnych implementacji nagłówka XFF został zgłoszony do Cloudflare 31 grudnia 2020, a rozwiązany 5 stycznia 2021. Problem z niepoprawnym traktowaniem nagłówka XFF przez API został zgłoszony do NordVPN 29 grudnia 2020 roku – nie został zakwalifikowany jako problem bezpieczeństwa, ponieważ w odpowiedzi z 6 stycznia 2021 została przedstawiona informacja, że błąd występuje tylko w danych zwracanych przez aplikację API – logi serwerów posiadają wpisy o prawdziwym adresie IP. Niestety po zgłoszeniu przestały działać niektóre punkty końcowe API… np. user/address.

Podsumowanie:

Jeśli Twój serwis znajduje się za opisanym WAFem i korzysta z XFF warto zweryfikować swoją konfigurację nagłówków. Jak zaleca sam Cloudflare najlepiej wykorzystać nagłówki CF-Connecting-IP lub True-Client-IP, które posiadają spójny format. Jeśli sami posiadamy serwery proxy, które przekazują adres IP w nagłówku XFF – i pozwalamy na zawartość przekazywaną przez klienta – warto zastanowić się nad ich usuwaniem i budowaniu od nowa tylko na podstawie źródłowego adresu IP.

Więcej informacji: How does Cloudflare handle HTTP Request headers?, nord.api, nordvpn-server-find, nordtoy, How to use public NordVPN API

Kategorie K a t e g o r i e : Pen Test

Tagi T a g i : , , , , , , , , ,

Komentowanie tego wpisu jest zablokowane.