# 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:

```plaintext
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:

```plaintext
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.

You can do security testing yourself, or I can do it for you.  
I handle security, you take care of business.

Book a free consultation call at [**https://cybermehul.com**](https://cybermehul.com) , get security assessments done before attackers do.
