Ajay Patel

A Surprising Security Vulnerability on the Google Search Results Page
Projects · January 9, 2021

Back in 2015, I discovered a surprising security vulnerability that allowed you to run malicious JavaScript code on the Google search engine results page, which might be one of the most secure pages on the internet given how much daily traffic it recieves. It wasn’t until 4 years later (or 2019) that they finally closed the case on it and I could share it without breaking my nondisclosure agreement with Google.

This sounds like a lot like an XSS attack, which you would imagine Google would sanitize for, but it’s not and it’ll soon become clear why as I explain.

Discovery

I was on Google searching for something like “text-to-speech javascript libraries”. I went from the Google search results page to clicking the first result, back to the Google search results page, to clicking on the second result, back to the Google search results page, etc. What I noticed was that my speakers kept making a clicking noise, like they were turning on and off—but just for a second.

I thought maybe there was some other program running on my computer or that my speakers were busted, but I realized it happened everytime I went to the Google search results page for this query. It also happened everytime I went to the first search results page, which was of a company that had a demo of their text-to-speech solution on it.

I figured the company’s page was loading their text-to-speech engine, that seemed to use the SpeechSynthesis interface of the WebSpeech API, and that’s why the speakers were turning on. But why were they on the Google page? That seemed like Google was maybe pulling in code on the search results page from the company’s page. That should never happen though, surely Google sanitizes anything that ever gets displayed on their pages from containing HTML or <script> tags.

This seemed like a plausible attack vector if there was some bug that allowed you to bring arbitrary code on to the Google search results page and a huge one at that.

Investigation

I dumped the source code of the search results page and started looking at the HTML around the search result, but it didn’t look like any <script> or HTML tags were making it through. I started looking for instances of the company’s domain and found something like:

<link rel="prerender" href="https://thecompanyspage.com/example">

I was not familiar with rel="prerender" myself in 2015. Indeed, it seemed to be some sort of new mechanism for pre-fetching and pre-loading a page to speed up page transistions and load times as an optimization.

Google seemed to have invented rel="prerender" to prefetch and load the first search result in the background, so that when you clicked on the first search result (as most users do), it would instantly be ready for viewing.

The rel="prerender" has to execute JavaScript to load dynamic content, but it wasn’t blocking the WebSpeech API from playing audio possibly. Luckily for Google, the pre-rendered content is loaded in its own origin and, therefore, sensitive resources like cookies on the Google page were not at risk from being harvested by attackers like they would be in an XSS attack.

Reproducibility

After learning this, the next step was to prove this out with my own website on Google’s search results page and see what I could make of it.

I needed a long obscure phrase that I could get the #1 Google search result spot for and that no one would accidently stumble upon so I could keep the vulnerability relatively secret until Google could fix it. I chose the apt phrase “Alphabet Spoke to Me In My Dreams” and purchased the alphabetspoketomeinmydreams.xyz domain name (now expired) that would show up as the first Google search result for the phrase. This was around the time Google restructured into Alphabet Inc.

Social Engineering & Impersonation

The first malicious thing I could think to do was to “vandalize” the Google search results page with one of those “win a prize” scams pretending to be Google to phish their users. I also thought it would be sufficiently creepy to show how you were able to access standard information about the user like their location, ISP, IP address while being prerendered and speak it back to them. I know Dwight Schrute would be scared of that.

Google Search Results Page Proof-of-Concept JavaScript code executing to play speech synthesis audio on the Google search results page.

So this looks pretty bad, but you would think that a user might notice if they click on the first result that it was a 3rd-party page causing the audio to play not Google right? Yes, except you can use the document.hidden property to detect prerender mode and condition your code so that it only runs in rel="prerender" mode on the Google search engine results page! This creates a pretty robust appearance for impersonation as nothing happens if you go to the page directly.

if (document.hidden) {
  // The page is being loaded in rel="prerender", run the WebSpeech API code
} else {
  // The page is being loaded normally, don't do anything
}

Chrome Vulnerability

While testing this, I found that this doesn’t only work on the Google search results page, but it turns out Chrome also “prerenders” the first result of the autocomplete in the URL search bar. Therefore, you can start playing WebSpeech audio without even having a page loaded in the browser frame.

Google Chrome Proof-of-Concept JavaScript code executing to play speech synthesis audio in the Chrome browser before any page is loaded in the browser at all when triggered by a user typing in a search into the URL search bar.

Denial-of-Service Possibilities

After testing other APIs, it seemed clear that only the WebSpeech API was making it through to the parent page. Things like alert() boxes were deferred from being visible until the page was actually loaded. But it also seemed clear that, while certain APIs were not available, JavaScript was running unrestricted. What happens if you do an infinite loop on the prerendered page?

while (true) {
  // Infinite loop
}

Yep, you could “denial of service” the Google search results page from being displayed. This is not an attack on their servers, but rather you lock up the user’s browser so Google’s search results page cannot load.

Google Search Results Page Denial-of-Service
Google Search Results Page Denial-of-Service Preventing the Google search results page from even loading by infinite looping in the prerendered page.

Reporting to Google Security and Chrome Security

I shared the live example and a locally reproducible example of the vulnerability to Google.

Google’s Response

Google’s security team responded promptly and professionally within 24 hours and decided to award a monetary reward for the vulnerability. The Chrome team did not dual-award since Google had already awarded. The Chrome team, however, were responsible for fixing the issue. I was not able to share this bug for quite some time due to the nondisclosure terms of Google’s vulnerability program.

The Fix

The bug report and discussion is now public and visible here.

I was suprised at how long it took to resolve and how difficult the fix seemed to be. It proved very difficult to break this API out from being enabled in the prerender context. In fact, at one point, one of the developers mentioned: “This is currently our oldest >= Medium severity security bug.” It was not until 2019 that they finally closed the bug after prerendering support was completely dropped from Chrome, due to it causing headaches like this, and the proof-of-concept could no longer be reproduced.

So, it seems premature optimization like rel="prerender" being the root of all evil isn’t just true because you might spend time optimizing something that isn’t worth it, but also because it likely increases the complexity and surface area for serious security bugs.


  • Founder @ Plasticity (acq. 2020)
  • PhD @ University of Pennsylvania (est. 2025)
  • Y Combinator Alum (S17 Batch)

More on About  →
Currently in 🔔 Philadelphia, PA, USA 🇺🇸
Copyright © 2024 Ajay Patel. All rights reserved.