6.6. Cross Site Scripting (XSS) - теория

 

Reflected XSS

Reflected XSS или отраженный межсайтовый скриптинг возникает, когда сервер использует значения полученных GET/POST параметров и без должной фильтрации вставляет их на страницу, которую затем отправляет обратно пользователю. Браузер пользователя, принимая запрошенную страницу, отображает пользователю. Если в качестве значений GET/POST параметров вставить вредоносный код на JavaScript, то можно добиться исполнения данного кода в браузере пользователя со всеми вытекающими последствиями.

Поясним написанное на простом и довольно распространенном примере.

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

Однако, что произойдет, если вместо обычной фразы или слова вставить вредоносный код JavaScript?

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

Почему такое стало возможным? Неужели нельзя вставлять переданные значения параметров на сгенерированных страницах?

Конечно же можно, просто проблема в том, что серевер не фильтрует введенные данные и не проводит проверку на наличие запрещенных и разрешенных символов.

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

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

Уязвимость Reflected XSS обладает следующими особенностями:

  • Довольно распространена атака с использованием GET запросов. Также можно провести атаку и с помощью POST запросов, однако такое возможно при определенных условиях и применяя более изощренные методы доставки.
  • Атака по сути является одноразовой, то есть, чтобы она сработала 2 и более раз, жертве необходимо все время открывать полученную ссылку. На практике такое происходит не часто.
  • Успешность атаки сильно зависит от методов доставки и примененных техник социальной инженерии, так как атака сработает только в том случае, если жертва перейдет по ссылке.

 

Поиск и тестирование Reflected XSS

Процедура тестирования и поиск уязвимостей состоит из нескольких шагов, которые в начале лучше выполнять в Burp Suite или ZAP, хотя иногда можно использовать только браузер:

  1. Определите точки входа или иначе говоря, места, где можно вводит и передавать данные серверу:
    1. URL с GET параметрами.
    2. Различные части URL, которые представляют собой каталог/папку (path)
    3. Различные формы, например форма поиска или фильтра.
    4. HTTP заголовки:
      1. Cookie
      2. Referrer
      3. User-Agent
      4. Любые кастомные заголовки
  2. Введите буквенные или числовые значения в найденные точки входа, а затем проверьте, возращает ли сервер введенные данные. Если да, то идем дальше.
  3. Добавьте к введенному слову такие символы, как кавычки, угловые скобки (<,>) и проанализируйте в каком контексте возвращаются эти данные. Например, они могут быть обернуты некоторыми HTML тегами, как <div>, <h1>, <span> и др. В данном случае эксплойт может сработать. Однако сервер может обернуть их в специальные экранирующие символы и тогда код/эксплойт не сработает.
  4. Внедрите простой JavaScript код. Если в ответном сообщении он возращается в неизменном виде, то перед нами подтвержденная уязвимость. Если код изменен или обрезан, то на сервере используется фильтрация. В этом случае попробуйте изменить код.
  5. Составьте требуемый эксплойт и протестируйте его в браузере.

Рассмотрим простой пример на основе приложения BodgeIT из виртуальной машины OWASP BWA. Первое, что бросается в глаза – это функция поиска по сайту:

Функция поиска в приложении BodgeIT

Тестирование Reflected XSS в функции поиска BodgeIT

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

Успешная атака Reflected XSS в BodgeIT

Отображение эксплойта XSS в панели разработчка

Как видим, эксплойт сработал - сервер не фильтрует вводимые данные.

Что делать, если при визуальном осмотре приложения в браузере не были обнаружены подходящие URL с GET параметрами?

Для ручного поиска уязвимостей незаменимым помощником станут программы сканирования директорий сайта, так называемые spiders/crawlers. Они позволяют полностью просканировать сайт и составить его структуру. Однако crawlers не всесильны и в некоторых случаях необходимо вручную исследовать сайт. Поэтому я бы рекомендовал комбинировать оба метода.

После того, как исследование структуры сайта закончено вы можете легко выделить подходящие  URL, а затем протестировать их, используя программы, как Burp Suite, ZAP.

Рассмотрим еще один пример на основе приложения Juice Shop. Данное приложение имеет несколько уязвимостей XSS. Одно из них было обнаружено благодаря ручному исследованию сайта.

Уязвимость присутствует в отслеживании купленного товара. Для этого необходимо войти на сайт. Вы можете использовать уже сущетвующую учетную запись и войти в систему, используя уязвимость SQL Injection на странице входа. Либо вы можете зарегистрироваться, как новый пользователь. Затем совершите покупку и перейдите в Account --> Orders & Payments --> Order History. Далее, для отслеживания заказа, кликнете на иконку с грузовиком:

