Cross-site Scripting Injection Attacks Using SVG Images

Christopher Davis

Cross-site scripting attacks, like all injection attacks, are a perennial favorite of attackers worldwide. These attacks focus on injecting malicious JavaScript that targets users of the website instead of the server itself. The most common way of performing a cross-site scripting attack is to leverage a user input field that is seen by others, such as a comment on a video that does not properly sanitize input.

The simplest way to create a cross-site scripting payload is to embed hostile JavaScript between two script tags. However, this sort of attack is easily detected and protected against through a myriad of sanitization libraries. A less common attack vector would be to embed JavaScript within an SVG image. SVG stands for scalable vector graphics, and instead of recording color and pixel placement such as a Bitmap or JPEG file type, SVG images are created through mathematical formulas. This allows the image to scale flawlessly no matter the size or resolution.

It is also possible to embed JavaScript within these types of images, meaning they can become a less common and harder to detect method of performing a cross-site scripting attack. It is extremely easy to create an image with a hostile JavaScript payload. However for this stored cross-site scripting attack to be successful, the image must be directly rendered on the page. Otherwise, the image will still be dangerous, but it requires the victim to directly view the image location.

An example of a stored attack is the image below. This image will run a harmless JavaScript alert if viewed directly. To do so, right-click on the image, then click “Open Image in New Tab”.

A SVG image of the Rietta Logo with a harmless XSS Alert stored within the image

If you were to download this image and open it in a plain text editor instead of an image editor, you would see that consists of XML markup, which can include arbitrary content:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="750"
   height="200"
   viewBox="0 0 198.4375 52.916666"
   version="1.1"
   id="svg8"
   sodipodi:docname="logo.svg"
   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(-9.8676114,4.8833333)">
    <path
       sodipodi:type="star"
       id="path3725-5"
       sodipodi:sides="6"
       sodipodi:cx="104.14816"
       sodipodi:cy="-16.792293"
       sodipodi:r1="7.333178"
       sodipodi:r2="6.350718"
       sodipodi:arg1="1.0502155"
       sodipodi:arg2="1.5738143"
       inkscape:flatsided="true"
       inkscape:rounded="0"
       inkscape:randomized="0"
       d="m 107.79557,-10.430538 -7.33315,-0.02213 -3.647402,-6.361755 3.685742,-6.339624 7.33314,0.02213 3.64741,6.361756 z"
       style="fill:#131f6b;fill-opacity:1;stroke-width:0.05937638"
       transform="scale(1,-1)" />
  <!-- The below lines were added in a text editor to the image XML. This is the stored XSS attack. -->
  <script type="text/javascript">
    alert("This is an example of a stored XSS attack in an SVG image")
  </script>
  </g>
</svg>

This represents a potential vulnerability, but it is not as serious as an attack that will fire as soon as the page is loaded, as a directly rendered image would. One of the best ways to stop this attack completely would be to disallow image tags and image uploads completely. Mime type checking is not enough to detect this attack, as the hostile SVG image only identifies itself as an SVG image. If image uploads must be allowed as a necessary feature, implementing controls such as forcibly converting SVG images to a JPG or PNG file type, or disallowing SVG image uploads entirely would provide a way to avoid this attack.

However, if SVG images are required as an absolutely necessary feature of an application, there are steps to more safely allow for SVG uploads. The first way is to avoid rendering the image on the site, instead requiring it to be downloaded. You can do so by setting the content-disposition attribute to attachment, which the browser will process by asking to save the file instead of rendering it on the site.

Enclosing the image in HTML image tags will prevent the JavaScript from executing, but extra precautions need to be taken with this method as attackers may attempt to break out of this through the use of embed or other HTML tags.

A more secure way to protect a site is by setting up the Content Security Policy on the page to disallow inline JavaScript. This will provide more overall protection than only forcing image tags around the uploaded image.

Finally, if your use case does not allow for inline JavaScript to be blocked entirely, you may be able to mitigate this attack by rendering uploaded images in a sandboxed IFRAME HTML tag. This attack will still fire if the SVG is rendered as the source of a standard IFRAME tag, if using this technique to mitigate this attack, it is imperative that the sandbox attribute is set.

Hopefully this article provides both insights into an unusual attack vector for Cross-Site Scripting attacks, as well as techniques for defense in depth against them.