При входе в личные кабинеты различных сервисов, в целях безопасности, часто используется 2FA — помимо логина и пароля, нужно ввести одноразовый код.

Но, как оказалось, не всё так безопасно даже с двухфакторной аутентификацией — за последний год я нашёл три (!) сервиса, когда одноразовый код для входа, который отправляется клиенту в SMS, можно было посмотреть в самом запросе.

Далее кратко о том, чем это грозило, на конкретных примерах.

1. Популярная сеть АЗС, более 500 000 зарегистрированных клиентов.

Запрос при входе в веб-версию личного кабинета:
POST https://someazs.ua/ua/profile/auth/
Accept: application/json, text/javascript, */*; q=0.01
Accept-Encoding: gzip, deflate, br
Accept-Language: ru,en-US;q=0.9,en;q=0.8,uk;q=0.7
Connection: keep-alive
Content-Length: 408
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: PHPSESSID=6n3l2o90hfb020u9ag020u8ha1; usersomeazs_popupcoupons=1;...
Host: someazs.ua
Origin: https://someazs.ua
Referer: https://someazs.ua/ua/login/
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
X-Compress: null
X-Requested-With: XMLHttpRequest

data[phone_mask]: 951234567
data[phone]: 0951234567




Ответ: {"Status":0,"Code":"7038","status":true,"step2":true}

Код из SMS — 7038 — виден просто в ответе сервера.

То есть на сайте, при входе в личный кабинет, в ответе был одноразовый код для входа, который отправляется клиенту в SMS — можно было войти в чужую учётную запись, указав только номер телефона клиента — а OTP посмотреть в самом запросе.

В личном кабинете доступны: номер карты лояльности, ФИО, балансы (бонусный в гривне, литровый, кофейный), история транзакций, в настройках — дата рождения, e-mail клиента и др.



С помощью дальнейших действий нетехнического характера (например, прозвон клиентов) при должном везении можно было бы воспользоваться клиентскими деньгами/литрами/кофе. Почему я пишу «при должном везении»? Когда общался о проблеме, мне сообщили, что расчёты чужими бонусами не так просто выполнить, даже если иметь доступ к учётной записи, так как есть дополнительные проверки. Тем не менее…

Ошибку исправили быстро, поблагодарили.



2. Сеть социальных магазинов (похожа на Fix-Price), мобильное приложение (более 100 тысяч скачиваний)



Отслеживая запросы через Fiddler, я заметил следующее. При входе в мобильное приложение, после ввода номера телефона и карты лояльности, клиенту отправляется одноразовый код.

Проблема в том, что при этом выполняется GET-запрос вида:
https://bulk.somesmssender.com/?sending_method=sms&from=someretailes&user=onviber4821&txt=%D0%9A%D0%BE%D0%B4+%D0%BF%D0%BE%D0%B4%D1%82%D0%B2%D0%B5%D1%80%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F%3A+1234&phone=380987654321&sign=42f66957a03090eb90556b0ef7fed2e1


Прямо в этом запросе виден и сам одноразовый код: текст отправляемой SMS — это
%D0%9A%D0%BE%D0%B4+%D0%BF%D0%BE%D0%B4%D1%82%D0%B2%D0%B5%D1%80%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F%3A+1234&


Простое преобразование сообщает: Код+подтверждения:+1234



Т.е. в самом приложении можно посмотреть, какой код будет отправлен. Здесь также можно входить в чужие учётные записи, уже без второго фактора.

Клиенты данной сети по определённым причинам наименее защищены от мошенничества, поэтому я много раз пытался донести информацию в компанию. Я писал три раза в августе на адрес, который был указан на странице приложения в Play Market — ни одного ответа не получил, даже автоматического.

Позже я написал в компанию, чей сервис SMS рассылки они используют. Мне ответили, что необходимо обратиться к владельцу мобильного приложения, так как они не могут со своей стороны повлиять на разработку и передачу информации данной компании.

Это верно, но, я предполагал, что сервис рассылки сообщений может повлиять на них как на партнёра/клиента. К тому же, причина, по которой я написал в сервис рассылки, в том, что указанный мной GET-запрос — это именно их разработка, и эта же ситуация с большой долей вероятности может быть у других их клиентов. Я предложил, что сервису желательно исправить логику рассылки — не передавать в запросе одновременно номера телефонов клиентов и одноразовый код — на это мне не ответили.

Немного о том, как ещё я пытался донести проблему
Затем на сайте магазина я написал на контактный email, подождал — и снова ничего. Но так как я упрямый, нашёл на том же сайте все возможные контакты (и общие ящики почты, и личные адреса) и написал им — как вы понимаете, тоже никто не ответил.
Поэтому позже я обратился в чат и прямо в чате задал вопрос, получали ли они мои письма. Сначала ответили, что не видят, потом нашли и пообещали, что передадут.

Что интересно, помимо отсутствия реакции на email на основной адрес приложения, после общения в чате мне приходили письма. В одном из них я увидел, как они меня завели в систему: «Умник»



Так себе отношение к клиенту.

Когда я последний раз проверял, исправления со стороны Avrora не было.
Со стороны сервиса рассылки также не было исправлений, но тут ситуация более серьёзная, поэтому названия сервиса я прямо не указываю.



3. Мобильное приложение для хранения скидочных карт и выполнения мобильных платежей (более 130 тыс. клиентов)

При входе нужно ввести только email и код. При этом выполняется запрос:

POST http://api.somewallet.com/mobileclient.svc/getRegistrationCode HTTP/1.1
Content-Type: application/json; charset=UTF-8
Content-Length: 100
Host: api.somewallet.com
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.12.3

{"request":{"Culture":"ru_RU","DeviceIdentifier":"4514825570005447","Identifier":"some@email"}}

Вот ответ:

{"GetRegistrationCodeResult":{"ErrorMessage":{"Code":0,"LogReferenceId":0,"Message":"SUCCESS"},"Data":{"Code":"044912"}}}


Здесь в ответе также видно код, который нужно ввести (044912).

После входа я получил доступ к следующим функциям:

  • просмотр карт лояльности и бонусов на них,
  • купонов для определённых магазинов,
  • контактной информации о клиенте (номера телефонов и email, дата рождения и ФИО),
  • установка, изменение и удаление кода доступа из 4 цифр,
  • просмотр платёжных карт,
  • получение платёжного токена (похоже на токен из моей предыдущей статьи Как ездить на такси за чужой счёт).

Таким образом, все функции приложения могли быть доступны любому, кто просто укажет email пользователя данного приложения — не нужно узнавать одноразовый код у пользователя, так как его видно сразу при входе. Проблема идентична двум другим.

На моё сообщение отреагировали, уязвимость исправили и финансово отблагодарили.



Можно сказать, что иногда в сервисах можно обойти двухфакторную аутентификацию просто просматривая запросы авторизации. И этому подвержены как веб-сайты, так и мобильные приложения. Так что в следующий раз, когда будете разрабатывать или тестировать сервисы, где есть личный кабинет, обращайте на это внимание.

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


  1. HaZeR
    30.10.2019 08:48

    Знакомая бага. Лет 8 назад, я такую же у регионального сотового оператора находил. Можно было в личный кабинет одного из сервисов попасть и слить весь баланс. По началу не верили, пришлось у них в офисе демонстрацию проводить, но исправили и даже футболку подарили.


    1. Gorodnya Автор
      30.10.2019 09:18

      Мне по операторам, например, вот такая статья нравится: https://habr.com/ru/post/305706/, и этот комментарий в моей.


  1. PashaWNN
    30.10.2019 08:49

    Во втором случае ведь ещё и есть полный доступ к сервису смс-оповещений от имени компании. В запросе и API-ключ присутствует. Так что можно на любые номера отправлять от их имени и за их счёт смс-сообщения.


    1. Gorodnya Автор
      30.10.2019 09:09

      Не получится, к счастью — при любом изменении параметров ошибка AUTH ERROR (sign).


  1. VANSCoder
    30.10.2019 09:48

    Про СМС рассылку это классика, надо было разослать клиентам сообщения «Ваши данные скомпрометированы, звоните на горячую линию магазина» и исправят охотнее если им не наплевать.

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


    1. Gorodnya Автор
      30.10.2019 10:20

      Собственно, выше я написал, что с помощью этого запроса SMS нельзя было рассылать, но кто знает, что можно в том сервисе рассылок)

      А по токенам — да, в конце третьего случая я указал: похоже на токен из моей предыдущей статьи Как ездить на такси за чужой счёт — это как раз оно)


      1. VANSCoder
        30.10.2019 15:09

        Странно получается просто, если нельзя менять текст сообщения то как тогда приложение отсылает разные коды? Или оно отсылает одно и тоже сообщение каждый раз? Вы пробовали генерировать sign?

        А про второе тупанул, думал речь была о скидочных картах.


        1. Gorodnya Автор
          30.10.2019 15:14

          При каждом новом запросе авторизации выполняется новый запрос с новым кодом.
          Генерировать подпись я, конечно, не пробовал, но все остальные параметры в запросе перебирал — ничего не отправляется, выдаёт ошибку AUTH ERROR (sign).


          1. VANSCoder
            30.10.2019 15:17

            Собственно по этому и нельзя было отправлять сообщения со своим текстом. А так ещё одна дырка для спамеров, надеюсь они догадались сменить API ключ.


    1. Alex_Galkin
      30.10.2019 14:26

      Скорее всего использовать чужой токен в своем приложении не позволит сервис провайдер или процессинг банка.
      Токен будет привязан к идентификатору приложения (мерчанта) и покупателя. Вызов этого токена из другого приложения вернет ошибку.
      Также в токенизации часто используют одноразовые токены. Перехваченный токен будет действителен только для одной оплаты и привязывать его куда бессмысленно.
      Так что тут приоритетная дырка скорее доступ, чем платежные данные.


      1. Gorodnya Автор
        30.10.2019 14:29

        В указанной статье про такси описано, что я успешно заказал и оплатил такси с чужим токеном.
        Там же указан пример документации одного из провайдеров. К сожалению, токены не привязываются к чему-либо.


        1. VANSCoder
          30.10.2019 15:13

          Знаю много приложений в которых токен привязывается к ID аккаунта. Ну а бывают такие что просто хранят токен на устройстве и не передают его никуда (привет бургеркинг) так что получив доступ к аккаунту придётся привязывать карту вновь.


          1. Gorodnya Автор
            30.10.2019 15:21

            Можете посмотреть API популярного в Украине платежного провайдера, там нет информации о привязке к ID аккаунта или ещё чему. Такие дела)


            1. Alex_Galkin
              30.10.2019 16:00

              На самом деле есть. Если посмотреть на описание формирования платежа.
              Там обязательный параметр merchantAccount. А вот clientAccountId необязательный.
              Из чего можно сделать вывод, что сервис провайдер имеет возможность проконтролировать, что выданный токен используется только тем мерчантом, который его получил. А вот наличие контроля пользователя под вопросом.

              В описанном случае про такси оплата чужим токеном получилась именно потому, что она проходила в рамках одного мерчанта. А контроля пользователя ни у мерчанта ни у сервис провайдера нет.
              Сервис провайдеру на заметку: Привяжи токен к пользователю!


              1. Gorodnya Автор
                30.10.2019 17:09

                Скорее всего (я давал API не того сервиса, через который работает такси), да, из-за того, что clientAccountId необязательный, и есть такие ситуации. Проблема в том, что наверняка большинство тоже не учитывают clientAccountId. Спасибо, что копнули глубже.


  1. Gorodnya Автор
    31.10.2019 21:40

    Коллеги прислали аналогичное: Как не нужно реализовывать смс авторизацию