Более глубокое понимание XSS

В предыдущей части мы говорили о том, что зачастую при атаках нужно адаптировать вредоносный код XSS для обеспечения эффективности в разных ситуациях. Например, если точка внедрения находится в innerHTML, использование <script>alert(1)</script> не даст никакого эффекта. Поэтому нам нужно немного глубже изучить XSS, чтобы знать, какие методы можно использовать для атак.

Изучение атак — это изучение защиты. Чтобы эффективно защищаться, мы должны знать, как атаковать.

Способы выполнения JavaScript

Получив контроль над HTML, вы можете выполнять JavaScript несколькими способами.

Самый распространенный способ - использовать тег <script>. Однако одним из его недостатков является то, что его легко идентифицирует веб-брандмауэр (Web Application Firewall, WAF). Другой недостаток заключается в том, что он не работает в контексте innerHTML, как упоминалось ранее.

Помимо <script>, мы также можем использовать другие теги в сочетании со встроенными обработчиками событий для выполнения кода. Например:

<img src="qweqwe" onerror="alert(1)">

Этот код загружает несуществующее изображение и использует событие `onerror` для выполнения кода.

На самом деле, многие люди (включая меня) используют `x` в качестве `src`, потому что его легко писать и запоминать. Обычно путь `x` не существует, но если он существует, `onerror` не сработает. Поэтому есть шутка, что вы можете разместить изображение с именем `x` в корневом каталоге сайта, и некоторые злоумышленники могут не обнаружить уязвимость XSS.

Помимо `onerror`, можно использовать любой обработчик событий. Например:

<button onclick="alert(1)">Click me, please</button>

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

Для более короткого кода можно использовать событие onload в svg:

<svg onload="alert(1)">

Немного дополнительной информации: в HTML кавычки (`"`) для значений атрибутов не обязательны. Если в вашем содержимом нет пробелов, вы можете их удалить без проблем. Даже пробелы между тегами и атрибутами можно заменить на `/`. Таким образом, код для svg можно написать так:

<svg/onload=alert(1)>

Для создания полезной нагрузки XSS не требуются пробелы или кавычки.

Часто используемые обработчики событий включают: 1. onerror 2. onload 3. onfocus 4. onblur 5. onanimationend 6. onclick 7. onmouseenter

Помимо этих обработчиков событий, начинающихся с "on", есть еще один способ выполнения кода, который могли видеть разработчики фронтенда:

<a href=javascript:void(0)>Link</a>

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

<a href=javascript:alert(1)>Link</a>

Подводя итог, существует несколько способов выполнения JavaScript в HTML:

1. Тег <script> 2. Обработчики событий в атрибутах (начинающиеся с on) 3. Псевдопротокол javascript:

Знание этих методов позволяет использовать их в разных ситуациях.

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

XSS в различных сценариях и механизмах защиты

Обычно места, куда можно внедрить вредоносный код, называются точками внедрения.

<div>  Hello, <span id="name"></span></div><script>  const qs = new URLSearchParams(window.location.search)  const name = qs.get('name')  document.querySelector('#name').innerHTML = name</script>

Точка внедрения находится в строке document.querySelector('#name').innerHTML = name.

Различные точки внедрения влияют на то, как можно проводить атаки и как от них защищаться. Давайте рассмотрим три разных сценария.

HTML Injection

Это наиболее распространенный сценарий, будь то приведенный выше пример или фрагмент кода PHP ниже:

<?php echo "Hello, <h1>" . $_GET['name'] . '</h1>';?>

В обоих этих случаях вы манипулируете предоставленным вам пустым HTML, поэтому вы можете напрямую писать любые нужные элементы, что дает большую свободу действий.

Например, использование часто встречаемого кода <img src=qweqwe onerror=alert(1)> позволяет выполнить JavaScript.

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

Attribute Injection

