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.

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.
| What to look for | Where it tends to appear | Why it matters |
|---|---|---|
| <script> element | Anywhere inside the root <svg>, often near the end of the file | Executable JavaScript with the page's full scripting context once the file is rendered |
| CDATA block | Wrapped around a script body as <![CDATA[ ... ]]> | Used so characters like < and & do not need XML escaping; a large CDATA section inside an image file is worth reading |
| Inline event handlers | Attributes on any element: onload, onclick, onmouseover, onerror | One attribute can be enough to run code, and onload on the root element fires on render |
| base64 blob | Text content of <desc>, <title>, <metadata>, a custom data- attribute, or a string literal in the script | A long alphanumeric run ending in = or == that a nearby script decodes is often the smuggled payload itself |
| Data URI | href, xlink:href, or a CSS url() reference, in the form data:text/html;base64,... | Carries a whole document inline, which the browser can navigate to or embed directly |
| foreignObject | A child element of the root <svg> | Lets arbitrary HTML, including iframe, form, and script elements, live inside what is nominally an image |
| Navigation attributes | <a href=...> wrappers and xlink:href on shapes and text | Turns a rendered shape into a click target that leaves for an attacker-controlled destination |
| External script reference | <script href=...> or <script xlink:href=...> | The 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.
<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:
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 hostThat 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.
<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:
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 hostThe 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
Can an SVG file contain malware?
What is SVG smuggling?
Why do phishing emails use SVG attachments?
How do I analyse a suspicious SVG file?
Are .svgz files dangerous?
Is it safe to open an SVG attachment in a browser to check it?
Continue Learning
Ready to decode?
See KlaroSkope transform obfuscated scripts into actionable intelligence.
Try It Free