Предисловие

Как известно, в России почти каждая первая финансовая организация позиционирует себя как софтверную IT-компанию, а не просто как "банк" или "платежная система". Сегодня речь пойдет о ЮМани — подразделении Сбера, IT-гиганта всея руси.

До того, как ЮМани стал тем, чем он сейчас является, сервис долгое время существовал как продукт Яндекса под названием Яндекс.Деньги — в те времена у меня был очень приятный опыт взаимодействия с техническим руководством компании, я неоднократно (будучи security researcher'ом) сообщал им об уязвимостях, а они, в свою очередь, оперативно это исправляли, давали обратную связь и вознаграждали за такую работу, аналогично тому, как это делали и зарубежные крупные IT-компании в рамках взаимодействия с white-hat хакерами. Такая вот IT-компания здорового человека. Но с тем, как Сбербанк поглотил Яндекс.Деньги и провёл ребрендинг, проект стал превращаться, скорее, в IT-компанию курильщика: взаимодействовать с представителями проекта в соц. сетях стало практически невозможно, какие-либо данные на страницах о Bug Bounty программах были удалены и даже ни одного email-адреса не оставили в качестве средства связи для сообщения об уязвимостях.

Пару месяцев назад я обнаружил уязвимость в сервисе ЮМани (о ней чуть позже) и сразу же решил сообщить о ней. Однако никаких релевантных этому форм связи, email-адресов и т.д. я не обнаружил — способов безопасно сообщить о такой уязвимости элементарно не было на официальном сайте сервиса. Я попытался связаться с людьми, работающими в ЮМани, однако, опять же, я не получил никакой обратной связи. На этом моменте я, что называется, "забил", в надежде, что ошибку исправят и без меня, ведь не может же такая дырень оставаться незамеченной долго, правда? Спойлер: может.

В один из прекрасных летних вечеров я воспользовался этой платежной системой в очередной раз, ну и, в рамках праздного интереса, решил проверить, "а не исправили ли". Как выяснилось, нет. Не исправили. И вот получается, что уже на протяжении очень продолжительного периода времени (около 2 месяцев) мне известно о критической уязвимости в сервисе, которую исправить можно было бы за 1-2 минуты (буквально). И тут я решаю связаться хоть каким-то способом с кем-то из этой организации. Кое-как откопал контакты HR-менеджера, с которой состоялся диалог:

HR-менеджер поделилась ссылкой на программу для баг-хантеров
HR-менеджер поделилась ссылкой на программу для баг-хантеров

Как можно догадаться из переписки выше, я отправил отчёт об уязвимости в ЮМани через этот очень сомнительный сервис. Но о нём позже. Пока поговорим о самой уязвимости.

Примечание: На момент написания этой статьи уязвимость исправлена, поэтому её публичное раскрытие не несёт угрозы для сервиса и/или его пользователей, а скорее наоброт — дает конкретное представление о масштабах халатности и том, как Сбербанк/ЮМани относится к тем, кто делает за них важную работу по поиску брешей в безопасности.

Уязвимость

Для поддержания сессии пользователя, сервис ЮМани использует Cookie, по которым идентифицирует учетную запись в системе и возвращает соответствующие пользовательские данные. Если мы зайдем на главную страницу сервиса и войдем в учетную запись, то мы окажемся в личном кабинете по адресу yoomoney.ru/main:

Главная страница личного кабинета ЮМани
Главная страница личного кабинета ЮМани

На странице присутствует базовая информация, такая как: последние 4 цифры имеющихся банковских карт, информация о балансе, номер телефона и информация о последних операциях по счету. Ничего особенного. При взаимодействии с интерфейсом, веб-приложение ходит к yoomoney.ru/api, откуда получает всю необходимую информацию. Казалось бы, этот API и будет нашим основным интересом в плане security research. При этом на данном API-эндпоинте корректно настроен CORS, запрещающий браузерам обращаться к API откуда-либо, кроме как с yoomoney.ru.

Прежде чем обратиться к серверу, браузер посылает Preflight-запрос — вызывая через HTTP методом OPTIONS удаленный сервер, запрашивая заголовки CORS. В такой запрос, как правило, включается заголовок Referrer, сообщающий, с какой странички делается запрос, а заголовки ответа выглядят примерно так:

access-control-allow-credentials: true
access-control-allow-origin: https://example.com

В данном примере сервер ответил, что запросы к нему можно делать только с сайта example.com.

Так вот если на /api CORS, конечно же, настроен правильно и отвечает, что обращаться к нему можно только с yoomoney.ru, то на страницах ЮМани /main (главная страница), /settings (настройки) и /cards (информация о картах) — ситуация совсем другая. В заголовке access-control-allow-origin сервис отвечал всегда тем, что присылалось в Referrer.

Например представим, что я поднял страничку privet-ya-luntik.ru и обращаюсь с помощью XHR-запроса в JS-коде этой страницы на yoomoney.ru/main. Мой браузер сделает запрос:

OPTIONS https://yoomoney.ru/main
Referrer: https://privet-ya-luntik.ru

А ЮМани любезно ответит:

access-control-allow-credentials: true
access-control-allow-origin: https://privet-ya-luntik.ru

т.е., позволит мне делать запросы к этим ресурсам на их сервере и получать их содержимое. Более того, запрашиваться эти ресурсы будут от имени авторизованного пользователя, т.к. заголовок access-control-allow-credentials подразумевает, что браузер может передавать оригинальные Cookie от ЮМани с каждым моим запросом.

Но что мы можем там найти? Это же простой интерфейс, а всё загружается с API. Бесполезная какая-то находка, правда? Я тоже так сначала подумал, но нет. Заглянем, например, в код страницы /main:

А вот тут становится сильно интереснее
А вот тут становится сильно интереснее

Как выяснилось, ЮМани встраивает пользовательские данные в изначально отдаваемую пользователю страницу, дабы тот не ждал лишние несколько мгновений, пока данные подгрузятся из API. Все эти данные заботливо упакованы в JS-объект window.__data__, который, в свою очередь, лежит в одном из тэгов <script>. Сразу в глаза бросается наличие в объекте моего номера телефона, адреса эл. почты, текущего баланса. После более тщательного исследования, выяснилось, что в целом из страницы можно вычленить:

  • баланс пользователя;

  • email пользователя;

  • телефон пользователя;

  • статусу идентификации;

  • ID аккаунта;

  • ID пользователя;

  • дату регистрации;

  • информацию о привязанных картах;

  • информацию о выпущенных картах;

  • информацию о текущих настройках безопасности;

  • секретный ключ (его можно потом использовать отдельно для действий от имени пользователя);

  • информации о привязанных аккаунтах (ГосУслуги, ВК, Сбер);

  • истории транзакций с датами, суммами и типами;

Я решил не долго думать и написал свой Proof-of-Concept. Вот что получилось:

Нажимаешь кнопочку на моей сторонней (вообще не имеющем к ЮМани отношения) страничке и всё — все данные аккаунта в платежной системе (да и доступ к нему) у меня в руках.

Мой пример довольно "вегетарианский" — он просто отображает украденные данные. Однако абсолютно ничего не мешает злоумышленнику создать страницу с идентичным JS-кодом, который будет делать запросы к ЮМани, получать оттуда данные, пересылать их на сервер злоумышленника и затем переадресовывать пользователя куда-то дальше. Конечный пользователь даже не узнает о том, что его данные и доступ к аккаунту были украдены.

Примечание: Разумеется, я не публиковал PoC в открытом доступе, доступ к моей страничке был защищен паролем, которым я позднее поделился с самим ЮМани.

Дальнейшее взаимодействие с ЮМани

Как мне и предложила HR, я направил багрепорт через BugBounty.ru — сервис, при использовании которого возникают очень странные ощущения. На самом ресурсе нет никакой вводной информации о том, кому он принадлежит, кем разработан и как поддерживается. Складывалось впечатление, что он написан кем-то на коленке за пачку сока (возможно, так и было, судя по тому, как развивались события дальше). Ну бог с ним. Зарегистрировался, захожу в раздел "Программы", ожидаю увидеть обширный список того, кто предлагает репортить уязвимости через эту платформу.

Вот кто этим пользуется, кхм.
Вот кто этим пользуется, кхм.

Програм всего три штуки: ЮМани, ВКонтакте и какой-то неоплачиваемый "Мой Полк", (что бы это ни было) запущенный самим сервисом BugBounty.ru — сомнительно, но окей. Захожу, пишу и отправляю отчет в начале рабочего дня, 26 августа 2024 года:

Подробное описание уязвимости
Подробное описание уязвимости

Сообщаю об этом той HR, которая изначально выдала мне ссылку на эту платформу. Через несколько минут в отчете появляется комментарий сотрудника:

«Окей» подумал я. Уязвимость критическая, исправить её легко, в любой нормальной организации её исправят за считанные часы — не нужно иметь пять высших образований и IQ выше 300 чтобы поправить header'ы CORS или просто их убрать (при их отсутствии будут применяться стандартные политики безопасности, которые были бы достаточными и при этом никак не сломали бы работу сервиса).

Проходит рабочий день, решаю поинтересоваться, как успехи:

В ответ тишина. Всё это время уязвимость на месте, по прежнему эксплатируема как и раньше. Статус отчета по прежнему "Рассмотрение". Уточню также, что дополнительно написал CEO ЮМани Ивану Глазачеву: "...через bugbounty.ru сообщил о критической уязвимости в ЮМани, с помощью которой злоумышленники могут получить неограниченный доступ к данным и кошельку пользователей сервиса <...> уязвимость по прежнему на месте. Хотел бы сообщить об этом Вам, дабы избежать ситуации когда из-за промедлений кто-то кроме меня найдет это и решит воспользоваться в преступных намерениях".

И вот спустя два месяца, ночью с 3 на 4 сентября я обнаружил, что уязвимость, наконец, закрыли. Захожу в BugBounty.ru, вижу, что репорт все еще висит в статусе "Рассмотрение". Пишу комментарий:

На следующие сутки на репорт отвечает сотрудник ЮМани:

Отчёт закрывается с переводом в статус "Дубликат" и всё. Мне, тем временем, предлагается поверить, что на протяжении 2 месяцев критическая уязвимость была на их сайте, им якобы об этом было известно и они не предпринимали абсолютно никаких мер по её устранению, пока я не направил им отчёт. А потом совершенно случайно так совпало, что я им написал и они закрыли эту уязвимость. При этом никаких ссылок не приводится, а общее число отчетов на сайте сервиса для баг-репортинга не изменилось. При этом до закрытия уязвимости отчет в статус "Дубликат" никто не переводил, в том числе, сотрудник, принявший отчет в работу изначально.

При этом в условиях программы обещается до 400 тысяч рублей за предоставленную информацию о критической уязвимости.

Отличная схема, скажу я вам. Заводите bugbounty программу на стороннем ресурсе без внешнего аудита и какой-либо репутации, получаете бесплатно репорты об уязвимостях, закрываете их, а сообщившим говорите "дубликат, денег не будет" и всё. Такой вот сайберсекьюрити на бесплатном аутсорсе. Мне кажется, смахивает на мошенничество.

Итог

Из вышесказанного можно делать выводы. Подобное отношение к bug bounty программе у Сбера/ЮМани показывает, чего стоят безопасность пользователей, их данных и денег для сервисов таких компаний. Дискредитируя такими действиями программы репортинга уязвимостей, они создают условия, в которых независимые исследователи не будут проводить исследования т.к. они заведомо будут знать о том, что вознаграждения они не увидят. Зато этим займутся злоумышленники, которые в лучшем случае, продадут информацию третьим лицам, а в худшем — украдут ваши деньги и личные данные.

Такие дела.

Обновлено:

Как и ожидалось, никаких подтверждений того, что действительно уязвимость была ранее известна — не представили. Вместо этого сказали "это подтверждается нашими внутренними документами" и всё, а документы мы вам не покажем, поверьте нам на слово :-)

Обнародовали ли они хотя бы какое-то косвенное подтверждение сказанного? Нет. Ни скриншотов, ни самих документов, ни тикетов, ни отчетов.

Комментарии (28)