From SVG and back, yet another mutation XSS via namespace confusion for DOMPurify < 2.2.2 bypass

Snippet 1: final payload

What is DOMPurify?

DOMPurify is a widely used HTML sanitizer library. It is mainly used to sanitize user input on web applications that permits the creation of HTML/Rich Text content. Think of a web-mail client or blog platform for example. A common usage pattern for DOMPurify is the following:

DOMPurify usage sample

Namespaces, and why they are important

HTML is a markup language based on XML. XML utilizes the concept of namespaces. An HTML document, compliant with the current specification of HTML 5, may contain elements from three different namespaces.

style tag in the HTML namespace
Figure 1: style tag in the HTML namespace
style tag in the SVG namespace
Figure 2: style tag in the SVG namespace

DOM mutation in a nutshell

Although it may sound counter-intuitive, parsing and serializing a DOM fragment is not an idempotent operation. This means that in some cases, depending on how the fragment is constructed, the serialized version of a DOM tree won’t result in the same DOM tree when parsed. This double-parsing behavior is inherent to DOMPurify’s standard usage. However, some unexpected results from double-parsing DOM fragments are not unexpected at all, as they are documented in HTML’s current specification. One of these cases regards nested forms. A DOM fragment with nested form tags is not to be considered a valid construction according to HTML’s current specification. However, the following HTML fragment when parsed once will result in a DOM tree with nested forms.

Snippet 2: nested form mutation gadget
nested forms parsed twice
Figure 3: nested forms parsed twice
Figure 4: table ownership mutation gadget

Foreign content

By default, all elements in an HTML document must be parsed by a browser according to the rules defined by the HTML namespace; however, if the parser encounters a <svg> or <math> tag, it should then parse those elements and their descendants according to the SVG and MathML namespaces respectively. One also needs to consider that both the MathML and SVG namespaces support foreign content as well, meaning a chain of namespaces transitions can be built as shown below.

Figure 5: html->svg->math->html transition chain

Namespace confusion, putting it all together

As mentioned before, homograph elements like the style tag have different properties and rendering rules depending on the namespace they are in. This means that if we use an ownership mutation gadget to change the direct parent of a homograph tag and cause its namespace to change, we can trick the sanitizer into producing a malicious serialized HTML fragment. That is exactly what Michał did. Michal’s original bypass used an ownership mutation gadget to change the direct parent of a mglyph tag from a form element in the HTML namespace to a mtext tag in the MathML namespace. This mutation results in the descendants of the mglyph element becoming members of the MathML namespace in the final DOM tree while existing in the HTML namespace in the tree sanitized by DOMPurify.

Figure 6: update parser feature
Figure 7: original mXSS bypass

Building the final payload

Now that I described the building blocks of a potential bypass I will describe the methodology I used to build my bypass. I began by pondering the following:

Figure 8: from SVG to MathML
Snippet 2: bypass fragment
Figure 9: bypass fragment in the MathML namespace
Figure 10: final payload DOM tree before sanitization
Figure 11: final payload DOM tree after sanitization

Disclosure timeline

I notified cure53 on 11/02/2020 around 13:30 (GMT-6). A fix was made available and tested at 13:41 (GMT-6). The fix was officially published in version 2.2.2 sometime later on the very same day.

Acknowledgments

I want to thank Michał Bentkowski and Gareth Heyes for their incredible work and constant contributions to the security community. Dr.-Ing. Mario Heiderich (@Cure53) for acting so fast and for being a gentleman despite having to stay up late to work on the fix. Lastly, @LiveOverFlow for reminding me in one of his videos that we need to move away from the basics as soon as we master them and constantly challenge ourselves.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Daniel Santos

Daniel Santos

Security researcher and penetration tester