Книга: «Все об уязвимости XSS». Глава 4. Классические векторы атаки 💉 • Event handlers: onerror , onload , onmouseover — твои лучшие друзья
Если тег <script> — это прямой удар в челюсть, то event handlers — это удар ножом в спину в тёмном переулке. Тихий, незаметный и смертельно эффективный. Когда WAF блокирует слово script , когда CSP запрещает инлайн-скрипты (иногда криво настроенный), когда фильтры вырезают < и > — именно обработчики событий (event handlers) становятся твоим главным оружием.
Почему они крутые? Потому что они легальны. HTML построен на событиях. Браузер обязан реагировать на действия пользователя и состояние элементов. Мы просто заставляем его реагировать так, как нам нужно.
Анатомия атаки через события
Суть проста: мы внедряем HTML-атрибут, который содержит JavaScript-код. Этот код выполняется, когда происходит определённое событие.
<tag onEvent="javascript_code">
Самое вкусное здесь то, что нам часто даже не нужно создавать новый тег. Мы можем паразитировать на существующих тегах, если у нас есть инъекция в атрибут.
onerror : Король автоматических триггеров
onerror — это святой грааль XSS. Почему? Потому что он срабатывает автоматически и без участия пользователя. Нам не нужно ждать, пока жертва кликнет или наведёт мышь.
Классический payload:
<img src=x onerror=alert(1)>
Разбор:
1. Мы создаём тег <img> .
2. Указываем src=x . Естественно, картинки с адресом “x” не существует.
3. Браузер пытается загрузить картинку, обламывается и генерирует событие error .
4. Срабатывает обработчик onerror , который выполняет наш alert(1) .
Это происходит мгновенно при загрузке страницы. Пользователь даже моргнуть не успеет.
Где работает:
• <img> — классика.
• <video> , <audio> — если указать битый источник.
• <source> — внутри медиа-тегов.
• <input type="image"> — редкий, но рабочий зверь.
• <script> — да, у скриптов тоже есть onerror, если внешний файл не загрузился! Но инъекция в скрипт обычно блокируется.
Продвинутая техника:
Иногда фильтры блокируют тег img . Не беда.
<video src=x onerror=alert(1)></video>
<audio src=x onerror=alert(1)></audio>
onload и onfocus : Загрузил — выполнил
onload работает по тому же принципу, что и onerror , но срабатывает при успешной загрузке.
SVG — скрытый убийца:
<svg/onload=alert(1)>
Этот payload прекрасен тем, что он очень короткий и часто проходит мимо фильтров, которые ищут img или script . SVG загружается, событие load срабатывает, профит.
Body onload:
<body onload=alert(1)>
Если у тебя есть возможность редактировать тег <body> (редко, но бывает в CMS), это гарантированное выполнение при загрузке страницы.
Autofocus + onfocus:
Это гениальная комбинация, которая заставляет элемент сработать сразу после рендеринга, без действий пользователя.
<input onfocus=alert(1) autofocus>
Атрибут autofocus заставляет браузер сразу перевести фокус на этот инпут. Событие focus срабатывает. JS выполняется. Работает безотказно в современных браузерах.
onmouseover , onclick и другие: Социальная инженерия в действии
Эти векторы требуют взаимодействия с пользователем. Но если ты умеешь делать свои ловушки привлекательными, они сработают.
Onmouseover (наведение мыши):
<a href="#" onmouseover=alert(1)>Наведи на меня!</a>
Можно сделать прозрачный слой на весь экран, и любое движение мыши вызовет срабатывание.
<div style="position:absolute;top:0;left:0;width:100%;height:100%" onmouseover=alert(1)></div>
Пользователь даже не поймёт, что произошло.
Onclick:
<button onclick=alert(1)>Получить приз</button>
Требует клика. Классика фишинга.
Onscroll:
<body onscroll=alert(1)>
<div style="height:10000px"></div>
Стоит пользователю прокрутить страницу (а он прокрутит), как код выполнится.
Обход фильтров с помощью Event Handlers
Event handlers дают нам огромную гибкость в обходе WAF.
- “Мусор” между атрибутами
Фильтры часто ищут пробел перед on... . Но HTML позволяет использовать слэш / вместо пробела.
<img src=x/onerror=alert(1)>
Для многих регулярных выражений это уже не атрибут, а часть src . А для браузера — валидный код.
- Случайный регистр (Case Sensitivity)
HTML нечувствителен к регистру атрибутов.
<img src=x oNeRrOr=alert(1)>
WAF ищет onerror , а видит oNeRrOr и пропускает. Браузеру всё равно.
- Кодирование внутри атрибута
Внутри значения атрибута можно использовать HTML-сущности. Это мощнейшая техника обхода фильтров, которые ищут конкретные символы (например, скобки () ).
Задача: Выполнить alert(1) , но фильтр режет ( .
Решение:
<img src=x onerror="alert(1)">
&#40; — это ( , &#41; — это ) . Браузер декодирует сущности перед выполнением JS. Фильтр видит набор цифр, браузер видит скобки. Шах и мат.
Можно закодировать вообще всё:
<img src=x onerror="alert(1)">
Это alert(1) .
- Избегаем кавычек
Если фильтр режет кавычки, HTML позволяет писать атрибуты без них (если в значении нет пробелов).
<img src=x onerror=alert(1)>
Или даже так:
<img/src=x/onerror=confirm(1)>
- Использование confirm и prompt
Фильтры часто банят слово alert . Используй аналоги:
• prompt(1) — выводит окно с полем ввода.
• confirm(1) — выводит окно с кнопками OK/Cancel.
• print() — вызывает диалог печати.
Для PoC (Proof of Concept) этого достаточно. А для реальной атаки ты всё равно будешь использовать eval , fetch или подгрузку внешнего скрипта.
Топ неочевидных Event Handlers
Кроме попсы типа onerror , есть экзотика, которую часто забывают добавить в черные списки.
1. ontoggle (для тега <details> ):
<details ontoggle=alert(1)>Click me</details>
- Срабатывает при открытии/закрытии спойлера.
2. onanimationstart :
<style>@keyframes x{}</style>
<div style="animation-name:x" onanimationstart=alert(1)></div>- Требует CSS, но срабатывает автоматически.
3. onpointerover : Современный аналог onmouseover , о котором часто забывают WAF’ы.
4. onbeforeinput : Срабатывает перед вводом текста в поле.
5. ondrag / ondrop : Можно заставить пользователя перетащить что-то и получить XSS.
Когда Event Handlers не работают
1. Контекст скрипта: Если ты внутри <script> , атрибуты не работают. Там нужен JS-синтаксис.
2. Строгий CSP: Если Content Security Policy запрещает unsafe-inline , то инлайн-обработчики ( onclick="..." ) будут заблокированы. Это их главный враг. Но даже CSP иногда можно обойти (об этом в главе 11).
3. Санитизация (Sanitization): Библиотеки типа DOMPurify вырезают все опасные атрибуты (начинающиеся на on ). Это лучшая защита.
Заключение
Event handlers — это швейцарский нож XSS-атаки. Они гибки, разнообразны и позволяют обходить множество примитивных фильтров. Твоя задача — знать их наизусть, понимать, когда и как они срабатывают, и уметь комбинировать их с техниками обфускации.
Не зацикливайся на onerror . Исследуй спецификации HTML, ищи новые события. Браузеры постоянно добавляют новые фичи, а значит — новые векторы атаки. Будь исследователем. И помни: любой атрибут, начинающийся на on , — это потенциальная дыра в безопасности. Твоя задача — найти ту, которую забыли закрыть.
