Началось все с прилета в Москву. Как и положено, я сдал необходимый ПЦР тест на ковид, дождался отрицательного результата, залил его на Госуслуги и… решил, что на этом мои московские приключения закончились. Но все оказалось не так просто. Ко мне внезапно пришел врач. И вручил постановление, что 2 недели я обязан сидеть дома в карантине, так как в самолете со мной летел один зараженный. Про приложение «Социальный мониторинг» я много слышал и даже читал статью на Хабре, где люди покопались в его бета-версии. Ну а какой же исследователь не соблазнится покопаться в таком интересном приложении?
Скажу сразу, моей целью не было обмануть систему или сбежать из карантина. Свои 2 недели я честно просидел дома. Ну хорошо, пару раз выходил в ближайший магазин за пивом. Целью было посмотреть, что о нас знает эта система и насколько правдивы некоторые высказывания ее авторов. Сначала я занялся сбором предварительной информации. Выяснил примерно следующее:
- Приложение написано на основе программы трекера мусоровозов. Чему я не нашел вменяемых подтверждений, да и копание во внутренностях программы тоже ничего подобного не подтвердило.
- Бета версия была никак не защищена, ее декомпилировали и выложили на github. Впрочем, с гитхаба ее убрали по требованию правообладателя.
- Бета версия передавала фотки на сервер в Эстонии для использования сервиса распознавания лиц.
- Дальше я прочитал высказывание главы ДИТ Эдуарда Лысенко, который сказал следующее: «Руководитель ДИТ Москвы категорически опроверг информацию, что приложение передаёт фотографии на сторонний сервер: «На самом деле, ничего никуда не передаётся, — заявил он. — Во-первых, не передаются никакие фотографии в принципе. Во-вторых, тот код биометрический, который появляется, он попадает исключительно на сервера ДИТа.» Здесь мне уже стало интересно, как это фотки не передаются в принципе. Неужели они преобразуются в биометрический код прямо на смартфоне?
- Новая версия программы сильно обфусцирована и теперь практически невозможно ее анализировать.
Дальше собрал слухи от друзей-знакомых:
- На самом деле передается не одна фотка, а серия фоток. Это сделано для того, чтобы было невозможно сфоткать свою бумажную фотку. Или даже видео передается.
- Если приложение перестало запрашивать фотки, карантин закончился.
«Живое» приложение я честно поставил на айфон и не планировал в него вообще лезть. Айфона, на который можно поставить jailbreak у меня с собой не было, но зато был рутованный Андроид. С него я и начал. К слову, на рутованном Андроиде приложение не запускается. Обфускация Java кода выглядит забавно. Переименованы все переменные и названия функций. Но, пройдя по цепочке, мы попадаем на вполне читабельный кусок кода. Примерно, как ходить по листингу дизассемблера, только в конце сюрприз. Я бы потратил минимум пару дней, копаясь в этом коде, но внезапно придумалось более простое решение. А что, если… они доверяют ОС проверку SSL сертификатов? По работе мне иногда приходится реверсить протоколы Apple, Google, Whatsapp и многие другие. Почти во всех системах корневые сертификаты прошиты внутри программы, что сильно затрудняет анализ траффика.
Ставлю на Макбук mitmproxy, очень удобный анализатор траффика с минимумом настроек. Скачиваю их корневой сертификат, добавляю на айфон профиль – и вот оно! Весь траффик программы мы видим, как на ладони.
Регистрация приложения
Установить и зарегистрировать приложение нужно в течение суток от начала карантина. По этому поводу приходит SMS-ка. Раньше это делать бессмысленно, просто телефон не найдется в базе. Вот так выглядит запрос на регистрацию:
<b>2020-09-12 17:48:03 POST https://sm-a-a90ae4b5a.mos.ru/api/covid/device/password HTTP/2.0
← 200 application/json 82b 156ms
{
"deviceId": "2FFA9DB6-4900-4973-B1AE-BA5874BEXXXX",
"phone": "7925xxxyyzz"
}</b>
В ответ мы всегда получаем “200 OK” и ничего более. Если телефон есть в базе, придет SMS-ка с кодом, который нужно ввести в приложение. Если телефона в базе нет, просто ничего не придет. В deviceId передается UDID айфона. К этому идентификатору все привязано. Если сломается айфон и мы поднимем новый из бекапа, приложение работать не будет. И будет штраф, с которым непонятно, как разбираться. Здесь и далее приватные данные будут полностью или частично заменяться на “XXYYZZ”.
Передача координат
Дальше я набрался смелости и подключил через mitmproxy «живой» телефон. Каждые минут 5-10, а также при запуске приложения, делается вот такой запрос:
2020-09-12 17:56:32 POST https://sm-a-a90ae4b5a.mos.ru/api/covid/device/60B1A8A1-2AD9-447C-BB25-91YYYYY19C6E/message
HTTP/2.0
← 200 application/json 83b 84ms
[
{
"accuracy": 65,
"battery_level": 68,
"charge": false,
"datetime": "2020-09-12T14:56:32Z",
"device_model": "iPhone 11 Pro",
"indoorNavigation": {
"bluetoothDevices": [],
"wifiDevices": [
{
"name": "wifi-XXX",
"rssi": 0
}
]
},
"install_datetime": "2020-09-08T07:57:11Z",
"lat": 55.XXZZZ732239728,
"locationDatetime": "2020-09-12T14:56:31Z",
"locationStatus": {
"gps": true,
"isPermissionGranted": true,
"network": true,
"passive": true
},
"lon": 37.YYZZZ270607305,
"os_version": "iOS 13.7.0",
"version_ext": "1.7 (127)"
}
]
Здесь мы опять же видим deviceId, он другой, первый запрос я делал с айпада.
accuracy – точность определения координат, не знаю, в каких единицах
battery_level – уровень заряда батарейки. Интересно, зачем он ДИТ-у?
charge – стоит ли телефон на зарядке. Тоже непонятно, зачем передается.
datetime – текущая дата и время. Возможно используется, чтобы нельзя было «подкрутить» время на устройстве.
device_model – модель телефона. Ну мне не жалко, если об этом узнает ДИТ.
indoorNavigation – довольно интересно. Здесь список известных wifi сетей, по которым можно определить координату. Но при этом не передается BSSID, что делает эту информацию абсолютно бесполезной. По имени сети координаты не определить.
install_datetime – может использоваться для защиты от эмуляции этих запросов. Хотя эту информацию можно при желании получить и довольно несложно.
lat, lon – собственно, координаты
location_status – включен ли GPS и разрешено ли программе пользоваться им в фоне. А если нет, будет штраф.
os_version – версия iOS
version_ext – не разбирался, возможно, версия самого приложения
Передача селфи
Начинается самое интересное. Сниффим траффик в момент передачи сделанного селфи. Итак, где же наши биометрические хеши и видео? А вот что происходит на самом деле:
<b>2020-09-12 18:00:15 POST https://sm-a-a90ae4b5a.mos.ru/api/covid/device/60B1A8A1-2AD9-447C-BB25-91YYYYY9C6E/photo
HTTP/2.0
← 200 application/json 39b 301ms
Request Response Detail
:authority: sm-a-a90ae4b5a.mos.ru
content-type: multipart/form-data; boundary=alamofire.boundary.04b478f466f0605d
accept: */*
shard: 6
authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTk1NjYzMjl9.iq-noX2tu13tr4ut7sBWpgWl77sELkT
kXCdODK9yvL8
֞ostype: iOS
accept-language: en-RU;q=1.0, ru-RU;q=0.9, cs-RU;q=0.8
accept-encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8
versionext: 1.7 (127)
content-length: 378238
֘user-agent:
cookie: session-cookie=163402e73a984c296450ad1fdcb1815835321af39172a2bef8658e48071941dc73acdb9d1d976170d2ef9
70da45f5c87
Multipart form [m:auto]
҅Form data:
photo: ����..JFIF.....�.�..��.�Exif..MM.*.............................J...........R.(..........�i.........Z.......�.....</b>
Ну все понятно, да? Эта часть защищена чуть сильнее, в запросе еще фигурирует Bearer токен, по которому организована авторизация. Момент получения токена я не отсниффил, возможно, токен приходит при запросе на селфи. Уходит ровно одна фотка. Где же ваш биометрический хеш, господин Лысенко?
Запрос статуса и снятие карантина
Кроме передачи координат приложение еще регулярно делает запрос статуса. Кстати, возможно, что при запросе статуса и приходит запрос сделать селфи. Когда приложение перестало у меня запрашивать селфи, я ради интереса отсниффил запрос статуса:
<b>2020-09-18 13:28:13 GET https://sm-a-a90ae4b5a.mos.ru/api/covid/device/60B1A8A1-2AD9-447C-BB25-91XXXXX19C6E/status
HTTP/2.0
← 200 application/json 317b 181ms
{
"code": 0,
"io": "АНДРЕЙ ЕВГЕНЬЕВИЧ М.",
"last_android_version": "1.1.1",
"last_ios_version": "1.0",
"message": "Все отлично! Уведомление о необходимости отправить фотографию придет в СМС-сообщении",
"quarantine": null,
"status": "active"
}</b>
Самое интересное в ответе сервера – это “quarantine: null”. Это означает, что карантин закончился. Но приложение вам об этом не говорит. Более того, в message они все еще обещают запрашивать селфи. И статус все равно активный. И координаты… ну конечно же приложение продолжает слать координаты устройства на сервер ДИТ! Поэтому удаляйте приложение сразу же по окончании карантина.
Что можно сделать
Из написанного очевидно, что можно написать скрипт, который будет эмулировать работу приложения и отсылать все нужные данные. Написание затрудняется тем, что в процессе тестирования можно легко схватить штраф. В первую очередь, нужно до конца отсниффить весь процесс регистрации, в том числе, в момент получения SMS-ки с кодом. Разобраться с Bearer аутентификацией и другими мелочами. Вполне возможно, что анализируют EXIF записи фоток. В которых есть точное время, а также могут быть координаты. Поэтому решением будет наделать много разных фоток заранее, а EXIF править уже на ходу перед отсылкой.
Ну и в заключении хочу сказать, что ни в коем случае не призываю кого-либо нарушать карантин, особенно при положительном тесте на коронавирус. Это лишь анализ системы и небольшой камушек в огород ДИТ, которые любят делать странные публичные заявления. Не болейте!
Автор: Андрей