Techniques07-Aug-26|9 min read

SVG Smuggling Analysis: Inspect a Malicious SVG Attachment

You have a suspicious .svg or .svgz attachment. Here is what to look for inside the XML, and how to get the embedded script and base64 payload back as readable text without rendering the file.

Definition:

SVG smuggling is a delivery technique that hides a payload inside an SVG image file. SVG is not a picture format in the way PNG and JPEG are; it is XML markup, and a browser renders it as a document. That means an .svg attachment can carry a <script> element, an inline event handler such as onload, a base64 blob that a script decodes at render time, or a foreignObject holding a block of HTML. Rendering the file parses that markup as a document, which is what turns an image attachment into a delivery mechanism.

You have a suspicious .svg attachment sitting in a quarantine queue or an inbox, and you want to know what is inside it before anyone double-clicks it. The instinct is to open it and look at the picture. That instinct is the problem. A browser is the tool most likely to run whatever the file's markup asks for, and an SVG attachment used for phishing is usually written on the assumption that you will do exactly that. Reading it statically is straightforward, because SVG is text. Malicious SVG analysis mostly comes down to knowing which parts of the XML to read. This article covers why SVG attachments carry payloads at all, the specific places worth checking, two synthetic samples walked through end to end, and what changes when the file arrives as a gzip-compressed .svgz.

A wireframe vector illustration drawn from thin luminous outlines and bezier anchor points on a dark background, its paths unraveling from left to right into streams of code-like dashes that form a skull, representing a script payload hidden inside an SVG image file

Have the file in front of you? Upload the .svg or .svgz at klaroskope.com/submit and get the embedded scripts, event handlers, navigation targets, data URIs, and base64 payloads back as readable text, without rendering the file.

  • Why phishing emails use SVG attachments
  • What to look for in a suspicious SVG
  • Walkthrough: an SVG with an inline script
  • Walkthrough: SVG smuggling with a base64 payload
  • What changes with .svgz
  • Why opening it in a browser is the wrong first move

Why Phishing Emails Use SVG Attachments

An SVG phishing attachment has a set of properties that operators find useful, and they compound. The file reads as an image to the recipient, and often to filtering that keys on the extension and the MIME type, since both say image. It is plain text, so it is cheap to regenerate per recipient with a different byte layout, which reduces the value of hash-based blocklists. And when the recipient opens it, the default handler on most desktops is a browser, which treats the file as a document with a live scripting context rather than as a static bitmap.

The payload usually does not stay in the SVG. A common shape is a short piece of script that assembles an HTML page in memory, hands it to the browser as a downloadable file or an in-page navigation, and lets that page render the credential-harvesting form or drive the next download. The SVG is the wrapper that got past the gateway; what matters to a defender is what the wrapper builds. This is a format boundary problem: the mail filter parses MIME, the image scanner accepts a valid SVG, and the script analyser receives no separate script file to scan, because the script only exists once the XML is parsed.

Public reporting through 2025 and 2026 describes SVG attachments turning up regularly in credential-phishing waves, commonly disguised as an invoice, a voicemail notification, or a document preview. The technique is not new and it is not exotic. It is mostly a packaging decision, which is why it can be handled by reading the file rather than by detonating it.

What to Look For in a Suspicious SVG

Because the file is XML, the interesting parts concentrate in a small number of places. Open it in a text editor rather than an image viewer, or hand it to a static analyser, and work through this list.

Payload loci inside an SVG file
<script> element
Where it tends to appearAnywhere inside the root <svg>, often near the end of the file
Why it mattersExecutable JavaScript with the page's full scripting context once the file is rendered
CDATA block
Where it tends to appearWrapped around a script body as <![CDATA[ ... ]]>
Why it mattersUsed so characters like < and & do not need XML escaping; a large CDATA section inside an image file is worth reading
Inline event handlers
Where it tends to appearAttributes on any element: onload, onclick, onmouseover, onerror
Why it mattersOne attribute can be enough to run code, and onload on the root element fires on render
base64 blob
Where it tends to appearText content of <desc>, <title>, <metadata>, a custom data- attribute, or a string literal in the script
Why it mattersA long alphanumeric run ending in = or == that a nearby script decodes is often the smuggled payload itself
Data URI
Where it tends to appearhref, xlink:href, or a CSS url() reference, in the form data:text/html;base64,...
Why it mattersCarries a whole document inline, which the browser can navigate to or embed directly
foreignObject
Where it tends to appearA child element of the root <svg>
Why it mattersLets arbitrary HTML, including iframe, form, and script elements, live inside what is nominally an image
Navigation attributes
Where it tends to appear<a href=...> wrappers and xlink:href on shapes and text
Why it mattersTurns a rendered shape into a click target that leaves for an attacker-controlled destination
External script reference
Where it tends to appear<script href=...> or <script xlink:href=...>
Why it mattersThe script body is empty, so reading the script text finds little; the destination attribute is the indicator