Иконка с уязвимой ссылкой для отслеживания товаров в Juice Shop

Обратите внимание на параметр id, значение которого отображается в URL и на самой странице:

Поиск уязвимости Reflected XSS в функции отслеживания товаров в Juice Shop

Если текущее значение параметра в URL заменить на другое значение, например Test123, то оно также будет отображено на странице:

Тестирование Reflcted XSS в приложении Juice Shop

Следует отметить, что Juice Shop не является классическим приложением. Весь фронтенд приложение создается скриптами JavaScript, а обращение к серверу происходит с помощью AJAX. Поэтому, если вернуться в Burp Suite или ZAP, то вы не найдете ссылку, которую видете в браузере.

Чтобы увидеть реальную ссылку включите перехват запросов в Burp Suite; в браузере в качестве значения параметра id введите другое значение, например HelloHello, и нажмите Enter. Иногда приходится нажимать Enter два раза. Burp Suite перехватит первый запрос, однако не удивляйтесь, если не увидите введенный запрос. Приложение спроектировано так, чтобы отправлять серию запросов. Поэтому просто кликнете на кнопку Forward до тех пор, пока не увидите URL, которое будет содержать знакомое HelloHello:

Перехваченый URL из приложения Juice Shop

Вот как выглядит запрос и ответ в Burp Suite:

Перехваченный URL в Burp Suite

То есть бразуер через AJAX отправил запрос на сервер, сервер же вернул ответ в виде JSON, а загруженные в браузер скрипты вставят полученный ответ уже на страницу браузера. Если вставить широко известный эксплойт, то он не работает, хотя и не обернут специальными экранирующими тегами:

 Внедренный эксплойт не сработал

Это связано с тем, что современные бразуеры обладают встроенной защитой, которая позволяет предотвратить выполнение JS кода. Попробуем другой эксплойт, например <img src=”1” onerror=”alert(123)”>. Тэг <img> используется для вставки изображений. Атрибут src используется для указания источника, где хранится файл, а атрибут onerror запускает JS код, если произошла какая-то ошибка. То есть мы намеренно указываем несуществующий источник, чтобы вызвать исполнение кода. Подобным приемом можно пользоваться и для других тегов, например <iframe>.

В этот раз эксплойт сработал:

Успешно сработанный эксплойт XSS

 

Stored XSS

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

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

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

 

Поиск и тестирование Stored XSS

Процедура тестирования и поиск уязвимостей Stored XSS во многом схожи с поиском Reflect XSS:

  1. Определите точки входа:
    1. Различные части URL, которые представляют собой каталог/папку (path)
    2. HTML формы и другие элементы, передающие данные на сервер
    3. HTTP заголовки
  2. Введите буквенные или числовые значения в найденные точки входа, а затем проверьте в каком виде их возращает сервер.
  3. Добавьте к введенному слову такие символы, как кавычки, угловые скобки (<,>) и проанализируйте экранирует или фильтрует ли их сервер.
  4. Введите простой JavaScript код и если он исполняется, то уязвимость найдена и подтверждена.

В качестве демонстрационного примера можем воспользоваться приложением Peruggia, которое позволяет загружать и комментировать изображения. На главной странице отображается фотография, которую мы можем прокомментировать. Поэтому введем любой текст вместе с JS кодом:

Тестирование Stored XSS через комментарии

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

 

Stored XSS через HTTP заголовок

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

Снова воспользуемся Peruggia, однако в этот раз для загрузки файлов нам необходимо залогиниться. Переходим на страницу Login и вводим admin/admin. В верхней панели появляется дополнительный пункт меню Upload/Delete.

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

Тестирование Stored XSS через загрузку файла

К тому же, название файла отобразилось сразу в двух тэгах: <a> и <img>. Если подкорректировать название загружаемого файла, то теоретически мы бы смогли внедрить код в один из тэгов. Включаем перехватом запросов в Burp Suite и снова загружаем файл:

Перехваченный запрос с загрузкой файла

Как видно из рисунка название файла передается в заголовке Content-Disposition в параметре filename. Попробуем ввести какой-нибудь JS код и проверим, как отобразит его сервер:

Внедрение XSS эксплойта в HTTP заголовок

Нарушенный синтаксис DOM элементов из-за эксплойта

Сервер принял новое название файла и без изменений отобразил его на странице, однако синтаксис тэгов оказался нарушен. Остается поэкспериментировать с кавычками, чтобы добиться правильного синтакиса. После ввода эксплойта onerror="alert(123)"", код сработал:

 

Успешно сработанный эксплойт Stored XSS

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

 

DOM-based XSS

