Как обойти двух факторную аутенфикацию Authy с ../sms

http://sakurity.com/blog/2015/03/15/authy_bypass.html
  • Перевод
image

С помощью простого ввода ../sms можно было обойти второй фактор на сайтах использующих 2FA через authy.com (а их довольно много).

Самое интересное что такая досадная уязвимость появилась не по вине Authy, да и нашел я всю эту цепочку багов с большой удачей.

Итак, процесс работы с authy простой и состоит из двух API вызовов: api.authy.com/protected/json/sms/AUTHY_ID?api_key=KEY для запроса нового токена и api.authy.com/protected/json/verify/SUPPLIED_TOKEN/AUTHY_ID?api_key=KEY для верификации полученного токена от юзера. Оба вызова должны вернуть 200 статус, тогда запрос считается успешным.

1. Началось все с проверки их SDK библиотек. Все выглядело хорошо кроме authy-node, которая вообще не кодировала токен перед вставкой в URL — this._request(«get», "/protected/json/verify/" + token + "/" + id, {}, callback, qs);

Можно было просто вставить VALID_TOKEN_FOR_OTHER_AUTHY_ID/OTHER_AUTH_ID# в поле токена и перезаписать API запрос так, чтобы он всегда возвращал 200 статус, что считалось за подтверждение второго шага и пускало атакующего в аккаунт. Дело в том что все после # считается фрагментом и не отсылается на сервер, и запрос вида /protected/json/verify/VALID_TOKEN_FOR_OTHER_AUTHY_ID/OTHER_AUTH_ID будет возвращать 200 и его невозможно отличить от обычных запросов.

2. Дальше был замечен интересный баг в authy-python: urllib.quote кодировал все знаки кроме слеша (почему — не пойму). Хм, а что можно сделать со слешем? Может попробывать вставить /../? Как вы знаете, /../, /%2e%2e/ и даже /%252e%252e/ значат «перейти на одну директорию вверх» в браузерах. Но сервера совершенно не обязаны иметь такую функцию. Однако я решил ради теста послать ../sms и это сработало! Я пока еще понятия не имею почему, но теперь вставляя ../sms мы превращали /verify вызов в /sms вызов (/verify/../sms/authy_id) который возвращал 200 и пускал нас в аккаунт.

3. И тут я вспомнил что читал интервью Дэниела по архитектуре Authy совсем недавно. Там говорилось что Authy использует Sinatra, которая за собой тащит rack-protection, который как я помню имел модуль под названием path_traversal.

И тут самое интересное — оказалось что path_traversal зачем то декодировал path обратно и убивал директории перед /../. Ну, в теории, это должно было защищать разработчиков от потенциальных path traversal уязвимостей. На практике же это и стало причиной такой серьезной уязвимости.

Итак, по порядку:

1. Я ввожу ../sms в поле токена

2. Клиент кодирует это как ..%2fsms и делает запрос на api.authy.com/protected/json/verify/..%2fsms/authy_id

3. path_traversal декодирует полученный URL на раннем этапе в api.authy.com/protected/json/verify/../sms/authy_id, делит все по / и удаляет директорию /verify

4. Теперь само приложение получает модифицированный путь api.authy.com/protected/json/sms/authy_id который посылает СМС жертве и возвращает 200 статус и ответом {«success»:true,«message»:«SMS token was sent»,«cellphone»:"+1-XXX-XXX-XX85"}

5. Наш клиент который хотел проверить наш токен видит 200 статус и делает вывод что токен правильный. Даже если используется кастомная реализация API, клиент скорее всего ищет success=true что в нашем ответе тоже присутствует.

Попросту говоря введя ../sms в поле токена можно было обойти двух факторную аутенфикацию на всех сайтах использующих Authy.

Есть еще один замечательный текст про похожую уязвимость с обходом первого фактора через вставку символа | в другом популярном 2FA провайдере Duo Security, но мне его лень переводить.

Тут была ссылка что мы ищем хакеров, но ее удалили. Напишите в личку пожалуйста, если вы находите такого рода уязвимости во время утренней зарядки.
Поделиться публикацией
Комментарии 11
    –9
    Вы уже сообщили Authy.com об этом прискорбном явлении?

    Спасибо.
      +10
      Егор — чистейший whitehat, к тому же, проблема описана в прошедшем времени.
        +58
        Все исправили еще 8 февраля. Просто ждал пока их успешно поглотит twilio. Моя шляпа чище колумбийского кокаина.
        0
        Отличная работа!
          0
          Т.е. после первого вызова (/json/sms) мы уже получаем токен сессии и второй вызов нам нужен только для его подтверждения? Мне казалось, что токен сессии должен быть выдан только после совпадения кода из смс (в ответе на /json/verify).
            +1
            Какой сессии? Первый вызов говорит «пошли этому юзеру токен на телефон» второй его проверяет.
            –4
            Как я понимаю, подход тот же, что и в SQL инъекции.
            Спасибо за пост!
              +2
              Что-то я не могу найти сходств.
                0
                Да… п.5 вносит отличия.
                Я не прав.

                Имел в виду, что отсутствие экранирования — это bad. И в SQL инъекции и в данном методе вся проблема в отсутствии экранирования, если я правильно понимаю…
                  0
                  На самом деле довольно много чего ломается с помощью путешествия по файлам через ../
                0
                Вообще то да, я называю это format injection — когда есть формат с определенными правилами/делимитерами и он ломается через вставку. Только SQL это другой класс injection я бы выделил. xss-да

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

              Самое читаемое