Stored XSS via SVG Upload

Engagement Summary

During a recent web application penetration test, I discovered a Stored Cross-Site Scripting (XSS) vulnerability by uploading a malicious SVG file containing inline JavaScript.

This blog outlines how the vulnerability was discovered, exploited, and responsibly disclosed.

What Is Stored XSS?

Stored XSS occurs when user-supplied input is stored by the server (e.g., in a database or file) and later rendered in a way that executes code in other users’ browsers. In this case, the payload was stored as an SVG file and served back to users with insufficient sanitization or CSP.


Vulnerable Functionality

The application allowed users to upload image files, including SVGs. These images were later rendered directly into the DOM using an <img> tag.

Example HTML:

htmlCopyEdit<img src="/uploads/user-image.svg">

Here’s the catch: the server preserved the Content-Type header of the uploaded file (e.g., image/svg+xml), and served the file inline without sanitization or restrictive CSP headers.


The Malicious SVG Payload

I crafted a minimal SVG file with embedded JavaScript:

xmlCopyEdit<?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 xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)">
  <circle cx="50" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>
</svg>
  • The onload event on the <svg> element triggers when the image is rendered.

  • The payload triggers a simple alert() — in a real scenario, this could be replaced with data exfiltration.


Proof of Concept

  1. Upload the malicious .svg file through the image upload feature.

  2. Note the image is embedded using <img src="/uploads/user.svg">.

  3. When any user visits the page containing the uploaded image, the onload JavaScript runs in their browser.

📸 Result: JavaScript executes in the victim’s browser — a stored XSS.


Why This Works

  • SVG is not sanitized.

  • SVG allows inline JavaScript and event handlers.

  • Uploaded file was served with Content-Type: image/svg+xml.

  • No Content Security Policy (CSP) to restrict inline scripts.

  • Rendering the SVG in an <img> tag does not prevent JavaScript execution in SVGs.

Note: Despite common belief, <img src="malicious.svg"> can execute JS in some SVGs, especially in legacy or improperly configured browsers.


Takeaway

Even "image" files like SVGs can carry dangerous payloads. Always assume that uploaded files are hostile and sanitize or sandbox them appropriately.

There were constraints which stopped me to perform further attacks

You can do it yourself, or we can do it for you. You handle everything, we handle pentesting for you. Contact us today to get free consultation.

0
Subscribe to my newsletter

Read articles from RockByte Security directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

RockByte Security
RockByte Security