Данный тип уязвимости немного отличается от расмотренных выше Reflected/Stored XSS. Дело в том, что в случае с Reflected/Stored XSS вредоносный код внедряется на веб-страницу из пользовательского ввода на стороне сервера. А в DOM XSS вредносносный код внедряется уже в браузере, то есть на стороне клиента самими скриптами JS, которые формируют и изменяют страницу или иначе говоря манипулируют DOM.

Чтобы понять, как работает данная уязвимость, рассмотрим довольно распространенный пример, который реализован во многих современных веб приложениях. Откроем тестовое приложение Acunetix, а затем несколько раз кликнем на кнопку Next. При этом обратите внимание на URL и саму страницу, в которой отображается номер страницы:

Проверка и тестирования сайта на DOM XSS

Если вручную изменить значение URL, например на 3test, то оно также отобразится на странице:

Поиск и тестирование на наличие DOM XSS

На первый взгляд кажется, что это обычная уязвимость Reflected XSS, однако это не так. В URL присутствует символ решетки (#), после которой следует остальная часть URL. Все, что следует после решетки в URL, браузер на сервер не отправляет, то есть данная часть адреса обрабатывается локально в самом браузере. В этом легко убедиться, если открыть панель разработчика и переключиться на вкладку Network. Затем несколько раз кликнете на кнопку Next или просто обновите страницу. В сетевых запросах вы увидите совершенно другое значение URL:

Отправка AJAX запросов

Пагинация (навигация по страницам) на этом сайте реализована средствами JavaScript. Скрипты анализируют значение URL, а затем отправляют соответствующий запрос на сервер через AJAX в виде числового значения, при этом скрипты также вставляют на страницу значение из URL. Если сейчас вставить тестовый эксплойт в URL, то он сработает, причем на сервер данный эксплойт даже не попадет:

Успешный эксплойт для DOM XSS

Иными словами, если на странице присутствуют JS скрипты, которые динамически и небезопасно манипулируют элментами веб страницы, то велика вероятность наличия уязвимости DOM-based XSS.

Выше описанный пример всего лишь частный случай. Не все приложения используют решетку в URL, но тем не менее они также могут быть уязвимы DOM XSS. К тому же на сервер все же  может попасть эксплойт в строке URL, однако фильтрация на стороне сервера все равно не поможет предотвратить атаку.

 

Поиск и тестирование DOM-based XSS

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

Определите точки входа. К ним относят:

  • URL, особенно обращайте внимание на символ решетки (#)/
  • Заголовки:
    • Cookie
    • Referrer
    • Нестандартные кастомные заголовки

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

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

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

Функции, которые манипулируют с HTTP заголовками и URL:

  • URL
  • URLUnencoded
  • location
  • referrer
  • location
  • name

Свойства и функции, которые манипулируют с DOM:

  • write()
  • writeln()
  • domain
  • элемент.innerHTML
  • элемент.outerHTML
  • элемент.insertAdjacentHTML
  • элемент.onevent
  • eval()

Вернемся к тестовому приложению Acunetix. Оно содержит несколько уязвимостей, одно из которых мы уже нашли. Теперь попробуем исследовать его более тщательно. Открываем исходный код страницы и поищем скрипты:

Поиск и анализ JS скриптов на странице

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

Остальные скрипты мы проверим на наличие небезопасных функций. Для этого откройте панель разработчика и перейдите во вкладку Sources (для Chrome) или Debugger (для Firefox):

Отображение загуженных файлов в панели разработчика Chrome

Отображение загруженных файлов в панели разработчика Firefox

Откройте любой скрипт, например post.js. Затем комбинацией клавиш Ctrl+F откройте панель поиска. Далее поочередно в поле поиска вводите названия функций, которые приводят к DOM XSS. В результате получаем следующие результаты:

Поиск функций уязвимых  к DOM XSS

В строке 104 извлекается значение Cookie, в строке 109 извлекается содержимое заголовка Referer. Затем содержимое обоих заголовков с помощью jQuery добавляется на страницу в строке 114.

Далее, начиная со строки 117, следует другой блок с потенциальной уязвимостью. В нем в переменную hrf записывается URL страницы. Затем происходит проверка. Если URL не является "www.facebook.com", то на страницу записывается блок кода с тегами <div>. Причем сам блок содержит пользовательский атрибут data-href, значением которого является извлеченное URL.

Теоретически у нас имеются 2 потенциальные уязвимости. Мы не можем контролировать заголовок Cookie, но у нас имеется частичный контроль над URL и заголовком Referer.

Как вы знаете, заголовок Referer добавляется браузером, когда вы переходите по ссылкам с одного сайта на другой. Если злоумышленник на своем сайте разместит ссылку на уязвимый сайт, то при переходе по ссылке можно в заголовке Referer передать JS эксплойт, который может сработать на уязвимом сайте.

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