Mutation XSS in Google Search

Are you sure that your website is safe from Cross-site Scripting if Google Search was not for five months?

Google XSS

On September 26, 2018, one of the developers working on the open-source Closure library (originally created by Google and used in Google Search) created a commit that removed part of input sanitization. Supposedly, this was because they encountered issues with user interface design. The author of this commit and its reviewers failed to notice that this change introduced an XSS vulnerability.

In February 2019, a security researcher Masato Kinugawa discovered this vulnerability and reported it to Google. Google reacted immediately and the vulnerability was fixed on February 22, 2019, by reverting the original commit that caused it. The whole case was described in a lot of detail by another security expert, LiveOverflow.

How Did the XSS Happen?

The vulnerability in the Closure library was very difficult to detect. It relied on a rarely used technique called mutation XSS. Mutation XSS vulnerabilities are caused by differences in how browsers interpret the HTML standard.

Due to browser differences, it is very difficult to sanitize user input on the server. The server needs to account for all differences between not only browsers but also their versions. The most efficient way to sanitize input against XSS is to do it by letting the browser interpret input without actually executing it.

There is an excellent client-side library for XSS sanitization: DOMPurify. This library is also used by Closure. However, DOMPurify is not foolproof. In rare cases, extra sanitization is needed. And it was exactly that extra sanitization that was removed in September 2018 with the update to Closure.

google xss

How Does DOMPurify Work?

DOMPurify sanitizes user input by using the template element. Browsers process the innerHtml property of the div element and the same property of the template element differently. In the case of the div element, innerHtml is executed immediately after it is assigned a value. In the case of the template element, you can apply sanitization before execution.

The idea behind DOMPurify is to take the user input, assign it to the innerHtml property of the template element, have the browser interpret it (but not execute it), and then sanitize it for potential XSS. However, it is the logic behind that interpretation that is the underlying cause of the mutation XSS.

How Does the Browser Interpret Invalid HTML?

To understand how browsers interpret invalid HTML, create an HTML document with only the following content:

<div><script title="</div>">

When you open it in the browser, you will see that the code is interpreted as follows:

<html>
<head></head>
<body>
<div>
<script title="</div>"></script>
</div>
</body>
</html>

Now, try to create a HTML document with the following content:

<script><div title="</script>">

The browser interprets this code differently:

<html>
<head>
<script><div title="</script>
</head>
<body>
">
</body>
</html>

This difference is due to the fact that when the browser encounters the <script> tag, it switches from the HTML parser to the JavaScript parser until the closing tag is found.

The Evil Behind noscript

In most cases, the browser will interpret the same document always in the same way independent of circumstances. However, there is one case where this behavior may be different due to certain client-side circumstances: the noscript tag.

The HTML specification states that the noscript tag must be interpreted differently depending on whether JavaScript is enabled in the browser or not. This difference in browser behavior is exactly what Masato Kinugawa used for his XSS proof-of-concept attack. It turns out that invalid HTML code is interpreted differently when assigned to the innerHtml property of the template element (as if JavaScript was disabled) and differently when assigned to the innerHtml property of the div element (as if JavaScript was enabled).

The Proof-of-Concept Attack

Masato Kinugawa used the following payload to perform the proof-of-concept attack:

<noscript><p title="</noscript><img src=x onerror=alert(1)>">

If JavaScript is disabled (as for the template element used by DOMPurify for XSS sanitization) the browser interprets the payload in the following way:

<noscript>
<p title="</noscript><img src=x onerror=alert(1)>"></p>
</noscript>

The "</noscript><img src=x onerror=alert(1)>" is the value of the title argument. Therefore, DOMPurify does not sanitize the input content because there is no JavaScript so there is no XSS risk.

DOMPurify

However, if JavaScript is enabled (as for the div element used by the browser) the browser interprets the payload the following way:

<noscript><p title="</noscript>
<img src="x" onerror="alert(1)">
"">
"

The noscript element ends early and the img element is interpreted fully, including the JavaScript content of the onerror attribute.

Google XSS

It is impossible to say if anyone else discovered this vulnerability before and whether it was used for any malicious purposes. Since the Closure library is used in other Google products as well, this vulnerability could have affected Gmail, Maps, Docs, and other services.

Share this post
Tomasz NideckiTomasz Andrzej Nidecki Technical Content Writer
LinkedIn: https://mt.linkedin.com/in/tonid

Tomasz Andrzej Nidecki (also known as tonid) is a Technical Content Writer working for Acunetix. A journalist, translator, and technical writer with 25 years of IT experience, Tomasz has been the Managing Editor of the hakin9 IT Security magazine in its early years and used to run a major technical blog dedicated to email security.