Photo of Michał Bentkowski

Michał Bentkowski

Another XSS in Google Colaboratory

Published on:

Three months ago, I wrote a blog post in which I described an XSS I found in Google Colaboratory. In this post, I will expand the topic and show you another XSS I identified in the same application, which is directly related to the previous.

I suggest you to have a look at the previous post before reading this one, but here’s an overview of what happened last time:

  • I analyzed an application called Google Colaboratory for XSS-es,
  • I found that the application make use of a library called MathJax to render LaTeX code,
  • I found an XSS in MathJax, abusing \unicode{} LaTeX macro.

Looking from a technical standpoint, the error was not in MathJax itself, but in the plugin, which was enabled by default, called Assistive MathML. Before MathJax author fixed the bug, Google got rid of the XSS by just disabling the plugin. This is a pretty sane approach as the root cause of the XSS is eliminated, right?…

Well, that’s true, unless there is a way to re-enable the plugin!

Once, I gave Google Colaboratory another shot and checked for other XSS-es. I noticed an interesting behaviour: when I press the right click on the LaTeX macro generated in the MarkDown, I get a standard Colaboratory popup-menu. However, when I right-click on the table of contents, I get a menu of MathJax! See that below:

“Why did it matter” - you might ask. Well, it turned out that it is possible to re-enable the Assistive MathML plugin!

And when I did that, my old method of XSS, for example using: $ \unicode {41<img src=1 onerror=alert(document.domain)>} $ started working again.

The question that I still had to ask was: is it possible to make the XSS fire for another user? Which demands a follow-up question: where does MathJax store information about re-enabled Assistive MathML?

The answer was pretty good for me, as it was stored in a cookie. I noticed the following cookie: mjx.menu=assistiveMML%3Atrue. It is a great news since cookies might be set across subdomain. I wrote about another example of XSS via cookie four years ago in a blog post: Gmail and Google+ - tale of two XSS-es. So here’s the attack scenario:

  1. We have an XSS on any other Google subdomain, for instance: some-random-domain.google.com,
  2. From that domain, we set the cookie: document.cookie="mjx.menu=assistiveMML%3atrue; Domain=.google.com; Path=/"
  3. Now, the mjx.menu cookie is being sent with every request to any Google subdomain.

I simulated the attack, by defining some-random-domain.google.com in /etc/hosts and then using the following code:

<!DOCTYPE html><meta charset="utf-8" />

<button onclick="exploit()" style="font-size:48px">
  Set cookie and redirect to Colaboratory!
</button>

<script>
  function exploit() {
    const COLAB_URL =
      "https://colab.research.google.com/notebooks/welcome.ipynb";

    document.cookie =
      "mjx.menu=assistiveMML%3atrue; Domain=.google.com; Path=/";

    location = COLAB_URL;
  }
</script>

The result? Below:

I think the most important conclusion from the above bug is that a special care must be given, when auditing any external JS libraries, to their storage mechanisms (like cookies or localStorage). In case of MathJax, the preferences in cookies overloaded the ones defined when loading the library.