Безопасность OAuth2 и Facebook Connect уязвимости

    Это — сиквел моей сногсшибательной первой статьи.

    Готов поспорить что каждый веб разработчик сталкивался с фейсбук коннектом или вконтакте логином или аутенфикацией через твиттер. Все это по сути построено на основе OAuth1/2.

    Мое мнение заключается в том что мы все ступили не на ту дорожку. OAuth это дорожка в ад (к слову, Эран Хаммер сейчас работает над заменой oauth — oz).

    В этой статье я не буду погружаться в модель атаки а перескажу совершенно конкретные уязвимости которые вы можете начать использовать прямо сейчас.

    image


    CSRF на логине


    В любой другой ситуации я бы сказал что CSRF на такой незначительной, хоть и state-changing, функции ничего не стоит. Но как только дело касается протоколов построенных на редиректах… Дело в том что в 99 процентов случаев процесс коннекта Site <= Facebook можно запустить загрузив GET /users/connect/facebook те начать процесс самому, без user interaction.
    Идея в том, чтобы перелогинеть юзера на Фейсбуке (провайдере) в свой аккаунт, загрузить этот эндпоинт для коннекта, который автоматически подключит аккаунт атакующего к аккаунту жертвы на клиенте, и потом входить в аккаунт жертвы самому.
    Фейсбук защищается лишь через проверку реферера:




    Это новое воплощение Самой Частой Ошибки (не проверки state) только на более высоком уровне. Мы не подкидываем свой колбэк со своим кодом, а заставляем сам фейсбук вернуть код от вашего аккаунта путем форсированного перелогина. ФБ отказалось это исправлять, так как это ломает совместимость с разными существующими плагинами для фб. VK.com тоже уязвим (этим ребятам даже репортить некуда, посмещище какое то. Даже магазин с фенечками более серьезно относится к безопасности). Этот баг можно использовать на любом сайте с omniauth, django-social-auth, php-sdk и так далее. Исправление — нужно проверять csrf token при загрузке /connect адреса.

    Я бы даже сказал сама идея перелогина бросает тень на OAuth — ведь даже с помощью cookie forcing можно полностью заменить cookies жертвы и перелогинеть его на любом Провайдере. Это концептуальная ошибка которую уже никак не исправить.

    signed_request


    СР это подписанный запрос с помощью client_secret и создан он… непонятно зачем. Дело в том что это по сути тот же code-flow только code передается в # фрагменте и выпущен он для redirect_uri = "". А значит если украсть signed_request через 302 редирект его можно использовать чтобы зайти как владелец СР на сайте клиента (просто проставить куку fbsr_CLIENT_ID=SR).

    Говоря простым языком, если на сайте есть Facebook JS SDK и вы нашли опен редирект (на домене или поддомене) — вы можете угнать любой аккаунт через слитие SR жертвы. И это опять таки не будет исправлено.

    OAuth1 фиксация Paypal


    Еще один WontFix. В пейпале перед запуском express checkout flow нужно получить request_token отослав все свои параметры и потом уже загружать paypal?token=request_token. Дело в том что вы можете выпустить этот токен для себя, путем фишинга загрузить этот УРЛ на браузере жертвы и убедить его оплатить этот токен (платеж и правда поступит Клиенту) то можно самому перейти по callback?token=token и получить все те деньги что заплатила жертва уже на свой аккаунт. Я проверял это на namecheap.

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

    Вывод


    Я крайне не рекомендую пользоваться логином через социальные сети. Если же вы вынуждены это реализовывать, постарайтесь прописать максимум проверок и поставить статические колбэки в настройках вашего клиента (hardening — уменьшение поверхности атаки). С багами выше я нашел эксплоиты для угона аккаунта на soundcloud, foursquare, songkick, airbnb, и еще десяток стартапов с соц логинами.

    P.S. в яндекс баунти написал как у них угонять аккаунты, но им не сильно интересно поэтому копирую сюда:

    1) фиксируем сессию на фейсбуке

    2) вызываем у залогиненого в яндексе урл
    social.yandex.ru/broker/start?retpath=https%3A%2F%2Fsocial.yandex.ru%2Fhtml%2Fcloser%2Fpromo_closer.html%23ddom%3D&consumer=social&popupName=social_popup&application=&action_if_anonymous=ignore&result_location=fragment&provider=fb&display=popup

    3) он автоматом приконектит атакерский фб к паспорту, но к счастью (моему сожалению) надо еще подтвердить auth через него. Немного соц инженерии, грузим
    passport.yandex.ru/passport?mode=authentication&retpath=https%3A%2F%2Fsocial.yandex.ru%2Fupdate%3Fprofile_id%3D23177612%26allow_auth%3D1
    как видите там ни слова про фейсбук или аккаунты, просто требует ввести пароль, что юзер с удовольствием сделает (side-bug. наверно стоит объяснять для чего спрашивают пароль а то мало ли)

    4) теперь можно через атакерский фб войти прямо в яндекс, отконнектить фейсбук, украсть почту и куки — perfect crime
    Original post in English.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 27

      +47
      Твоих мыслей ход понятен, автор, но суров очень стиль твой (с) Йода
        +3
        Я старался писать путем просто выбрасывания фактов без воды, иначе статья вышла бы раза в три больше и людям было бы лень читать. Сейчас же в каждой строчке сразу по несколько шагов, если что не понятно в комментариях с радостью уточню.
        +16
        Обожаю наблюдать, как неуловимый™ Хомяков находит довольно серьёзные дыры, его никто не хочет всерьёз воспринимать в Баунти, он сливает эксплойты в паблик и у всего white-hat сообщества натуральный бугурт. Цикличная ситуация, очередной цикл дождались :)
          +6
          > неуловимый™

          Вспомнился анекдот про неуловимого Джо, Я так больше не играю :(
          +1
          У меня заработало, только если пользователь изначально не залоинен на фейсбуке.
          Если же он залогинен под своим аккаунтом, то при попытке скрытого перелогина запрос блокируется:

          Refused to display 'https://www.facebook.com/common/invalid_request.php' in a frame because it set 'X-Frame-Options' to 'DENY'
            +3
            >У меня заработало, только если пользователь изначально не залоинен на фейсбуке.

            Да, но разлогинеть не сложно. Достаточно знать токен (любой) этой жертвы (можно слить с других сайтов) и загрузить logout.php?access_token=123

            Ошибка, что вы написали, не имеет никакого значения. Это отказ браузера показывать страницу с результатом, на работу CSRF никак не влияет.
            0
            А как можно защититься простым пользователям? Через полное отключение всех социальных плагинов (скажем, в AdBlock)?
              0
              Даже NoScript врядли поможет. Можно попробывать анти CSRF плагины но это все только для FF.
              +11
              Очень сложно и практически невозможно понять если не знаешь каких-то терминов.

              «фиксируем сессию на фейсбуке» — что это значит вообще?

              «signed_request. СР это подписанный запрос с помощью client_secret и создан он… непонятно зачем.» — так что такое «CP»? Почему именно СР? Использование аббревиатуры без расшифровки очень печалит. Попробуйте представить что вы не значете что такое СР и попытайтесь понять абзац. У меня не получилось.
                +1
                фиксируем сессию на фейсбуке

                Авторизируемся на фб через iframe, приведенный в начале?

                так что такое «CP»?

                signed_request == SR (en) == СР (ru)

                И правда действительно слишком сложным языком описаны на самом деле простые вещи.
                  +2
                  Никогда бы не догадался, что SR == СР. Спасибо.
                  –1
                  Помоему когда пишут сокращение без объяснение то по дефолту имеется слова чуть раньше (Signed Request).

                  Фиксируем сессию — понятно любому знакомому с атакой, фиксации сессии. Ну и звучит же очевидно.

                  Термины объяснены в первой статье, типа.
                    +4
                    Обычно сокращения имеют прямую связь с тем, что они собственно сокращают. В вашем случае связь весьма неочевидная. Можно было ещё сократить ЫК, тоже подобный подход.

                    Фиксируем сессию — понятно любому знакомому с атакой, фиксации сессии. Ну и звучит же очевидно.

                    «Фиксируем сессию — это фиксируем сессию». Это очевидно, но ни капельки не понятно.

                    Термины объяснены в первой статье, типа.

                    Первая статья закрыта, типа.
                      +4
                      Блин и правда была закрыта. Простите меня!
                  +1
                  В яндекс баунти отвечают минимум через месяц. Я посылал баги в декабре, а мне только в конце января ответили. (И присудили награду в 10к руб.)
                    +2
                    Ага или пишут идиотские отписки про «неиспользуемый браузер» на репорты XSSок, видимо штрафуют за уязвимости, обедов лишают… Причем какие-то еще и фиксят после репортов (не все перепроверял).

                    (как быстро коммент уйдет в минус?)
                      +1
                      +1, мне ответили через 21 день. Даже фейсбук быстрее отвечает, а у них объем говнорепортов колоссальный.
                        0
                        Они всегда так отвечают. А переписка с ними может затянуться настолько долго, что пропадает все желание)
                    0
                    Ну и заодно там где загружен родные sdk можно скриптом вытянуть acces_token
                    и тихо слить все данные
                      0
                      Да, это тоже. signed_request + access_token = доступ вообще ко всему
                        0
                        А для получения токена пользователь должен разрешить доступ для какого-то фб приложения или нет?
                          0
                          если пользователь уже залогинен на странице то нет.
                          например в консоли хрома можно выполнить

                          VK._session.sid
                          или
                          FB.getAccessToken()

                          то будет отображен AccessToken
                            0
                            (если доступ авторизован то токен утекет автоматически)
                              0
                              пока что это вариант использовать клиентскую авторизацию для последующей серверной авторизации
                        0
                        Протестировать бы…
                          +1
                          Все баги рабочие и исправлены не будут. можно тестить
                          +1
                          OAuth — кровавое месиво из подтверждения identity (это ад) и расшаривания прав (тут всё более-менее, хотя под контроль пользователя из провайдеров более-менее отдаёт на ручной контроль прав доступа только Facebook). Увы, ничего более-менее стандартного и готового нет. OA2 только добавляет новых workflow, при этом не предоставляя ни нормальной документации, ни унифицируя ничего.
                          А пока там все соберутся и будут использовать oz (или ещё что) ещё не один год пройдёт.
                          Пока гром не грянет (как тот коммит в rails), мужики даже креститься не станут.

                          Only users with full accounts can post comments. Log in, please.