Walkthrough: An SVG With an Inline Script

Here is a minimal synthetic sample. It is benign by construction: the payload is alert(1), the standard proof-of-concept call, so the file demonstrates the mechanism without doing anything to the machine that renders it.

xml
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" onload="alert(1)">
  <rect width="120" height="120" fill="#0f0f0f"/>
  <text x="12" y="64" fill="#eee">Invoice</text>
  <script type="text/javascript">
    alert(1);
  </script>
</svg>

Two separate execution paths sit in seven lines. The <script> element is the obvious one. The onload attribute on the root <svg> element is the one that gets missed, because it does not look like code; it looks like a rendering hint. Either is enough on its own, and samples in the wild often carry both, so the file still runs if one of them is stripped.

Analysed statically, both loci come back as text:

text
INPUT: invoice.svg (249 bytes, plain SVG)

EXTRACTED
  script element:  alert(1);
  event handler:   onload on the root <svg> element, value alert(1)
  indicators:      none recovered; this synthetic sample contacts no host

That is the whole answer for a sample this simple, and it took no rendering to get. In a real submission those same two loci are where the interesting part lives: a URL, a document.write of a credential form, or a call that assembles the next stage.

Walkthrough: SVG Smuggling With a Base64 Payload

The first sample puts its payload in plain sight. The pattern usually meant by SVG smuggling splits the file into two halves: an inert-looking blob of data, and a short piece of script that turns that blob back into a document. Neither half looks like much on its own, which is the point.

xml
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120">
  <rect width="120" height="120" fill="#0f0f0f"/>
  <desc id="p">PGh0bWw+PGJvZHk+PGgxPkludm9pY2U8L2gxPjxzY3JpcHQ+YWxlcnQoMSk8L3NjcmlwdD48L2JvZHk+PC9odG1sPg==</desc>
  <script type="text/javascript"><![CDATA[
    var blob = atob(document.getElementById("p").textContent);
    var url = URL.createObjectURL(new Blob([blob], { type: "text/html" }));
    var a = document.createElement("a");
    a.href = url;
    a.download = "invoice.html";
    a.click();
  ]]></script>
</svg>

The blob sits in a <desc> element, which is a legitimate SVG element for a long-form description and therefore an unremarkable place for a wall of text to live. The script reads it by id, base64-decodes it, wraps the result in a Blob with a text/html type, and clicks a synthetic anchor to trigger a download. The recipient sees a save dialog for invoice.html. The browser assembled that file locally rather than fetching it, so there is no download URL for a proxy or a URL reputation service to inspect, and that absence is the evasion the technique is built around.

Reading the file statically pairs the blob with the script that consumes it, and decodes it:

text
INPUT: invoice.svg (541 bytes, plain SVG)

EXTRACTED
  script element:  var blob = atob(document.getElementById("p").textContent); ...
  base64 blob:     inside <desc id="p">, referenced by the script above
  decoded blob:    <html><body><h1>Invoice</h1><script>alert(1)</script></body></html>
  indicators:      none recovered; the decoded page contacts no host

The decoded line is what a triage decision rests on. Here it is a benign page that pops an alert. In a live sample it is commonly a credential-harvesting form posting to an attacker-controlled endpoint, or a page that redirects on load, and the host it names is the indicator worth blocking and sweeping for. Note also that the pairing matters as much as the decode: a base64 blob on its own could be a legitimately embedded font or thumbnail, and it is the decoding script sitting beside it that makes the blob interesting.

What Changes With .svgz

A .svgz file is a gzip-compressed SVG, so what gets reported as svgz malware is the same technique with one more wrapper around it. Same XML, same loci: the file starts with the gzip magic bytes 1f 8b rather than with a < character, so a text editor shows binary noise and a grep for <script> comes back with no match. That is essentially the whole difference, and it is enough to defeat a first-pass triage that assumed the file would be readable text.

Treat .svgz as an SVG that needs one decompression step first. Run it through gzip -dc file.svgz, and the checklist above applies to the output. Do not assume the .svg extension rules out a gzip container either, since some tooling accepts a gzipped SVG under either name; checking the first two bytes settles it. KlaroSkope accepts both .svg and .svgz and decompresses the gzip form before analysis, so this is not something you need to normalise by hand before uploading.

Why Opening It in a Browser Is the Wrong First Move

Double-clicking the attachment is the fastest way to see what it looks like and the fastest way to run it. There is no preview mode for SVG: rendering the file is executing the file. The onload handler fires, the script block runs, the base64 blob gets decoded and handed to whatever the script does with it, and any network call the script makes has already left the machine by the time the tab finishes painting.

