Книга: «Все об уязвимости XSS». Глава 3: Как работает XSS — Анатомия атаки 🔬Контексты выполнения: HTML, JavaScript, атрибуты, URL

Ты нашёл точку входа. Вставил <script>alert(1)</script> . Не сработало. Вставил onerror=alert(1) . Опять мимо. В чём дело? В том, что твой payload — это ключ, а для каждого замка нужен свой ключ. В мире XSS замок — это контекст выполнения. Это то место в коде страницы, куда попадает твой ввод. Не поняв контекст, ты будешь как мартышка с гранатой — много шума, но цель не поражена. Давай разберёмся, какие бывают контексты и как подбирать под них правильные отмычки.

HTML Context: Классика жанра

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

Пример уязвимого кода:

<div>Результаты поиска: [USER_INPUT]</div>

Браузер читает этот HTML и ждёт, что внутри &lt;div&gt; будет текст. Но если ты вместо текста подсунешь ему теги, он их честно отрендерит.

Твои payload’ы:

• <script>alert(1)</script> Это дедовский метод, который до сих пор работает. Ты просто создаёшь новый HTML-элемент <script> , и браузер послушно выполняет его содержимое. Это альфа и омега XSS.

• <img src=x onerror=alert(1) > Что, если WAF или фильтр режет слово <script> ? Не проблема. Создаём тег <img> с несуществующим источником ( src=x ). Браузер не найдёт картинку и вызовет событие onerror . В обработчик этого события мы и пихаем наш код. Элегантно и эффективно.

• <svg onload=alert(1) > Ещё один мощный вектор. SVG-изображения — это, по сути, XML, который может содержать JavaScript. Событие onload сработает, как только SVG будет загружен в DOM. Фильтры часто пропускают <svg> , считая его безобидной картинкой.

• <iframe src="javascript:alert(1)"> Создаём iframe и в качестве источника указываем javascript: URI. Браузер выполнит код. Этот метод хорош, когда нужно изолировать свой payload от основного DOM.

Суть: В HTML-контексте твоя задача — создать новый тег, который может выполнять JavaScript.

HTML Attribute Context: Игра в кавычки

Здесь твой ввод попадает внутрь атрибута существующего HTML-тега.

Пример уязвимого кода:

<a href="/search?q=[USER_INPUT]">Поиск</a>

или

<input type="text" value="[USER_INPUT]">

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

Сценарий 1: Атрибут в кавычках ( value="..." )

Ты внутри кавычек. Чтобы вырваться, нужно сначала закрыть их.

Пример: <input value="USER_INPUT">

Payload: "><script>alert(1)</script>

Как это работает:

1. " — закрывает атрибут value . HTML превращается в <input value=""> .

2. > — закрывает тег <input> .

3. <script>alert(1)</script> — создаёт новый тег, который выполняется.

4. Остаток оригинального кода ( "> ) становится мусорным текстом в DOM, но уже никому не мешает.

Альтернативный payload (для URL-атрибутов типа href , src ): javascript:alert(1)

Если ты в атрибуте, который ожидает URL (например, <a href="USER_INPUT"> ), то можно использовать псевдо-протокол javascript: . Пример: <a href="javascript:alert(1)">

Сценарий 2: Атрибут без кавычек ( value=... )

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

Пример: <input value=USER_INPUT>

Payload: onmouseover=alert(1)

Как это работает:

1. Браузер видит <input value=твое_значение onmouseover=alert(1)> .

2. Он считает, что value закончился перед onmouseover .

3. onmouseover=alert(1) становится новым, совершенно валидным атрибутом — обработчиком события.

4. Когда юзер наведёт мышь на инпут, твой код сработает.

Твои лучшие друзья — event handlers:

• onmouseover — срабатывает при наведении мыши.

• onclick — требует клика.

• onerror — для медиа-тегов.

• onfocus — для полей ввода. Можно скомбинировать с autofocus , чтобы сработало сразу: autofocus onfocus=alert(1) .

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

JavaScript Context: Вырвись из строки

Это высший пилотаж. Твой ввод вставляется внутрь существующего тега <script> , обычно в строковую переменную.

Пример уязвимого кода:

<script>
var username = '[USER_INPUT]';
console.log('Welcome, ' + username);
</script>

Здесь &lt;script&gt; не сработает — ты уже внутри скрипта. Задача — сломать синтаксис JavaScript, выйти из текущей строки и выполнить свой код.

Payload: ';alert(1)//

Как это работает:

1. ' — закрывает строку var username = '' .

2. ; — завершает текущую инструкцию ( var username = ''; ). Это важно для синтаксической корректности.

3. alert(1) — твой собственный, новый JavaScript-код.

4. // — комментирует остаток строки. Без этого оригинальная закрывающая кавычка ( ' ) вызвала бы синтаксическую ошибку.

Финальный код, который видит браузер:

<script>
var username = '';alert(1)//';
console.log('Welcome, ' + username);
</script>

Всё, что после // , игнорируется. Профит.

Ещё пример (внутри функции):

<script>
trackEvent('login', '[USER_INPUT]');
</script>

Payload: &#039;);alert(1);(&#039;

Разбор:

1. ') — закрывает строку и вызов функции trackEvent .

2. ; — завершает инструкцию.

3. alert(1) — твой код.

4. ;(' — чтобы “съесть” оставшийся '); из оригинального кода. Получается ('); , что является синтаксически валидным (вызов пустой анонимной функции).

Особый случай — template literals:

<script>
var message = `Hello, ${'[USER_INPUT]'}`;
</script>

Payload: ${alert(1)} Здесь даже ничего ломать не надо. Template literals в ES6 позволяют выполнять выражения прямо внутри строки.

Суть: В JS-контексте твоя задача — завершить текущую строку/оператор и вставить свой код, не забыв закомментировать или сбалансировать оставшийся “хвост” оригинального кода, чтобы не было синтаксических ошибок.

URL Context: javascript: и `` спешат на помощь

Здесь твой ввод используется как часть URL. Это часто пересекается с контекстом атрибутов ( href , src , action ).

Пример уязвимого кода:

<script>
location.href = '[USER_INPUT]';
</script>

или

<a href="[USER_INPUT]">Перейти</a>

Твои payload’ы:

• javascript:alert(1) Самый прямой путь. Ты подменяешь протокол (http, https, ftp) на javascript: . Браузер видит это и выполняет следующий за ним код. Этот метод стар как мир, но до сих пор эффективен.

• text/html,<script>alert(1)</script> Более хитрый способ. `` URI позволяет встраивать данные прямо в URL. Здесь мы говорим браузеру: “вот тебе HTML-документ, отрендери его”. И он рендерит наш документ, который состоит всего из одного скрипта. Отлично обходит фильтры, которые ищут только javascript: .

Важный момент — URL-кодирование. Браузеры и серверы могут кодировать спецсимволы в URL. Пробел станет %20 , кавычка — %22 . Иногда это ломает payload, а иногда, наоборот, помогает обойти WAF. Например, javascript:alert(1) может быть закодирован как javascript:%61lert(1) . Экспериментируй.

Контекст решает всё

Прежде чем бросаться payload’ами, остановись и подумай.

1. Посмотри исходный код страницы. Где именно появляется твой ввод?

2. Определи контекст: это голый HTML, атрибут, JS-строка или URL?

3. Подбери правильный ключ:

• HTML -> Внедряй теги ( <script> , <img> , <svg> ).

• Атрибут -> Закрывай кавычки/тег или добавляй обработчики событий.

• JavaScript -> Завершай строку, вставляй свой код и комментируй остаток.

• URL -> Используй javascript: или `` URI.

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

45 views·3 shares