Иногда вы можете столкнуться с кодом, подобно следующему, где в качестве значения атрибута в скобках используется введенное содержимое:

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Demo</title>
</head>
<body>
    <div id="content"></div>
    <script>
        const qs = new URLSearchParams(window.location.search);
        const clazz = qs.get('clazz');
        document.querySelector('#content').innerHTML = `
            <div class="${clazz}">
                Demo
            </div>
        `;
    </script>
</body>
</html>

В этом случае использование ранее упомянутого кода <img src=qweqwe onerror=alert(1)> не сработает, потому что введенное значение будет обрабатывается как содержимое атрибута.

Чтобы выполнить XSS в этом сценарии, можно экранировать атрибут и закрыть тег, например: "><img src=qweqwe onerror=alert(1)>. Таким образом, весь HTML превратится в:

<div class=""><img src=qweqwe onerror=alert(1)>">Demo</div>

После экранирования атрибута можно вставлять нужные HTML-теги.

Из этого случая можно понять, почему я упомянул, что контекст важен. Если вы думаете, что XSS встречается только в первом упомянутом мной сценарии и достаточно обрабатывать символы <>, то в этом контексте это будет неэффективно, потому что злоумышленник может атаковать без использования новых тегов.

Например, используя код " tabindex=1 onfocus="alert(1)" x=", который вообще не содержит <>, HTML превратится в:

<div class="" tabindex=1 onfocus="alert(1)" x="">  Demo</div>

В отличие от добавления новых HTML-тегов, этот метод атаки использует событие onfocus исходного тега div для выполнения XSS. Поэтому при фильтрации, помимо <>, необходимо кодировать также символы ' и ".

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

document.querySelector('#content').innerHTML = `  <div class=${clazz}>Demo</div>

Вышеупомянутый атрибут не заключен в кавычки или апострофы, поэтому даже если мы думаем, что защитили его, закодировав <>"', злоумышленник все равно может добавлять другие атрибуты с помощью пробелов.

JavaScript Injection

Иногда пользовательский ввод отражается не только в HTML, но и в JavaScript-коде. Рассмотрим пример:

<script>const name = "<?php echo $_GET['name'] ?>";  alert(name);</script>

Глядя на этот фрагмент кода, можно подумать, что достаточно закодировать кавычки ("), чтобы злоумышленник не смог выйти из строки. Однако такой подход ошибочен. Злоумышленник может закрыть тег скрипта (</script>) и внедрять дальше другие теги.

Поэтому, как и в предыдущих случаях, нужно кодировать не только кавычки, но и символы <, >, '. Это предотвратит выход из строки.

Однако даже при этом стоит учитывать, что добавление пустой строки приведет к ошибке SyntaxError, так как браузер не сможет интерпретировать код как допустимый JavaScript.

Рассмотрим еще один пример:

<script>const name = `Hello,<?php echo $_GET['name'] ?>`;alert(name);</script>

В этом случае для внедрения кода можно использовать шаблонные строки и синтаксис ${alert(1)}. Хотя для фронтенд-разработчиков такой код выглядит подозрительно, не каждый инженер может его распознать. Возможно, этот код написал бэкенд-разработчик, который просто подумал: "Для многострочных строк нужен этот символ", не осознавая его значения и потенциальных опасностей.

Заключение

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

Однако гарантирует ли кодирование символов <>"' полную безопасность? Не всегда.

Существует часто упускаемая из виду ситуация, которую мы кратко упомянули выше, но не обсуждали подробно. Мы познакомимся с этим далее.

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

1. Тег <script> 2. Обработчики событий в атрибутах (всегда начинающиеся с "on") 3. Псевдопротокол javascript:

Если первый метод, например innerHTML = '<script>alert(1)</script>', не работает, если невозможно использовать обработчики событий и псевдопротокол javascript:, а точка внедрения - innerHTML = data, какие еще способы можно использовать для выполнения скриптов? Подумайте над этим.

Last updated