The same caution applies to a few habits that feel safer but are not, or not by much: dropping the file into an online SVG viewer or converter, previewing it in a file manager or an IDE that renders SVG thumbnails, and pasting a base64 blob into the address bar as a data: URI to see what it holds. The last one is worth calling out because it feels like decoding. Navigating to a data:text/html URL renders that HTML with the browser's normal capabilities. Decode the blob with something that does not render the result, then read it as text.

Rendering an SVG is running it. A browser tab, an SVG-to-PNG converter, an IDE preview pane, and a file manager thumbnailer each parse the markup, and several of them execute what it contains. Decode the file statically first, and treat the recovered HTML or JavaScript as the thing you actually analyse.

Where the SVG Sits in the Delivery Chain

An SVG attachment is rarely the whole attack. It is one hop in a chain assembled to cross parser boundaries: mail gateway to attachment, attachment to rendered document, rendered document to a downloaded HTML file or a redirect, and from there to whatever the operator wanted to deliver. Each hop hands the payload to a different parser, and detection that lives inside one parser has trouble seeing across the handoff. What is a format boundary attack works through that structure in detail.

The format choice is often not the operator's own work either. Loader-as-a-Service describes delivery chains sold as subscription products, where the wrapper format is a configuration option rather than a fingerprint of a particular group. That is worth keeping in mind during triage: the SVG wrapper tells you comparatively little about who sent it, while the decoded payload and the hosts it names tell you a good deal about what to contain.

If the recovered script is itself obfuscated rather than readable, which is common once the payload is more than a proof of concept, the next step is a JavaScript deobfuscation problem rather than an SVG one. JavaScript string array deobfuscation covers the shape that turns up most often, and the multi-layer problem covers what happens when the layers stack.

Decode a Suspicious SVG

For file uploads, including gzip compressed .svgz attachments, use the full console at klaroskope.com/submit.

Frequently Asked Questions

Q

Can an SVG file contain malware?

An SVG file can contain executable script, which is the property that matters. SVG is XML markup rather than a bitmap, and the format allows a <script> element, inline event handler attributes such as onload, data URIs, and a foreignObject element that can hold arbitrary HTML. When the file is rendered by a browser, or by anything that embeds a browser engine, that markup is parsed as a document and the script runs. The SVG usually does not carry a traditional executable itself; it carries the code that builds or fetches the next stage.
Q

What is SVG smuggling?

SVG smuggling is the practice of hiding a payload inside an SVG file as data, alongside a short piece of script that reconstructs it when the file is rendered. A common form places a base64 blob in an element such as <desc>, or in a custom data- attribute, then uses script to decode it, wrap it in a Blob, and trigger a download or an in-page navigation. Because the resulting file is assembled in the browser rather than fetched over the network, there is no download URL for a proxy or a reputation service to inspect, which is the evasion the technique is built around.
Q

Why do phishing emails use SVG attachments?

An SVG phishing attachment reads as an image to the recipient, and often to filtering that keys on extension and MIME type, while behaving as a scriptable document once it is opened. It is also plain text, so it is cheap to regenerate per recipient with a different byte layout, which reduces the value of hash-based blocklists. Public reporting through 2025 and 2026 describes SVG attachments turning up regularly in credential-phishing waves, commonly disguised as invoices, voicemail notifications, or document previews.
Q

How do I analyse a suspicious SVG file?

Read it as text rather than rendering it. Open the file in a text editor, or hand it to a static analyser, and look for <script> elements, CDATA blocks, inline event handlers such as onload and onerror, long base64 runs in element text or attributes, data: URIs, foreignObject blocks, and href or xlink:href navigation targets. Decode any base64 blob with a tool that does not render the result, then read the decoded HTML or JavaScript. If the file is a .svgz, decompress the gzip container first. You can also upload the file at klaroskope.com/submit, which extracts those loci and returns them as readable text.
Q

Are .svgz files dangerous?

A .svgz file carries the same risk as the SVG inside it, since it is a gzip-compressed SVG and not a different format. A sample catalogued as svgz malware is ordinary SVG smuggling that happens to arrive gzip-wrapped. The practical difference falls on the analyst rather than the attacker: the file begins with gzip magic bytes instead of XML, so a text editor shows binary content and a grep for <script> comes back empty, which can make a malicious file look inert on a first pass. Decompress it and apply the same checks you would apply to any SVG. KlaroSkope accepts both .svg and .svgz.
Q

Is it safe to open an SVG attachment in a browser to check it?

Rendering an SVG is what executes it, so opening it to check it is the step worth avoiding. A browser parses the markup as a document, fires any onload handler, runs any script element, and follows what the script does next, including network requests. Online SVG viewers and converters, IDE preview panes, and file manager thumbnails parse the same markup, and several of them execute it too. Decode the file statically and read the recovered content as text; if you later want to render something, render the decoded output in an isolated environment rather than the original attachment.

Found this useful? Sharing is caring!

Ready to decode?

See KlaroSkope transform obfuscated scripts into actionable intelligence.

Try It Free